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

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

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

12
var (
13
        // historicalChannelBucket stores all channels that have seen their
14
        // commitment tx confirm. All information from their previous open state
15
        // is retained.
16
        historicalChannelBucket = []byte("historical-chan-bucket")
17
)
18

19
// MigrateHistoricalBalances patches the two new fields, `InitialLocalBalance`
20
// and `InitialRemoteBalance`, for all the open channels saved in historical
21
// channel bucket. Unlike migration 25, it will only read the old channel info
22
// first and then patch the new tlv records with empty values. For historical
23
// channels, we previously didn't save the initial balances anywhere and since
24
// it's corresponding open channel bucket is deleted after closure, we have
25
// lost that balance info.
UNCOV
26
func MigrateHistoricalBalances(tx kvdb.RwTx) error {
×
UNCOV
27
        log.Infof("Migrating historical local and remote balances...")
×
UNCOV
28

×
UNCOV
29
        // First fetch the top level bucket which stores all data related to
×
UNCOV
30
        // historically stored channels.
×
UNCOV
31
        rootBucket := tx.ReadWriteBucket(historicalChannelBucket)
×
UNCOV
32

×
UNCOV
33
        // If no bucket is found, we can exit early.
×
UNCOV
34
        if rootBucket == nil {
×
35
                return nil
×
36
        }
×
37

38
        // Read a list of historical channels.
UNCOV
39
        channels, err := findHistoricalChannels(rootBucket)
×
UNCOV
40
        if err != nil {
×
41
                return err
×
42
        }
×
43

44
        // Migrate the balances.
UNCOV
45
        for _, c := range channels {
×
UNCOV
46
                if err := migrateBalances(rootBucket, c); err != nil {
×
47
                        return err
×
48
                }
×
49
        }
50

UNCOV
51
        return err
×
52
}
53

54
// findHistoricalChannels finds all historical channels.
55
func findHistoricalChannels(historicalBucket kvdb.RBucket) ([]*OpenChannel,
UNCOV
56
        error) {
×
UNCOV
57

×
UNCOV
58
        channels := []*OpenChannel{}
×
UNCOV
59

×
UNCOV
60
        // readChannel is a helper closure that reads the channel info from the
×
UNCOV
61
        // historical sub-bucket.
×
UNCOV
62
        readChannel := func(rootBucket kvdb.RBucket, cp []byte) error {
×
UNCOV
63
                c := &OpenChannel{}
×
UNCOV
64

×
UNCOV
65
                chanPointBuf := bytes.NewBuffer(cp)
×
UNCOV
66
                err := mig.ReadOutpoint(chanPointBuf, &c.FundingOutpoint)
×
UNCOV
67
                if err != nil {
×
68
                        return fmt.Errorf("read funding outpoint got: %w", err)
×
69
                }
×
70

71
                // Read the sub-bucket.
UNCOV
72
                chanBucket := rootBucket.NestedReadBucket(cp)
×
UNCOV
73
                if chanBucket == nil {
×
74
                        log.Errorf("unable to read bucket for chanPoint=%s",
×
75
                                c.FundingOutpoint)
×
76
                        return nil
×
77
                }
×
78

79
                // Try to fetch channel info in old format.
UNCOV
80
                err = fetchChanInfoCompatible(chanBucket, c, true)
×
UNCOV
81
                if err != nil {
×
82
                        return fmt.Errorf("%s: fetch chan info got: %w",
×
83
                                c.FundingOutpoint, err)
×
84
                }
×
85

UNCOV
86
                channels = append(channels, c)
×
UNCOV
87

×
UNCOV
88
                return nil
×
89
        }
90

91
        // Iterate the root bucket.
UNCOV
92
        err := historicalBucket.ForEach(func(cp, _ []byte) error {
×
UNCOV
93
                return readChannel(historicalBucket, cp)
×
UNCOV
94
        })
×
95

UNCOV
96
        if err != nil {
×
97
                return nil, err
×
98
        }
×
99

UNCOV
100
        return channels, nil
×
101
}
102

103
// fetchChanInfoCompatible tries to fetch the channel info for a historical
104
// channel. It will first fetch the info assuming `InitialLocalBalance` and
105
// `InitialRemoteBalance` are not serialized. Upon receiving an error, it will
106
// then fetch it again assuming the two fields are present in db.
107
func fetchChanInfoCompatible(chanBucket kvdb.RBucket, c *OpenChannel,
UNCOV
108
        legacy bool) error {
×
UNCOV
109

×
UNCOV
110
        // Try to fetch the channel info assuming the historical channel in in
×
UNCOV
111
        // the old format, where the two fields, `InitialLocalBalance` and
×
UNCOV
112
        // `InitialRemoteBalance` are not saved to db.
×
UNCOV
113
        err := FetchChanInfo(chanBucket, c, legacy)
×
UNCOV
114
        if err == nil {
×
UNCOV
115
                return err
×
UNCOV
116
        }
×
117

118
        // If we got an error above, the historical channel may already have
119
        // the new fields saved. This could happen when a channel is closed
120
        // after applying migration 25. In this case, we'll borrow the
121
        // `FetchChanInfo` info method from migration 26 where we assume the
122
        // two fields are saved.
UNCOV
123
        return mig26.FetchChanInfo(chanBucket, &c.OpenChannel, legacy)
×
124
}
125

126
// migrateBalances serializes the channel info using the new tlv format where
127
// the two fields, `InitialLocalBalance` and `InitialRemoteBalance` are patched
128
// with empty values.
UNCOV
129
func migrateBalances(rootBucket kvdb.RwBucket, c *OpenChannel) error {
×
UNCOV
130
        var chanPointBuf bytes.Buffer
×
UNCOV
131
        err := mig.WriteOutpoint(&chanPointBuf, &c.FundingOutpoint)
×
UNCOV
132
        if err != nil {
×
133
                return err
×
134
        }
×
135

136
        // Get the channel bucket.
UNCOV
137
        chanBucket := rootBucket.NestedReadWriteBucket(chanPointBuf.Bytes())
×
UNCOV
138
        if chanBucket == nil {
×
139
                return fmt.Errorf("empty historical chan bucket")
×
140
        }
×
141

142
        // Update the channel info.
UNCOV
143
        if err := PutChanInfo(chanBucket, c, false); err != nil {
×
144
                return fmt.Errorf("unable to put chan info: %w", err)
×
145
        }
×
146

UNCOV
147
        return nil
×
148
}
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