• 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
/channeldb/migration27/channel.go
1
package migration27
2

3
import (
4
        "bytes"
5
        "fmt"
6

7
        lnwire "github.com/lightningnetwork/lnd/channeldb/migration/lnwire21"
8
        mig25 "github.com/lightningnetwork/lnd/channeldb/migration25"
9
        mig26 "github.com/lightningnetwork/lnd/channeldb/migration26"
10
        mig "github.com/lightningnetwork/lnd/channeldb/migration_01_to_11"
11
        "github.com/lightningnetwork/lnd/kvdb"
12
        "github.com/lightningnetwork/lnd/tlv"
13
)
14

15
const (
16
        // A tlv type definition used to serialize and deserialize a KeyLocator
17
        // from the database.
18
        keyLocType tlv.Type = 1
19

20
        // A tlv type used to serialize and deserialize the
21
        // `InitialLocalBalance` field.
22
        initialLocalBalanceType tlv.Type = 2
23

24
        // A tlv type used to serialize and deserialize the
25
        // `InitialRemoteBalance` field.
26
        initialRemoteBalanceType tlv.Type = 3
27
)
28

29
var (
30
        // chanInfoKey can be accessed within the bucket for a channel
31
        // (identified by its chanPoint). This key stores all the static
32
        // information for a channel which is decided at the end of  the
33
        // funding flow.
34
        chanInfoKey = []byte("chan-info-key")
35

36
        // localUpfrontShutdownKey can be accessed within the bucket for a
37
        // channel (identified by its chanPoint). This key stores an optional
38
        // upfront shutdown script for the local peer.
39
        localUpfrontShutdownKey = []byte("local-upfront-shutdown-key")
40

41
        // remoteUpfrontShutdownKey can be accessed within the bucket for a
42
        // channel (identified by its chanPoint). This key stores an optional
43
        // upfront shutdown script for the remote peer.
44
        remoteUpfrontShutdownKey = []byte("remote-upfront-shutdown-key")
45

46
        // lastWasRevokeKey is a key that stores true when the last update we
47
        // sent was a revocation and false when it was a commitment signature.
48
        // This is nil in the case of new channels with no updates exchanged.
49
        lastWasRevokeKey = []byte("last-was-revoke")
50

51
        // ErrNoChanInfoFound is returned when a particular channel does not
52
        // have any channels state.
53
        ErrNoChanInfoFound = fmt.Errorf("no chan info found")
54
)
55

56
// OpenChannel embeds a mig26.OpenChannel with the extra update-to-date
57
// serialization and deserialization methods.
58
//
59
// NOTE: doesn't have the Packager field as it's not used in current migration.
60
type OpenChannel struct {
61
        mig26.OpenChannel
62
}
63

64
// FetchChanInfo deserializes the channel info based on the legacy boolean.
UNCOV
65
func FetchChanInfo(chanBucket kvdb.RBucket, c *OpenChannel, legacy bool) error {
×
UNCOV
66
        infoBytes := chanBucket.Get(chanInfoKey)
×
UNCOV
67
        if infoBytes == nil {
×
68
                return ErrNoChanInfoFound
×
69
        }
×
UNCOV
70
        r := bytes.NewReader(infoBytes)
×
UNCOV
71

×
UNCOV
72
        var (
×
UNCOV
73
                chanType   mig.ChannelType
×
UNCOV
74
                chanStatus mig.ChannelStatus
×
UNCOV
75
        )
×
UNCOV
76

×
UNCOV
77
        if err := mig.ReadElements(r,
×
UNCOV
78
                &chanType, &c.ChainHash, &c.FundingOutpoint,
×
UNCOV
79
                &c.ShortChannelID, &c.IsPending, &c.IsInitiator,
×
UNCOV
80
                &chanStatus, &c.FundingBroadcastHeight,
×
UNCOV
81
                &c.NumConfsRequired, &c.ChannelFlags,
×
UNCOV
82
                &c.IdentityPub, &c.Capacity, &c.TotalMSatSent,
×
UNCOV
83
                &c.TotalMSatReceived,
×
UNCOV
84
        ); err != nil {
×
85
                return fmt.Errorf("ReadElements got: %w", err)
×
86
        }
×
87

UNCOV
88
        c.ChanType = mig25.ChannelType(chanType)
×
UNCOV
89
        c.ChanStatus = mig25.ChannelStatus(chanStatus)
×
UNCOV
90

×
UNCOV
91
        // For single funder channels that we initiated and have the funding
×
UNCOV
92
        // transaction to, read the funding txn.
×
UNCOV
93
        if c.FundingTxPresent() {
×
UNCOV
94
                if err := mig.ReadElement(r, &c.FundingTxn); err != nil {
×
UNCOV
95
                        return fmt.Errorf("read FundingTxn got: %w", err)
×
UNCOV
96
                }
×
97
        }
98

UNCOV
99
        if err := mig.ReadChanConfig(r, &c.LocalChanCfg); err != nil {
×
100
                return fmt.Errorf("read LocalChanCfg got: %w", err)
×
101
        }
×
UNCOV
102
        if err := mig.ReadChanConfig(r, &c.RemoteChanCfg); err != nil {
×
103
                return fmt.Errorf("read RemoteChanCfg got: %w", err)
×
104
        }
×
105

106
        // Retrieve the boolean stored under lastWasRevokeKey.
UNCOV
107
        lastWasRevokeBytes := chanBucket.Get(lastWasRevokeKey)
×
UNCOV
108
        if lastWasRevokeBytes == nil {
×
UNCOV
109
                // If nothing has been stored under this key, we store false in
×
UNCOV
110
                // the OpenChannel struct.
×
UNCOV
111
                c.LastWasRevoke = false
×
UNCOV
112
        } else {
×
113
                // Otherwise, read the value into the LastWasRevoke field.
×
114
                revokeReader := bytes.NewReader(lastWasRevokeBytes)
×
115
                err := mig.ReadElements(revokeReader, &c.LastWasRevoke)
×
116
                if err != nil {
×
117
                        return fmt.Errorf("read LastWasRevoke got: %w", err)
×
118
                }
×
119
        }
120

121
        // Make the tlv stream based on the legacy param.
UNCOV
122
        var (
×
UNCOV
123
                ts            *tlv.Stream
×
UNCOV
124
                err           error
×
UNCOV
125
                localBalance  uint64
×
UNCOV
126
                remoteBalance uint64
×
UNCOV
127
        )
×
UNCOV
128

×
UNCOV
129
        keyLocRecord := mig25.MakeKeyLocRecord(
×
UNCOV
130
                keyLocType, &c.RevocationKeyLocator,
×
UNCOV
131
        )
×
UNCOV
132

×
UNCOV
133
        // If it's legacy, create the stream with a single tlv record.
×
UNCOV
134
        if legacy {
×
UNCOV
135
                ts, err = tlv.NewStream(keyLocRecord)
×
UNCOV
136
        } else {
×
UNCOV
137
                // Otherwise, for the new format, we will encode the balance
×
UNCOV
138
                // fields in the tlv stream too.
×
UNCOV
139
                ts, err = tlv.NewStream(
×
UNCOV
140
                        keyLocRecord,
×
UNCOV
141
                        tlv.MakePrimitiveRecord(
×
UNCOV
142
                                initialLocalBalanceType, &localBalance,
×
UNCOV
143
                        ),
×
UNCOV
144
                        tlv.MakePrimitiveRecord(
×
UNCOV
145
                                initialRemoteBalanceType, &remoteBalance,
×
UNCOV
146
                        ),
×
UNCOV
147
                )
×
UNCOV
148
        }
×
UNCOV
149
        if err != nil {
×
150
                return fmt.Errorf("create tlv stream got: %w", err)
×
151
        }
×
152

UNCOV
153
        if err := ts.Decode(r); err != nil {
×
UNCOV
154
                return fmt.Errorf("decode tlv stream got: %w", err)
×
UNCOV
155
        }
×
156

157
        // For the new format, attach the balance fields.
UNCOV
158
        if !legacy {
×
UNCOV
159
                c.InitialLocalBalance = lnwire.MilliSatoshi(localBalance)
×
UNCOV
160
                c.InitialRemoteBalance = lnwire.MilliSatoshi(remoteBalance)
×
UNCOV
161
        }
×
162

163
        // Finally, read the optional shutdown scripts.
UNCOV
164
        if err := mig25.GetOptionalUpfrontShutdownScript(
×
UNCOV
165
                chanBucket, localUpfrontShutdownKey, &c.LocalShutdownScript,
×
UNCOV
166
        ); err != nil {
×
167
                return fmt.Errorf("local shutdown script got: %w", err)
×
168
        }
×
169

UNCOV
170
        return mig25.GetOptionalUpfrontShutdownScript(
×
UNCOV
171
                chanBucket, remoteUpfrontShutdownKey, &c.RemoteShutdownScript,
×
UNCOV
172
        )
×
173
}
174

175
// PutChanInfo serializes the channel info based on the legacy boolean.
UNCOV
176
func PutChanInfo(chanBucket kvdb.RwBucket, c *OpenChannel, legacy bool) error {
×
UNCOV
177
        var w bytes.Buffer
×
UNCOV
178
        if err := mig.WriteElements(&w,
×
UNCOV
179
                mig.ChannelType(c.ChanType), c.ChainHash, c.FundingOutpoint,
×
UNCOV
180
                c.ShortChannelID, c.IsPending, c.IsInitiator,
×
UNCOV
181
                mig.ChannelStatus(c.ChanStatus), c.FundingBroadcastHeight,
×
UNCOV
182
                c.NumConfsRequired, c.ChannelFlags,
×
UNCOV
183
                c.IdentityPub, c.Capacity, c.TotalMSatSent,
×
UNCOV
184
                c.TotalMSatReceived,
×
UNCOV
185
        ); err != nil {
×
186
                return err
×
187
        }
×
188

189
        // For single funder channels that we initiated, and we have the
190
        // funding transaction, then write the funding txn.
UNCOV
191
        if c.FundingTxPresent() {
×
UNCOV
192
                if err := mig.WriteElement(&w, c.FundingTxn); err != nil {
×
193
                        return err
×
194
                }
×
195
        }
196

UNCOV
197
        if err := mig.WriteChanConfig(&w, &c.LocalChanCfg); err != nil {
×
198
                return err
×
199
        }
×
UNCOV
200
        if err := mig.WriteChanConfig(&w, &c.RemoteChanCfg); err != nil {
×
201
                return err
×
202
        }
×
203

204
        // Make the tlv stream based on the legacy param.
UNCOV
205
        tlvStream, err := mig26.MakeTlvStream(&c.OpenChannel, legacy)
×
UNCOV
206
        if err != nil {
×
207
                return err
×
208
        }
×
209

UNCOV
210
        if err := tlvStream.Encode(&w); err != nil {
×
211
                return err
×
212
        }
×
213

UNCOV
214
        if err := chanBucket.Put(chanInfoKey, w.Bytes()); err != nil {
×
215
                return err
×
216
        }
×
217

218
        // Finally, add optional shutdown scripts for the local and remote peer
219
        // if they are present.
UNCOV
220
        if err := mig25.PutOptionalUpfrontShutdownScript(
×
UNCOV
221
                chanBucket, localUpfrontShutdownKey, c.LocalShutdownScript,
×
UNCOV
222
        ); err != nil {
×
223
                return err
×
224
        }
×
225

UNCOV
226
        return mig25.PutOptionalUpfrontShutdownScript(
×
UNCOV
227
                chanBucket, remoteUpfrontShutdownKey, c.RemoteShutdownScript,
×
UNCOV
228
        )
×
229
}
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