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

lightningnetwork / lnd / 13541633202

26 Feb 2025 10:16AM UTC coverage: 58.834% (-0.001%) from 58.835%
13541633202

Pull #9549

github

yyforyongyu
routing: fix flake in `TestFilteredChainView/bitcoind_polling`
Pull Request #9549: Fix unit test flake `TestHistoricalConfDetailsTxIndex`

104 of 124 new or added lines in 3 files covered. (83.87%)

75 existing lines in 19 files now uncovered.

136424 of 231878 relevant lines covered (58.83%)

19208.82 hits per line

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

87.6
/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 {
61✔
39

61✔
40
        t.Helper()
61✔
41

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

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

61✔
52
        node, err := rpctest.New(netParams, nil, extraArgs, "")
61✔
53
        require.NoError(t, err, "unable to create backend node")
61✔
54
        t.Cleanup(func() {
122✔
55
                require.NoError(t, node.TearDown())
61✔
56
        })
61✔
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
61✔
64
        node.ConnectionRetryTimeout = rpctest.DefaultConnectionRetryTimeout * 2
61✔
65

61✔
66
        if err := node.SetUp(createChain, spendableOutputs); err != nil {
61✔
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
61✔
73
        _, err = node.Client.Generate(numBlocks)
61✔
74
        require.NoError(t, err, "failed to generate blocks")
61✔
75

61✔
76
        return node
61✔
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 {
18✔
88

18✔
89
        t.Helper()
18✔
90

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

18✔
236
        return conn
18✔
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 {
18✔
242

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

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

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

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

61✔
256
                if bitcoindHeight == minerHeight {
79✔
257
                        return uint32(bitcoindHeight)
18✔
258
                }
18✔
259

260
                select {
43✔
261
                case <-time.After(100 * time.Millisecond):
43✔
NEW
262
                case <-timeout:
×
NEW
263
                        t.Fatalf("timed out in syncNotifierWithMiner, got "+
×
NEW
264
                                "err=%v, minerHeight=%v, bitcoindHeight=%v",
×
NEW
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()
43✔
271
                require.NoError(t, err)
43✔
272
                if count != 0 {
86✔
273
                        continue
43✔
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.
NEW
301
                t.Log("Expected to the chain backend to have one conn with " +
×
NEW
302
                        "the miner, instead it's disconnected!")
×
NEW
303

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

×
NEW
307
                // NOTE:AddNode must take a host that has the format
×
NEW
308
                // `host:port`, otherwise the default port will be used. Check
×
NEW
309
                // `normalizeAddress` in btcd for details.
×
NEW
310
                err = miner.Client.AddNode(host, rpcclient.ANAdd)
×
NEW
311
                require.NoError(t, err, "Failed to connect miner to the chain "+
×
NEW
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 {
1✔
320

1✔
321
        t.Helper()
1✔
322

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

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

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

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

359
        return spvNode
1✔
360
}
361

362
func init() {
51✔
363
        // Before we start any node, we need to make sure that any btcd or
51✔
364
        // bitcoind node that is started through the RPC harness uses a unique
51✔
365
        // port as well to avoid any port collisions.
51✔
366
        rpctest.ListenAddressGenerator =
51✔
367
                port.GenerateSystemUniqueListenerAddresses
51✔
368
}
51✔
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