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

lightningnetwork / lnd / 13725358077

07 Mar 2025 04:51PM UTC coverage: 58.224% (-10.4%) from 68.615%
13725358077

Pull #9458

github

web-flow
Merge bf4c6625f into ab2dc09eb
Pull Request #9458: multi+server.go: add initial permissions for some peers

346 of 549 new or added lines in 10 files covered. (63.02%)

27466 existing lines in 443 files now uncovered.

94609 of 162492 relevant lines covered (58.22%)

1.81 hits per line

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

2.8
/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,
UNCOV
38
        createChain bool, spendableOutputs uint32) *rpctest.Harness {
×
UNCOV
39

×
UNCOV
40
        t.Helper()
×
UNCOV
41

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

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

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

×
UNCOV
66
        if err := node.SetUp(createChain, spendableOutputs); err != nil {
×
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.
UNCOV
72
        numBlocks := netParams.MinerConfirmationWindow*2 + 17
×
UNCOV
73
        _, err = node.Client.Generate(numBlocks)
×
UNCOV
74
        require.NoError(t, err, "failed to generate blocks")
×
UNCOV
75

×
UNCOV
76
        return node
×
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,
UNCOV
87
        miner *rpctest.Harness, txindex, rpcpolling bool) *chain.BitcoindConn {
×
UNCOV
88

×
UNCOV
89
        t.Helper()
×
UNCOV
90

×
UNCOV
91
        tempBitcoindDir := t.TempDir()
×
UNCOV
92

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

×
UNCOV
233
        // Tear down the rpc client.
×
UNCOV
234
        rpcClient.Shutdown()
×
UNCOV
235

×
UNCOV
236
        return conn
×
237
}
238

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

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

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

×
UNCOV
251
                bitcoindHeight := info.Blocks
×
UNCOV
252

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

×
UNCOV
256
                if bitcoindHeight == minerHeight {
×
UNCOV
257
                        return uint32(bitcoindHeight)
×
UNCOV
258
                }
×
259

UNCOV
260
                select {
×
UNCOV
261
                case <-time.After(100 * time.Millisecond):
×
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.
UNCOV
270
                count, err := miner.Client.GetConnectionCount()
×
UNCOV
271
                require.NoError(t, err)
×
UNCOV
272
                if count != 0 {
×
UNCOV
273
                        continue
×
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,
UNCOV
319
        minerAddr string) *neutrino.ChainService {
×
UNCOV
320

×
UNCOV
321
        t.Helper()
×
UNCOV
322

×
UNCOV
323
        spvDir := t.TempDir()
×
UNCOV
324

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

336
        // Create an instance of neutrino connected to the running btcd
337
        // instance.
UNCOV
338
        spvConfig := neutrino.Config{
×
UNCOV
339
                DataDir:      spvDir,
×
UNCOV
340
                Database:     spvDatabase,
×
UNCOV
341
                ChainParams:  *netParams,
×
UNCOV
342
                ConnectPeers: []string{minerAddr},
×
UNCOV
343
        }
×
UNCOV
344
        spvNode, err := neutrino.NewChainService(spvConfig)
×
UNCOV
345
        if err != nil {
×
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.
UNCOV
351
        _ = spvNode.Start()
×
UNCOV
352
        for !spvNode.IsCurrent() {
×
UNCOV
353
                time.Sleep(time.Millisecond * 100)
×
UNCOV
354
        }
×
UNCOV
355
        t.Cleanup(func() {
×
UNCOV
356
                _ = spvNode.Stop()
×
UNCOV
357
        })
×
358

UNCOV
359
        return spvNode
×
360
}
361

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