• 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

70.0
/chanbackup/multi.go
1
package chanbackup
2

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

8
        "github.com/lightningnetwork/lnd/keychain"
9
        "github.com/lightningnetwork/lnd/lnencrypt"
10
        "github.com/lightningnetwork/lnd/lnwire"
11
)
12

13
// MultiBackupVersion denotes the version of the multi channel static channel
14
// backup. Based on this version, we know how to encode/decode packed/unpacked
15
// versions of multi backups.
16
type MultiBackupVersion byte
17

18
const (
19
        // DefaultMultiVersion is the default version of the multi channel
20
        // backup. The serialized format for this version is simply: version ||
21
        // numBackups || SCBs...
22
        DefaultMultiVersion = 0
23

24
        // NilMultiSizePacked is the size of a "nil" packed Multi (45 bytes).
25
        // This consists of the 24 byte chacha nonce, the 16 byte MAC, one byte
26
        // for the version, and 4 bytes to signal zero entries.
27
        NilMultiSizePacked = 24 + 16 + 1 + 4
28
)
29

30
// Multi is a form of static channel backup that is amenable to being
31
// serialized in a single file. Rather than a series of ciphertexts, a
32
// multi-chan backup is a single ciphertext of all static channel backups
33
// concatenated. This form factor gives users a single blob that they can use
34
// to safely copy/obtain at anytime to backup their channels.
35
type Multi struct {
36
        // Version is the version that should be observed when attempting to
37
        // pack the multi backup.
38
        Version MultiBackupVersion
39

40
        // StaticBackups is the set of single channel backups that this multi
41
        // backup is comprised of.
42
        StaticBackups []Single
43
}
44

45
// PackToWriter packs (encrypts+serializes) the target set of static channel
46
// backups into a single AEAD ciphertext into the passed io.Writer. This is the
47
// opposite of UnpackFromReader. The plaintext form of a multi-chan backup is
48
// the following: a 4 byte integer denoting the number of serialized static
49
// channel backups serialized, a series of serialized static channel backups
50
// concatenated. To pack this payload, we then apply our chacha20 AEAD to the
51
// entire payload, using the 24-byte nonce as associated data.
52
func (m Multi) PackToWriter(w io.Writer, keyRing keychain.KeyRing) error {
3✔
53
        // The only version that we know how to pack atm is version 0. Attempts
3✔
54
        // to pack any other version will result in an error.
3✔
55
        switch m.Version {
3✔
56
        case DefaultMultiVersion:
3✔
57
                break
3✔
58

UNCOV
59
        default:
×
UNCOV
60
                return fmt.Errorf("unable to pack unknown multi-version "+
×
UNCOV
61
                        "of %v", m.Version)
×
62
        }
63

64
        var multiBackupBuffer bytes.Buffer
3✔
65

3✔
66
        // First, we'll write out the version of this multi channel baackup.
3✔
67
        err := lnwire.WriteElements(&multiBackupBuffer, byte(m.Version))
3✔
68
        if err != nil {
3✔
69
                return err
×
70
        }
×
71

72
        // Now that we've written out the version of this multi-pack format,
73
        // we'll now write the total number of backups to expect after this
74
        // point.
75
        numBackups := uint32(len(m.StaticBackups))
3✔
76
        err = lnwire.WriteElements(&multiBackupBuffer, numBackups)
3✔
77
        if err != nil {
3✔
78
                return err
×
79
        }
×
80

81
        // Next, we'll serialize the raw plaintext version of each of the
82
        // backup into the intermediate buffer.
83
        for _, chanBackup := range m.StaticBackups {
6✔
84
                err := chanBackup.Serialize(&multiBackupBuffer)
3✔
85
                if err != nil {
3✔
86
                        return fmt.Errorf("unable to serialize backup "+
×
87
                                "for %v: %v", chanBackup.FundingOutpoint, err)
×
88
                }
×
89
        }
90

91
        // With the plaintext multi backup assembled, we'll now encrypt it
92
        // directly to the passed writer.
93
        e, err := lnencrypt.KeyRingEncrypter(keyRing)
3✔
94
        if err != nil {
3✔
95
                return fmt.Errorf("unable to generate encrypt key %w", err)
×
96
        }
×
97

98
        return e.EncryptPayloadToWriter(multiBackupBuffer.Bytes(), w)
3✔
99
}
100

101
// UnpackFromReader attempts to unpack (decrypt+deserialize) a packed
102
// multi-chan backup form the passed io.Reader. If we're unable to decrypt the
103
// any portion of the multi-chan backup, an error will be returned.
104
func (m *Multi) UnpackFromReader(r io.Reader, keyRing keychain.KeyRing) error {
3✔
105
        // We'll attempt to read the entire packed backup, and also decrypt it
3✔
106
        // using the passed key ring which is expected to be able to derive the
3✔
107
        // encryption keys.
3✔
108
        e, err := lnencrypt.KeyRingEncrypter(keyRing)
3✔
109
        if err != nil {
3✔
UNCOV
110
                return fmt.Errorf("unable to generate encrypt key %w", err)
×
UNCOV
111
        }
×
112
        plaintextBackup, err := e.DecryptPayloadFromReader(r)
3✔
113
        if err != nil {
3✔
114
                return err
×
115
        }
×
116
        backupReader := bytes.NewReader(plaintextBackup)
3✔
117

3✔
118
        // Now that we've decrypted the payload successfully, we can parse out
3✔
119
        // each of the individual static channel backups.
3✔
120

3✔
121
        // First, we'll need to read the version of this multi-back up so we
3✔
122
        // can know how to unpack each of the individual SCB's.
3✔
123
        var multiVersion byte
3✔
124
        err = lnwire.ReadElements(backupReader, &multiVersion)
3✔
125
        if err != nil {
3✔
126
                return err
×
127
        }
×
128

129
        m.Version = MultiBackupVersion(multiVersion)
3✔
130
        switch m.Version {
3✔
131

132
        // The default version is simply a set of serialized SCB's with the
133
        // number of total SCB's prepended to the front of the byte slice.
134
        case DefaultMultiVersion:
3✔
135
                // First, we'll need to read out the total number of backups
3✔
136
                // that've been serialized into this multi-chan backup. Each
3✔
137
                // backup is the same size, so we can continue until we've
3✔
138
                // parsed out everything.
3✔
139
                var numBackups uint32
3✔
140
                err = lnwire.ReadElements(backupReader, &numBackups)
3✔
141
                if err != nil {
3✔
142
                        return err
×
143
                }
×
144

145
                // We'll continue to parse out each backup until we've read all
146
                // that was indicated from the length prefix.
147
                for ; numBackups != 0; numBackups-- {
6✔
148
                        // Attempt to parse out the net static channel backup,
3✔
149
                        // if it's been malformed, then we'll return with an
3✔
150
                        // error
3✔
151
                        var chanBackup Single
3✔
152
                        err := chanBackup.Deserialize(backupReader)
3✔
153
                        if err != nil {
3✔
154
                                return err
×
155
                        }
×
156

157
                        // Collect the next valid chan backup into the main
158
                        // multi backup slice.
159
                        m.StaticBackups = append(m.StaticBackups, chanBackup)
3✔
160
                }
161

UNCOV
162
        default:
×
UNCOV
163
                return fmt.Errorf("unable to unpack unknown multi-version "+
×
UNCOV
164
                        "of %v", multiVersion)
×
165
        }
166

167
        return nil
3✔
168
}
169

170
// TODO(roasbeef): new key ring interface?
171
//  * just returns key given params?
172

173
// PackedMulti represents a raw fully packed (serialized+encrypted)
174
// multi-channel static channel backup.
175
type PackedMulti []byte
176

177
// Unpack attempts to unpack (decrypt+desrialize) the target packed
178
// multi-channel back up. If we're unable to fully unpack this back, then an
179
// error will be returned.
180
func (p *PackedMulti) Unpack(keyRing keychain.KeyRing) (*Multi, error) {
3✔
181
        var m Multi
3✔
182

3✔
183
        packedReader := bytes.NewReader(*p)
3✔
184
        if err := m.UnpackFromReader(packedReader, keyRing); err != nil {
3✔
UNCOV
185
                return nil, err
×
UNCOV
186
        }
×
187

188
        return &m, nil
3✔
189
}
190

191
// TODO(roasbsef): fuzz parsing
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