• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lightningnetwork / lnd / 15574102646

11 Jun 2025 01:44AM UTC coverage: 68.554% (+9.9%) from 58.637%
15574102646

Pull #9652

github

web-flow
Merge eb863e46a into 92a5d35cf
Pull Request #9652: lnwallet/chancloser: fix flake in TestRbfCloseClosingNegotiationLocal

11 of 12 new or added lines in 1 file covered. (91.67%)

7276 existing lines in 84 files now uncovered.

134508 of 196208 relevant lines covered (68.55%)

44569.29 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.65
/lntest/unittest/backend.go
1
package unittest
2

3
import (
4
        "fmt"
5
        "os/exec"
6
        "path/filepath"
7
        "strings"
8
        "testing"
9
        "time"
10

11
        "github.com/btcsuite/btcd/chaincfg"
12
        "github.com/btcsuite/btcd/integration/rpctest"
13
        "github.com/btcsuite/btcd/rpcclient"
14
        "github.com/btcsuite/btcwallet/chain"
15
        "github.com/btcsuite/btcwallet/walletdb"
16
        "github.com/lightninglabs/neutrino"
17
        "github.com/lightningnetwork/lnd/kvdb"
18
        "github.com/lightningnetwork/lnd/lntest/port"
19
        "github.com/lightningnetwork/lnd/lntest/wait"
20
        "github.com/stretchr/testify/require"
21
)
22

23
var (
24
        // TrickleInterval is the interval at which the miner should trickle
25
        // transactions to its peers. We'll set it small to ensure the miner
26
        // propagates transactions quickly in the tests.
27
        TrickleInterval = 10 * time.Millisecond
28
)
29

30
var (
31
        // NetParams are the default network parameters for the tests.
32
        NetParams = &chaincfg.RegressionNetParams
33
)
34

35
// NewMiner spawns testing harness backed by a btcd node that can serve as a
36
// miner.
37
func NewMiner(t *testing.T, netParams *chaincfg.Params, extraArgs []string,
38
        createChain bool, spendableOutputs uint32) *rpctest.Harness {
122✔
39

122✔
40
        t.Helper()
122✔
41

122✔
42
        args := []string{
122✔
43
                "--nobanning",
122✔
44
                "--debuglevel=debug",
122✔
45
                fmt.Sprintf("--trickleinterval=%v", TrickleInterval),
122✔
46

122✔
47
                // Don't disconnect if a reply takes too long.
122✔
48
                "--nostalldetect",
122✔
49
        }
122✔
50
        extraArgs = append(extraArgs, args...)
122✔
51

122✔
52
        node, err := rpctest.New(netParams, nil, extraArgs, "")
122✔
53
        require.NoError(t, err, "unable to create backend node")
122✔
54
        t.Cleanup(func() {
244✔
55
                require.NoError(t, node.TearDown())
122✔
56
        })
122✔
57

58
        // We want to overwrite some of the connection settings to make the
59
        // tests more robust. We might need to restart the backend while there
60
        // are already blocks present, which will take a bit longer than the
61
        // 1 second the default settings amount to. Doubling both values will
62
        // give us retries up to 4 seconds.
63
        node.MaxConnRetries = rpctest.DefaultMaxConnectionRetries * 2
122✔
64
        node.ConnectionRetryTimeout = rpctest.DefaultConnectionRetryTimeout * 2
122✔
65

122✔
66
        if err := node.SetUp(createChain, spendableOutputs); err != nil {
122✔
67
                t.Fatalf("unable to set up backend node: %v", err)
×
68
        }
×
69

70
        // Next mine enough blocks in order for segwit and the CSV package
71
        // soft-fork to activate.
72
        numBlocks := netParams.MinerConfirmationWindow*2 + 17
122✔
73
        _, err = node.Client.Generate(numBlocks)
122✔
74
        require.NoError(t, err, "failed to generate blocks")
122✔
75

122✔
76
        return node
122✔
77
}
78

79
// NewBitcoindBackend spawns a new bitcoind node that connects to a miner at the
80
// specified address. The txindex boolean can be set to determine whether the
81
// backend node should maintain a transaction index. The rpcpolling boolean
82
// can be set to determine whether bitcoind's RPC polling interface should be
83
// used for block and tx notifications or if its ZMQ interface should be used.
84
// A connection to the newly spawned bitcoind node is returned once the bitcoind
85
// is synced to the miner's best height.
86
func NewBitcoindBackend(t *testing.T, netParams *chaincfg.Params,
87
        miner *rpctest.Harness, txindex, rpcpolling bool) *chain.BitcoindConn {
36✔
88

36✔
89
        t.Helper()
36✔
90

36✔
91
        tempBitcoindDir := t.TempDir()
36✔
92

36✔
93
        rpcPort := port.NextAvailablePort()
36✔
94
        torBindPort := port.NextAvailablePort()
36✔
95
        zmqBlockPort := port.NextAvailablePort()
36✔
96
        zmqTxPort := port.NextAvailablePort()
36✔
97
        zmqBlockHost := fmt.Sprintf("tcp://127.0.0.1:%d", zmqBlockPort)
36✔
98
        zmqTxHost := fmt.Sprintf("tcp://127.0.0.1:%d", zmqTxPort)
36✔
99

36✔
100
        // TODO(yy): Make this configurable via `chain.BitcoindConfig` and
36✔
101
        // replace the default P2P port when set.
36✔
102
        p2pPort := port.NextAvailablePort()
36✔
103
        netParams.DefaultPort = fmt.Sprintf("%d", p2pPort)
36✔
104

36✔
105
        args := []string{
36✔
106
                "-datadir=" + tempBitcoindDir,
36✔
107
                "-regtest",
36✔
108
                "-rpcauth=weks:469e9bb14ab2360f8e226efed5ca6fd$507c670e800a95" +
36✔
109
                        "284294edb5773b05544b220110063096c221be9933c82d38e1",
36✔
110
                fmt.Sprintf("-rpcport=%d", rpcPort),
36✔
111
                fmt.Sprintf("-bind=127.0.0.1:%d=onion", torBindPort),
36✔
112
                fmt.Sprintf("-port=%d", p2pPort),
36✔
113
                "-disablewallet",
36✔
114
                "-zmqpubrawblock=" + zmqBlockHost,
36✔
115
                "-zmqpubrawtx=" + zmqTxHost,
36✔
116

36✔
117
                // whitelist localhost to speed up relay.
36✔
118
                "-whitelist=127.0.0.1",
36✔
119

36✔
120
                // Disable v2 transport as btcd doesn't support it yet.
36✔
121
                //
36✔
122
                // TODO(yy): Remove this line once v2 conn is supported in
36✔
123
                // `btcd`.
36✔
124
                "-v2transport=0",
36✔
125
        }
36✔
126
        if txindex {
68✔
127
                args = append(args, "-txindex")
32✔
128
        }
32✔
129

130
        bitcoind := exec.Command("bitcoind", args...)
36✔
131
        err := bitcoind.Start()
36✔
132
        require.NoError(t, err, "unable to start bitcoind")
36✔
133

36✔
134
        t.Cleanup(func() {
72✔
135
                // Kill `bitcoind` and assert there's no error.
36✔
136
                err = bitcoind.Process.Kill()
36✔
137
                require.NoError(t, err)
36✔
138

36✔
139
                err = bitcoind.Wait()
36✔
140
                if strings.Contains(err.Error(), "signal: killed") {
72✔
141
                        return
36✔
142
                }
36✔
143

144
                require.NoError(t, err)
×
145
        })
146

147
        // Wait for the bitcoind instance to start up.
148
        time.Sleep(time.Second)
36✔
149

36✔
150
        host := fmt.Sprintf("127.0.0.1:%d", rpcPort)
36✔
151
        cfg := &chain.BitcoindConfig{
36✔
152
                ChainParams: netParams,
36✔
153
                Host:        host,
36✔
154
                User:        "weks",
36✔
155
                Pass:        "weks",
36✔
156
                // Fields only required for pruned nodes, not needed for these
36✔
157
                // tests.
36✔
158
                Dialer:             nil,
36✔
159
                PrunedModeMaxPeers: 0,
36✔
160
        }
36✔
161

36✔
162
        if rpcpolling {
54✔
163
                cfg.PollingConfig = &chain.PollingConfig{
18✔
164
                        BlockPollingInterval: time.Millisecond * 20,
18✔
165
                        TxPollingInterval:    time.Millisecond * 20,
18✔
166
                }
18✔
167
        } else {
36✔
168
                cfg.ZMQConfig = &chain.ZMQConfig{
18✔
169
                        ZMQBlockHost:    zmqBlockHost,
18✔
170
                        ZMQTxHost:       zmqTxHost,
18✔
171
                        ZMQReadDeadline: 5 * time.Second,
18✔
172
                }
18✔
173
        }
18✔
174

175
        var conn *chain.BitcoindConn
36✔
176
        err = wait.NoError(func() error {
72✔
177
                var err error
36✔
178
                conn, err = chain.NewBitcoindConn(cfg)
36✔
179
                if err != nil {
36✔
180
                        return err
×
181
                }
×
182

183
                return conn.Start()
36✔
184
        }, 10*time.Second)
185
        if err != nil {
36✔
186
                t.Fatalf("unable to establish connection to bitcoind at %v: "+
×
187
                        "%v", tempBitcoindDir, err)
×
188
        }
×
189
        t.Cleanup(conn.Stop)
36✔
190

36✔
191
        // Assert that the connection with the miner is made.
36✔
192
        //
36✔
193
        // Create a new RPC client.
36✔
194
        rpcCfg := rpcclient.ConnConfig{
36✔
195
                Host:                 cfg.Host,
36✔
196
                User:                 cfg.User,
36✔
197
                Pass:                 cfg.Pass,
36✔
198
                DisableConnectOnNew:  true,
36✔
199
                DisableAutoReconnect: false,
36✔
200
                DisableTLS:           true,
36✔
201
                HTTPPostMode:         true,
36✔
202
        }
36✔
203

36✔
204
        rpcClient, err := rpcclient.New(&rpcCfg, nil)
36✔
205
        require.NoError(t, err, "failed to create RPC client")
36✔
206

36✔
207
        // Connect to the miner node.
36✔
208
        err = rpcClient.AddNode(miner.P2PAddress(), rpcclient.ANAdd)
36✔
209
        require.NoError(t, err, "failed to connect to miner")
36✔
210

36✔
211
        // Get the network info and assert the num of outbound connections is 1.
36✔
212
        err = wait.NoError(func() error {
216✔
213
                result, err := rpcClient.GetNetworkInfo()
180✔
214
                require.NoError(t, err)
180✔
215

180✔
216
                if int(result.Connections) != 1 {
324✔
217
                        return fmt.Errorf("want 1 conn, got %d",
144✔
218
                                result.Connections)
144✔
219
                }
144✔
220

221
                if int(result.ConnectionsOut) != 1 {
36✔
222
                        return fmt.Errorf("want 1 outbound conn, got %d",
×
223
                                result.Connections)
×
224
                }
×
225

226
                return nil
36✔
227
        }, wait.DefaultTimeout)
228
        require.NoError(t, err, "timeout connecting to the miner")
36✔
229

36✔
230
        // Assert the chain backend is synced to the miner.
36✔
231
        syncBitcoindWithMiner(t, rpcClient, miner, p2pPort)
36✔
232

36✔
233
        // Tear down the rpc client.
36✔
234
        rpcClient.Shutdown()
36✔
235

36✔
236
        return conn
36✔
237
}
238

239
// syncBitcoindWithMiner waits until the bitcoind node is synced with the miner.
240
func syncBitcoindWithMiner(t *testing.T, notifier *rpcclient.Client,
241
        miner *rpctest.Harness, p2pPort int) uint32 {
36✔
242

36✔
243
        _, minerHeight, err := miner.Client.GetBestBlock()
36✔
244
        require.NoError(t, err, "unable to retrieve miner's current height")
36✔
245

36✔
246
        timeout := time.After(10 * time.Second)
36✔
247
        for {
280✔
248
                info, err := notifier.GetBlockChainInfo()
244✔
249
                require.NoError(t, err)
244✔
250

244✔
251
                bitcoindHeight := info.Blocks
244✔
252

244✔
253
                t.Logf("miner height=%v, bitcoind height=%v", minerHeight,
244✔
254
                        bitcoindHeight)
244✔
255

244✔
256
                if bitcoindHeight == minerHeight {
280✔
257
                        return uint32(bitcoindHeight)
36✔
258
                }
36✔
259

260
                select {
208✔
261
                case <-time.After(100 * time.Millisecond):
208✔
262
                case <-timeout:
×
263
                        t.Fatalf("timed out in syncNotifierWithMiner, got "+
×
264
                                "err=%v, minerHeight=%v, bitcoindHeight=%v",
×
265
                                err, minerHeight, bitcoindHeight)
×
266
                }
267

268
                // Get the num of connections the miner has. We expect it to
269
                // have at least one connection with the chain backend.
270
                count, err := miner.Client.GetConnectionCount()
208✔
271
                require.NoError(t, err)
208✔
272
                if count != 0 {
416✔
273
                        continue
208✔
274
                }
275

276
                // Reconnect the miner and the chain backend.
277
                //
278
                // NOTE: The connection should have been made before we perform
279
                // the `syncNotifierWithMiner`. However, due unknown reason, the
280
                // miner may refuse to process the inbound connection made by
281
                // the bitcoind node, causing the connection to fail. It's
282
                // possible there's a bug in the handshake between the two
283
                // nodes.
284
                //
285
                // A normal flow is, bitcoind starts a v2 handshake flow, which
286
                // btcd will fail and disconnect. Upon seeing this
287
                // disconnection, bitcoind will try a v1 handshake and succeeds.
288
                // The failed flow is, upon seeing the v2 handshake, btcd
289
                // doesn't seem to perform the disconnect. Instead an EOF
290
                // websocket error is found.
291
                //
292
                // TODO(yy): Fix the above bug in `btcd`. This can be reproduced
293
                // using `make flakehunter-unit pkg=$pkg case=$case`, with,
294
                // `case=TestHistoricalConfDetailsNoTxIndex/rpc_polling_enabled`
295
                // `pkg=chainntnfs/bitcoindnotify`.
296
                // Also need to modify the temp dir logic so we can save the
297
                // debug logs.
298
                // This bug is likely to be fixed when we implement the
299
                // encrypted p2p conn, or when we properly fix the shutdown
300
                // issues in all our RPC conns.
301
                t.Log("Expected to the chain backend to have one conn with " +
×
302
                        "the miner, instead it's disconnected!")
×
303

×
304
                // We now ask the miner to add the chain backend back.
×
305
                host := fmt.Sprintf("127.0.0.1:%d", p2pPort)
×
306

×
307
                // NOTE:AddNode must take a host that has the format
×
308
                // `host:port`, otherwise the default port will be used. Check
×
309
                // `normalizeAddress` in btcd for details.
×
310
                err = miner.Client.AddNode(host, rpcclient.ANAdd)
×
311
                require.NoError(t, err, "Failed to connect miner to the chain "+
×
312
                        "backend")
×
313
        }
314
}
315

316
// NewNeutrinoBackend spawns a new neutrino node that connects to a miner at
317
// the specified address.
318
func NewNeutrinoBackend(t *testing.T, netParams *chaincfg.Params,
319
        minerAddr string) *neutrino.ChainService {
2✔
320

2✔
321
        t.Helper()
2✔
322

2✔
323
        spvDir := t.TempDir()
2✔
324

2✔
325
        dbName := filepath.Join(spvDir, "neutrino.db")
2✔
326
        spvDatabase, err := walletdb.Create(
2✔
327
                kvdb.BoltBackendName, dbName, true, kvdb.DefaultDBTimeout,
2✔
328
                false,
2✔
329
        )
2✔
330
        if err != nil {
2✔
331
                t.Fatalf("unable to create walletdb: %v", err)
×
332
        }
×
333
        t.Cleanup(func() {
4✔
334
                spvDatabase.Close()
2✔
335
        })
2✔
336

337
        // Create an instance of neutrino connected to the running btcd
338
        // instance.
339
        spvConfig := neutrino.Config{
2✔
340
                DataDir:      spvDir,
2✔
341
                Database:     spvDatabase,
2✔
342
                ChainParams:  *netParams,
2✔
343
                ConnectPeers: []string{minerAddr},
2✔
344
        }
2✔
345
        spvNode, err := neutrino.NewChainService(spvConfig)
2✔
346
        if err != nil {
2✔
347
                t.Fatalf("unable to create neutrino: %v", err)
×
UNCOV
348
        }
×
349

350
        // We'll also wait for the instance to sync up fully to the chain
351
        // generated by the btcd instance.
352
        _ = spvNode.Start()
2✔
353
        for !spvNode.IsCurrent() {
4✔
354
                time.Sleep(time.Millisecond * 100)
2✔
355
        }
2✔
356
        t.Cleanup(func() {
4✔
357
                _ = spvNode.Stop()
2✔
358
        })
2✔
359

360
        return spvNode
2✔
361
}
362

363
func init() {
108✔
364
        // Before we start any node, we need to make sure that any btcd or
108✔
365
        // bitcoind node that is started through the RPC harness uses a unique
108✔
366
        // port as well to avoid any port collisions.
108✔
367
        rpctest.ListenAddressGenerator =
108✔
368
                port.GenerateSystemUniqueListenerAddresses
108✔
369
}
108✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc