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

lightningnetwork / lnd / 13211764208

08 Feb 2025 03:08AM UTC coverage: 49.288% (-9.5%) from 58.815%
13211764208

Pull #9489

github

calvinrzachman
itest: verify switchrpc server enforces send then track

We prevent the rpc server from allowing onion dispatches for
attempt IDs which have already been tracked by rpc clients.

This helps protect the client from leaking a duplicate onion
attempt. NOTE: This is not the only method for solving this
issue! The issue could be addressed via careful client side
programming which accounts for the uncertainty and async
nature of dispatching onions to a remote process via RPC.
This would require some lnd ChannelRouter changes for how
we intend to use these RPCs though.
Pull Request #9489: multi: add BuildOnion, SendOnion, and TrackOnion RPCs

474 of 990 new or added lines in 11 files covered. (47.88%)

27321 existing lines in 435 files now uncovered.

101192 of 205306 relevant lines covered (49.29%)

1.54 hits per line

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

0.0
/lntest/port/port.go
1
package port
2

3
import (
4
        "fmt"
5
        "net"
6
        "os"
7
        "path/filepath"
8
        "strconv"
9
        "sync"
10
        "time"
11

12
        "github.com/lightningnetwork/lnd/lntest/wait"
13
)
14

15
const (
16
        // ListenerFormat is the format string that is used to generate local
17
        // listener addresses.
18
        ListenerFormat = "127.0.0.1:%d"
19

20
        // defaultNodePort is the start of the range for listening ports of
21
        // harness nodes. Ports are monotonically increasing starting from this
22
        // number and are determined by the results of NextAvailablePort().
23
        defaultNodePort int = 10000
24

25
        // uniquePortFile is the name of the file that is used to store the
26
        // last port that was used by a node. This is used to make sure that
27
        // the same port is not used by multiple nodes at the same time. The
28
        // file is located in the temp directory of a system.
29
        uniquePortFile = "rpctest-port"
30
)
31

32
var (
33
        // portFileMutex is a mutex that is used to make sure that the port file
34
        // is not accessed by multiple goroutines of the same process at the
35
        // same time. This is used in conjunction with the lock file to make
36
        // sure that the port file is not accessed by multiple processes at the
37
        // same time either. So the lock file is to guard between processes and
38
        // the mutex is to guard between goroutines of the same process.
39
        portFileMutex sync.Mutex
40
)
41

42
// NextAvailablePort returns the first port that is available for listening by a
43
// new node, using a lock file to make sure concurrent access for parallel tasks
44
// on the same system don't re-use the same port.
UNCOV
45
func NextAvailablePort() int {
×
UNCOV
46
        portFileMutex.Lock()
×
UNCOV
47
        defer portFileMutex.Unlock()
×
UNCOV
48

×
UNCOV
49
        lockFile := filepath.Join(os.TempDir(), uniquePortFile+".lock")
×
UNCOV
50
        timeout := time.After(wait.DefaultTimeout)
×
UNCOV
51

×
UNCOV
52
        var (
×
UNCOV
53
                lockFileHandle *os.File
×
UNCOV
54
                err            error
×
UNCOV
55
        )
×
UNCOV
56
        for {
×
UNCOV
57
                // Attempt to acquire the lock file. If it already exists, wait
×
UNCOV
58
                // for a bit and retry.
×
UNCOV
59
                lockFileHandle, err = os.OpenFile(
×
UNCOV
60
                        lockFile, os.O_CREATE|os.O_EXCL, 0600,
×
UNCOV
61
                )
×
UNCOV
62
                if err == nil {
×
UNCOV
63
                        // Lock acquired.
×
UNCOV
64
                        break
×
65
                }
66

67
                // Wait for a bit and retry.
68
                select {
×
69
                case <-timeout:
×
70
                        panic("timeout waiting for lock file")
×
71
                case <-time.After(10 * time.Millisecond):
×
72
                }
73
        }
74

75
        // Release the lock file when we're done.
UNCOV
76
        defer func() {
×
UNCOV
77
                // Always close file first, Windows won't allow us to remove it
×
UNCOV
78
                // otherwise.
×
UNCOV
79
                _ = lockFileHandle.Close()
×
UNCOV
80
                err := os.Remove(lockFile)
×
UNCOV
81
                if err != nil {
×
82
                        panic(fmt.Errorf("couldn't remove lock file: %w", err))
×
83
                }
84
        }()
85

UNCOV
86
        portFile := filepath.Join(os.TempDir(), uniquePortFile)
×
UNCOV
87
        port, err := os.ReadFile(portFile)
×
UNCOV
88
        if err != nil {
×
UNCOV
89
                if !os.IsNotExist(err) {
×
90
                        panic(fmt.Errorf("error reading port file: %w", err))
×
91
                }
UNCOV
92
                port = []byte(strconv.Itoa(defaultNodePort))
×
93
        }
94

UNCOV
95
        lastPort, err := strconv.Atoi(string(port))
×
UNCOV
96
        if err != nil {
×
97
                panic(fmt.Errorf("error parsing port: %w", err))
×
98
        }
99

100
        // We take the next one.
UNCOV
101
        lastPort++
×
UNCOV
102
        for lastPort < 65535 {
×
UNCOV
103
                // If there are no errors while attempting to listen on this
×
UNCOV
104
                // port, close the socket and return it as available. While it
×
UNCOV
105
                // could be the case that some other process picks up this port
×
UNCOV
106
                // between the time the socket is closed, and it's reopened in
×
UNCOV
107
                // the harness node, in practice in CI servers this seems much
×
UNCOV
108
                // less likely than simply some other process already being
×
UNCOV
109
                // bound at the start of the tests.
×
UNCOV
110
                addr := fmt.Sprintf(ListenerFormat, lastPort)
×
UNCOV
111
                l, err := net.Listen("tcp4", addr)
×
UNCOV
112
                if err == nil {
×
UNCOV
113
                        err := l.Close()
×
UNCOV
114
                        if err == nil {
×
UNCOV
115
                                err := os.WriteFile(
×
UNCOV
116
                                        portFile,
×
UNCOV
117
                                        []byte(strconv.Itoa(lastPort)), 0600,
×
UNCOV
118
                                )
×
UNCOV
119
                                if err != nil {
×
120
                                        panic(fmt.Errorf("error updating "+
×
121
                                                "port file: %w", err))
×
122
                                }
123

UNCOV
124
                                return lastPort
×
125
                        }
126
                }
127
                lastPort++
×
128

×
129
                // Start from the beginning if we reached the end of the port
×
130
                // range. We need to do this because the lock file now is
×
131
                // persistent across runs on the same machine during the same
×
132
                // boot/uptime cycle. So in order to make this work on
×
133
                // developer's machines, we need to reset the port to the
×
134
                // default value when we reach the end of the range.
×
135
                if lastPort == 65535 {
×
136
                        lastPort = defaultNodePort
×
137
                }
×
138
        }
139

140
        // No ports available? Must be a mistake.
141
        panic("no ports available for listening")
×
142
}
143

144
// GenerateSystemUniqueListenerAddresses is a function that returns two
145
// listener addresses with unique ports per system and should be used to
146
// overwrite rpctest's default generator which is prone to use colliding ports.
UNCOV
147
func GenerateSystemUniqueListenerAddresses() (string, string) {
×
UNCOV
148
        port1 := NextAvailablePort()
×
UNCOV
149
        port2 := NextAvailablePort()
×
UNCOV
150
        return fmt.Sprintf(ListenerFormat, port1),
×
UNCOV
151
                fmt.Sprintf(ListenerFormat, port2)
×
UNCOV
152
}
×
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