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

lightningnetwork / lnd / 15561477203

10 Jun 2025 01:54PM UTC coverage: 58.351% (-10.1%) from 68.487%
15561477203

Pull #9356

github

web-flow
Merge 6440b25db into c6d6d4c0b
Pull Request #9356: lnrpc: add incoming/outgoing channel ids filter to forwarding history request

33 of 36 new or added lines in 2 files covered. (91.67%)

28366 existing lines in 455 files now uncovered.

97715 of 167461 relevant lines covered (58.35%)

1.81 hits per line

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

0.0
/channeldb/migration25/migration.go
1
package migration25
2

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

7
        mig "github.com/lightningnetwork/lnd/channeldb/migration_01_to_11"
8
        "github.com/lightningnetwork/lnd/kvdb"
9
)
10

11
var (
12
        // openChanBucket stores all the currently open channels. This bucket
13
        // has a second, nested bucket which is keyed by a node's ID. Within
14
        // that node ID bucket, all attributes required to track, update, and
15
        // close a channel are stored.
16
        openChannelBucket = []byte("open-chan-bucket")
17

18
        // chanInfoKey can be accessed within the bucket for a channel
19
        // (identified by its chanPoint). This key stores all the static
20
        // information for a channel which is decided at the end of  the
21
        // funding flow.
22
        chanInfoKey = []byte("chan-info-key")
23

24
        // ErrNoChanDBExists is returned when a channel bucket hasn't been
25
        // created.
26
        ErrNoChanDBExists = fmt.Errorf("channel db has not yet been created")
27

28
        // ErrNoActiveChannels  is returned when there is no active (open)
29
        // channels within the database.
30
        ErrNoActiveChannels = fmt.Errorf("no active channels exist")
31

32
        // ErrChannelNotFound is returned when we attempt to locate a channel
33
        // for a specific chain, but it is not found.
34
        ErrChannelNotFound = fmt.Errorf("channel not found")
35
)
36

37
// MigrateInitialBalances patches the two new fields, InitialLocalBalance and
38
// InitialRemoteBalance, for all the open channels. It does so by reading the
39
// revocation log at height 0 to learn the initial balances and then updates
40
// the channel's info.
41
// The channel info is saved in the nested bucket which is accessible via
42
// nodePub:chainHash:chanPoint. If any of the sub-buckets turns out to be nil,
43
// we will log the error and continue to process the rest.
UNCOV
44
func MigrateInitialBalances(tx kvdb.RwTx) error {
×
UNCOV
45
        log.Infof("Migrating initial local and remote balances...")
×
UNCOV
46

×
UNCOV
47
        openChanBucket := tx.ReadWriteBucket(openChannelBucket)
×
UNCOV
48

×
UNCOV
49
        // If no bucket is found, we can exit early.
×
UNCOV
50
        if openChanBucket == nil {
×
51
                return nil
×
52
        }
×
53

54
        // Read a list of open channels.
UNCOV
55
        channels, err := findOpenChannels(openChanBucket)
×
UNCOV
56
        if err != nil {
×
57
                return err
×
58
        }
×
59

60
        // Migrate the balances.
UNCOV
61
        for _, c := range channels {
×
UNCOV
62
                if err := migrateBalances(tx, c); err != nil {
×
UNCOV
63
                        return err
×
UNCOV
64
                }
×
65
        }
66

UNCOV
67
        return err
×
68
}
69

70
// findOpenChannels finds all open channels.
UNCOV
71
func findOpenChannels(openChanBucket kvdb.RBucket) ([]*OpenChannel, error) {
×
UNCOV
72
        channels := []*OpenChannel{}
×
UNCOV
73

×
UNCOV
74
        // readChannel is a helper closure that reads the channel info from the
×
UNCOV
75
        // channel bucket.
×
UNCOV
76
        readChannel := func(chainBucket kvdb.RBucket, cp []byte) error {
×
UNCOV
77
                c := &OpenChannel{}
×
UNCOV
78

×
UNCOV
79
                // Read the sub-bucket level 3.
×
UNCOV
80
                chanBucket := chainBucket.NestedReadBucket(
×
UNCOV
81
                        cp,
×
UNCOV
82
                )
×
UNCOV
83
                if chanBucket == nil {
×
84
                        log.Errorf("unable to read bucket for chanPoint=%x", cp)
×
85
                        return nil
×
86
                }
×
87
                // Get the old channel info.
UNCOV
88
                if err := fetchChanInfo(chanBucket, c, true); err != nil {
×
89
                        return fmt.Errorf("unable to fetch chan info: %w", err)
×
90
                }
×
91

92
                // Fetch the channel commitments, which are useful for freshly
93
                // open channels as they don't have any revocation logs and
94
                // their current commitments reflect the initial balances.
UNCOV
95
                if err := FetchChanCommitments(chanBucket, c); err != nil {
×
96
                        return fmt.Errorf("unable to fetch chan commits: %w",
×
97
                                err)
×
98
                }
×
99

UNCOV
100
                channels = append(channels, c)
×
UNCOV
101

×
UNCOV
102
                return nil
×
103
        }
104

105
        // Iterate the root bucket.
UNCOV
106
        err := openChanBucket.ForEach(func(nodePub, v []byte) error {
×
UNCOV
107
                // Ensure that this is a key the same size as a pubkey, and
×
UNCOV
108
                // also that it leads directly to a bucket.
×
UNCOV
109
                if len(nodePub) != 33 || v != nil {
×
110
                        return nil
×
111
                }
×
112

113
                // Read the sub-bucket level 1.
UNCOV
114
                nodeChanBucket := openChanBucket.NestedReadBucket(nodePub)
×
UNCOV
115
                if nodeChanBucket == nil {
×
116
                        log.Errorf("no bucket for node %x", nodePub)
×
117
                        return nil
×
118
                }
×
119

120
                // Iterate the bucket.
UNCOV
121
                return nodeChanBucket.ForEach(func(chainHash, _ []byte) error {
×
UNCOV
122
                        // Read the sub-bucket level 2.
×
UNCOV
123
                        chainBucket := nodeChanBucket.NestedReadBucket(
×
UNCOV
124
                                chainHash,
×
UNCOV
125
                        )
×
UNCOV
126
                        if chainBucket == nil {
×
127
                                log.Errorf("unable to read bucket for chain=%x",
×
128
                                        chainHash)
×
129
                                return nil
×
130
                        }
×
131

132
                        // Iterate the bucket.
UNCOV
133
                        return chainBucket.ForEach(func(cp, _ []byte) error {
×
UNCOV
134
                                return readChannel(chainBucket, cp)
×
UNCOV
135
                        })
×
136
                })
137
        })
138

UNCOV
139
        if err != nil {
×
140
                return nil, err
×
141
        }
×
142

UNCOV
143
        return channels, nil
×
144
}
145

146
// migrateBalances queries the revocation log at height 0 to find the initial
147
// balances and save them to the channel info.
UNCOV
148
func migrateBalances(tx kvdb.RwTx, c *OpenChannel) error {
×
UNCOV
149
        // Get the bucket.
×
UNCOV
150
        chanBucket, err := FetchChanBucket(tx, c)
×
UNCOV
151
        if err != nil {
×
152
                return err
×
153
        }
×
154
        // Get the initial balances.
UNCOV
155
        localAmt, remoteAmt, err := c.balancesAtHeight(chanBucket, 0)
×
UNCOV
156
        if err != nil {
×
UNCOV
157
                return fmt.Errorf("unable to get initial balances: %w", err)
×
UNCOV
158
        }
×
159

UNCOV
160
        c.InitialLocalBalance = localAmt
×
UNCOV
161
        c.InitialRemoteBalance = remoteAmt
×
UNCOV
162

×
UNCOV
163
        // Update the channel info.
×
UNCOV
164
        if err := putChanInfo(chanBucket, c, false); err != nil {
×
165
                return fmt.Errorf("unable to put chan info: %w", err)
×
166
        }
×
167

UNCOV
168
        return nil
×
169
}
170

171
// FetchChanBucket is a helper function that returns the bucket where a
172
// channel's data resides in given: the public key for the node, the outpoint,
173
// and the chainhash that the channel resides on.
UNCOV
174
func FetchChanBucket(tx kvdb.RwTx, c *OpenChannel) (kvdb.RwBucket, error) {
×
UNCOV
175
        // First fetch the top level bucket which stores all data related to
×
UNCOV
176
        // current, active channels.
×
UNCOV
177
        openChanBucket := tx.ReadWriteBucket(openChannelBucket)
×
UNCOV
178
        if openChanBucket == nil {
×
179
                return nil, ErrNoChanDBExists
×
180
        }
×
181

182
        // Within this top level bucket, fetch the bucket dedicated to storing
183
        // open channel data specific to the remote node.
UNCOV
184
        nodePub := c.IdentityPub.SerializeCompressed()
×
UNCOV
185
        nodeChanBucket := openChanBucket.NestedReadWriteBucket(nodePub)
×
UNCOV
186
        if nodeChanBucket == nil {
×
187
                return nil, ErrNoActiveChannels
×
188
        }
×
189

190
        // We'll then recurse down an additional layer in order to fetch the
191
        // bucket for this particular chain.
UNCOV
192
        chainBucket := nodeChanBucket.NestedReadWriteBucket(c.ChainHash[:])
×
UNCOV
193
        if chainBucket == nil {
×
194
                return nil, ErrNoActiveChannels
×
195
        }
×
196

197
        // With the bucket for the node and chain fetched, we can now go down
198
        // another level, for this channel itself.
UNCOV
199
        var chanPointBuf bytes.Buffer
×
UNCOV
200
        err := mig.WriteOutpoint(&chanPointBuf, &c.FundingOutpoint)
×
UNCOV
201
        if err != nil {
×
202
                return nil, err
×
203
        }
×
204

UNCOV
205
        chanBucket := chainBucket.NestedReadWriteBucket(chanPointBuf.Bytes())
×
UNCOV
206
        if chanBucket == nil {
×
207
                return nil, ErrChannelNotFound
×
208
        }
×
209

UNCOV
210
        return chanBucket, nil
×
211
}
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