Skip to content

Commit bae1bff

Browse files
committed
dump+dumpchannels: add additional debug info to channels
1 parent 4926ae2 commit bae1bff

File tree

2 files changed

+240
-65
lines changed

2 files changed

+240
-65
lines changed

cmd/chantools/dumpchannels.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,30 @@ func dumpClosedChannelInfo(chanDb *channeldb.ChannelStateDB) error {
107107
return err
108108
}
109109

110-
dumpChannels, err := dump.ClosedChannelDump(channels, chainParams)
110+
historicalChannels := make([]*channeldb.OpenChannel, len(channels))
111+
for idx := range channels {
112+
closedChan := channels[idx]
113+
histChan, err := chanDb.FetchHistoricalChannel(
114+
&closedChan.ChanPoint,
115+
)
116+
switch err {
117+
// The channel was closed in a pre-historic version of lnd.
118+
// Ignore the error.
119+
case channeldb.ErrNoHistoricalBucket:
120+
case channeldb.ErrChannelNotFound:
121+
122+
case nil:
123+
historicalChannels[idx] = histChan
124+
125+
// Non-nil error not due to older versions of lnd.
126+
default:
127+
return err
128+
}
129+
}
130+
131+
dumpChannels, err := dump.ClosedChannelDump(
132+
channels, historicalChannels, chainParams,
133+
)
111134
if err != nil {
112135
return fmt.Errorf("error converting to dump format: %w", err)
113136
}

dump/dump.go

Lines changed: 216 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,27 @@ import (
1010
"github.com/btcsuite/btcd/btcutil"
1111
"github.com/btcsuite/btcd/chaincfg"
1212
"github.com/btcsuite/btcd/chaincfg/chainhash"
13+
"github.com/btcsuite/btcd/txscript"
1314
"github.com/lightningnetwork/lnd/chanbackup"
1415
"github.com/lightningnetwork/lnd/channeldb"
1516
"github.com/lightningnetwork/lnd/input"
1617
"github.com/lightningnetwork/lnd/keychain"
18+
"github.com/lightningnetwork/lnd/lnwallet"
1719
"github.com/lightningnetwork/lnd/lnwire"
1820
)
1921

2022
const (
2123
lndInternalDerivationPath = "m/1017'/%d'/%d'/0/%d"
2224
)
2325

24-
// BackupSingle is the information we want to dump from an lnd channel backup
26+
// BackupMulti is the information we want to dump from a lnd channel backup
2527
// multi file. See `chanbackup.Multi` for information about the fields.
2628
type BackupMulti struct {
2729
Version chanbackup.MultiBackupVersion
2830
StaticBackups []BackupSingle
2931
}
3032

31-
// BackupSingle is the information we want to dump from an lnd channel backup.
33+
// BackupSingle is the information we want to dump from a lnd channel backup.
3234
// See `chanbackup.Single` for information about the fields.
3335
type BackupSingle struct {
3436
Version chanbackup.SingleBackupVersion
@@ -57,6 +59,7 @@ type OpenChannel struct {
5759
FundingBroadcastHeight uint32
5860
NumConfsRequired uint16
5961
ChannelFlags lnwire.FundingFlag
62+
ThawHeight uint32
6063
IdentityPub string
6164
Capacity btcutil.Amount
6265
TotalMSatSent lnwire.MilliSatoshi
@@ -66,31 +69,47 @@ type OpenChannel struct {
6669
RemoteChanCfg ChannelConfig
6770
LocalCommitment channeldb.ChannelCommitment
6871
RemoteCommitment channeldb.ChannelCommitment
72+
LocalCommitmentDebug ChannelDebugInfo
73+
RemoteCommitmentDebug ChannelDebugInfo
6974
RemoteCurrentRevocation string
7075
RemoteNextRevocation string
7176
FundingTxn string
7277
LocalShutdownScript lnwire.DeliveryAddress
7378
RemoteShutdownScript lnwire.DeliveryAddress
7479
}
7580

81+
// ChannelDebugInfo is a struct that holds additional information about an open
82+
// or pending channel that is useful for debugging.
83+
type ChannelDebugInfo struct {
84+
ToLocalScript string
85+
ToLocalAddr string
86+
ToRemoteScript string
87+
ToRemoteAddr string
88+
}
89+
7690
// ClosedChannel is the information we want to dump from a closed channel in
7791
// lnd's channel DB. See `channeldb.ChannelCloseSummary` for information about
7892
// the fields.
7993
type ClosedChannel struct {
80-
ChanPoint string
81-
ShortChanID lnwire.ShortChannelID
82-
ChainHash chainhash.Hash
83-
ClosingTXID string
84-
RemotePub string
85-
Capacity btcutil.Amount
86-
CloseHeight uint32
87-
SettledBalance btcutil.Amount
88-
TimeLockedBalance btcutil.Amount
89-
CloseType string
90-
IsPending bool
91-
RemoteCurrentRevocation string
92-
RemoteNextRevocation string
93-
LocalChanConfig ChannelConfig
94+
ChanPoint string
95+
ShortChanID lnwire.ShortChannelID
96+
ChainHash chainhash.Hash
97+
ClosingTXID string
98+
RemotePub string
99+
Capacity btcutil.Amount
100+
CloseHeight uint32
101+
SettledBalance btcutil.Amount
102+
TimeLockedBalance btcutil.Amount
103+
CloseType string
104+
IsPending bool
105+
RemoteCurrentRevocation string
106+
RemoteNextRevocation string
107+
LocalChanConfig ChannelConfig
108+
NextLocalCommitHeight uint64
109+
RemoteCommitTailHeight uint64
110+
LastRemoteCommitSecret string
111+
LocalUnrevokedCommitPoint string
112+
HistoricalChannel *OpenChannel
94113
}
95114

96115
// ChannelConfig is the information we want to dump from a channel
@@ -119,68 +138,194 @@ func OpenChannelDump(channels []*channeldb.OpenChannel,
119138

120139
dumpChannels := make([]OpenChannel, len(channels))
121140
for idx, channel := range channels {
122-
var buf bytes.Buffer
123-
if channel.FundingTxn != nil {
124-
err := channel.FundingTxn.Serialize(&buf)
125-
if err != nil {
126-
return nil, err
127-
}
141+
openChan, err := openChannelDump(channel, params)
142+
if err != nil {
143+
return nil, fmt.Errorf("error converting to dump "+
144+
"format: %w", err)
128145
}
129-
revPreimage, err := channel.RevocationProducer.AtIndex(
130-
channel.LocalCommitment.CommitHeight,
131-
)
146+
dumpChannels[idx] = *openChan
147+
}
148+
return dumpChannels, nil
149+
}
150+
151+
func openChannelDump(channel *channeldb.OpenChannel,
152+
params *chaincfg.Params) (*OpenChannel, error) {
153+
154+
var buf bytes.Buffer
155+
if channel.FundingTxn != nil {
156+
err := channel.FundingTxn.Serialize(&buf)
132157
if err != nil {
133158
return nil, err
134159
}
135-
perCommitPoint := input.ComputeCommitmentPoint(revPreimage[:])
136-
137-
dumpChannels[idx] = OpenChannel{
138-
ChanType: channel.ChanType,
139-
ChainHash: channel.ChainHash,
140-
FundingOutpoint: channel.FundingOutpoint.String(),
141-
ShortChannelID: channel.ShortChannelID,
142-
IsPending: channel.IsPending,
143-
IsInitiator: channel.IsInitiator,
144-
ChanStatus: channel.ChanStatus(),
145-
FundingBroadcastHeight: channel.FundingBroadcastHeight,
146-
NumConfsRequired: channel.NumConfsRequired,
147-
ChannelFlags: channel.ChannelFlags,
148-
IdentityPub: PubKeyToString(
149-
channel.IdentityPub,
150-
),
151-
Capacity: channel.Capacity,
152-
TotalMSatSent: channel.TotalMSatSent,
153-
TotalMSatReceived: channel.TotalMSatReceived,
154-
PerCommitPoint: PubKeyToString(perCommitPoint),
155-
LocalChanCfg: ToChannelConfig(
156-
params, channel.LocalChanCfg,
157-
),
158-
RemoteChanCfg: ToChannelConfig(
159-
params, channel.RemoteChanCfg,
160-
),
161-
LocalCommitment: channel.LocalCommitment,
162-
RemoteCommitment: channel.RemoteCommitment,
163-
RemoteCurrentRevocation: PubKeyToString(
164-
channel.RemoteCurrentRevocation,
165-
),
166-
RemoteNextRevocation: PubKeyToString(
167-
channel.RemoteNextRevocation,
168-
),
169-
FundingTxn: hex.EncodeToString(buf.Bytes()),
170-
LocalShutdownScript: channel.LocalShutdownScript,
171-
RemoteShutdownScript: channel.RemoteShutdownScript,
172-
}
173160
}
174-
return dumpChannels, nil
161+
revPreimage, err := channel.RevocationProducer.AtIndex(
162+
channel.LocalCommitment.CommitHeight,
163+
)
164+
if err != nil {
165+
return nil, err
166+
}
167+
perCommitPoint := input.ComputeCommitmentPoint(revPreimage[:])
168+
169+
openChan := &OpenChannel{
170+
ChanType: channel.ChanType,
171+
ChainHash: channel.ChainHash,
172+
FundingOutpoint: channel.FundingOutpoint.String(),
173+
ShortChannelID: channel.ShortChannelID,
174+
IsPending: channel.IsPending,
175+
IsInitiator: channel.IsInitiator,
176+
ChanStatus: channel.ChanStatus(),
177+
FundingBroadcastHeight: channel.FundingBroadcastHeight,
178+
NumConfsRequired: channel.NumConfsRequired,
179+
ChannelFlags: channel.ChannelFlags,
180+
ThawHeight: channel.ThawHeight,
181+
IdentityPub: PubKeyToString(
182+
channel.IdentityPub,
183+
),
184+
Capacity: channel.Capacity,
185+
TotalMSatSent: channel.TotalMSatSent,
186+
TotalMSatReceived: channel.TotalMSatReceived,
187+
PerCommitPoint: PubKeyToString(perCommitPoint),
188+
LocalChanCfg: ToChannelConfig(
189+
params, channel.LocalChanCfg,
190+
),
191+
RemoteChanCfg: ToChannelConfig(
192+
params, channel.RemoteChanCfg,
193+
),
194+
LocalCommitment: channel.LocalCommitment,
195+
RemoteCommitment: channel.RemoteCommitment,
196+
RemoteCurrentRevocation: PubKeyToString(
197+
channel.RemoteCurrentRevocation,
198+
),
199+
RemoteNextRevocation: PubKeyToString(
200+
channel.RemoteNextRevocation,
201+
),
202+
FundingTxn: hex.EncodeToString(buf.Bytes()),
203+
LocalShutdownScript: channel.LocalShutdownScript,
204+
RemoteShutdownScript: channel.RemoteShutdownScript,
205+
}
206+
207+
localDebug, err := CollectDebugInfo(
208+
channel, perCommitPoint, true, channel.IsInitiator, params,
209+
)
210+
if err != nil {
211+
return nil, fmt.Errorf("error collecting local debug info: %w",
212+
err)
213+
}
214+
215+
remoteDebug, err := CollectDebugInfo(
216+
channel, channel.RemoteCurrentRevocation, false,
217+
!channel.IsInitiator, params,
218+
)
219+
if err != nil {
220+
return nil, fmt.Errorf("error collecting remote debug info: %w",
221+
err)
222+
}
223+
224+
openChan.LocalCommitmentDebug = *localDebug
225+
openChan.RemoteCommitmentDebug = *remoteDebug
226+
227+
return openChan, nil
228+
}
229+
230+
// CollectDebugInfo collects the additional debug information for the given
231+
// channel.
232+
func CollectDebugInfo(channel *channeldb.OpenChannel,
233+
commitPoint *btcec.PublicKey, ourCommit, initiator bool,
234+
params *chaincfg.Params) (*ChannelDebugInfo, error) {
235+
236+
chanType := channel.ChanType
237+
ourChanCfg := &channel.LocalChanCfg
238+
theirChanCfg := &channel.RemoteChanCfg
239+
leaseExpiry := channel.ThawHeight
240+
241+
keyRing := lnwallet.DeriveCommitmentKeys(
242+
commitPoint, ourCommit, chanType, ourChanCfg, theirChanCfg,
243+
)
244+
245+
// First, we create the script for the delayed "pay-to-self" output.
246+
// This output has 2 main redemption clauses: either we can redeem the
247+
// output after a relative block delay, or the remote node can claim
248+
// the funds with the revocation key if we broadcast a revoked
249+
// commitment transaction.
250+
toLocalScript, err := lnwallet.CommitScriptToSelf(
251+
chanType, initiator, keyRing.ToLocalKey, keyRing.RevocationKey,
252+
uint32(ourChanCfg.CsvDelay), leaseExpiry,
253+
)
254+
if err != nil {
255+
return nil, err
256+
}
257+
258+
// Next, we create the script paying to the remote.
259+
toRemoteScript, _, err := lnwallet.CommitScriptToRemote(
260+
chanType, initiator, keyRing.ToRemoteKey, leaseExpiry,
261+
)
262+
if err != nil {
263+
return nil, err
264+
}
265+
266+
toLocalPkScript, err := txscript.ParsePkScript(toLocalScript.PkScript)
267+
if err != nil {
268+
return nil, err
269+
}
270+
toLocalAddr, err := toLocalPkScript.Address(params)
271+
if err != nil {
272+
return nil, err
273+
}
274+
275+
toRemotePkScript, err := txscript.ParsePkScript(toRemoteScript.PkScript)
276+
if err != nil {
277+
return nil, err
278+
}
279+
toRemoteAddr, err := toRemotePkScript.Address(params)
280+
if err != nil {
281+
return nil, err
282+
}
283+
284+
return &ChannelDebugInfo{
285+
ToLocalScript: hex.EncodeToString(toLocalScript.WitnessScript),
286+
ToLocalAddr: toLocalAddr.String(),
287+
ToRemoteScript: hex.EncodeToString(
288+
toRemoteScript.WitnessScript,
289+
),
290+
ToRemoteAddr: toRemoteAddr.String(),
291+
}, nil
175292
}
176293

177294
// ClosedChannelDump converts the closed channels in the given channel DB into a
178295
// dumpable format.
179296
func ClosedChannelDump(channels []*channeldb.ChannelCloseSummary,
297+
historicalChannels []*channeldb.OpenChannel,
180298
params *chaincfg.Params) ([]ClosedChannel, error) {
181299

182300
dumpChannels := make([]ClosedChannel, len(channels))
183301
for idx, channel := range channels {
302+
var (
303+
nextLocalHeight, remoteTailHeight uint64
304+
lastRemoteSecret string
305+
localUnrevokedCommitPoint *btcec.PublicKey
306+
historicalChannel *OpenChannel
307+
)
308+
309+
if channel.LastChanSyncMsg != nil {
310+
msg := channel.LastChanSyncMsg
311+
nextLocalHeight = msg.NextLocalCommitHeight
312+
remoteTailHeight = msg.RemoteCommitTailHeight
313+
lastRemoteSecret = hex.EncodeToString(
314+
msg.LastRemoteCommitSecret[:],
315+
)
316+
localUnrevokedCommitPoint = msg.LocalUnrevokedCommitPoint
317+
}
318+
319+
histChan := historicalChannels[idx]
320+
if histChan != nil {
321+
openChan, err := openChannelDump(histChan, params)
322+
if err != nil {
323+
return nil, fmt.Errorf("error converting to "+
324+
"dump format: %w", err)
325+
}
326+
historicalChannel = openChan
327+
}
328+
184329
dumpChannels[idx] = ClosedChannel{
185330
ChanPoint: channel.ChanPoint.String(),
186331
ShortChanID: channel.ShortChanID,
@@ -204,6 +349,13 @@ func ClosedChannelDump(channels []*channeldb.ChannelCloseSummary,
204349
LocalChanConfig: ToChannelConfig(
205350
params, channel.LocalChanConfig,
206351
),
352+
NextLocalCommitHeight: nextLocalHeight,
353+
RemoteCommitTailHeight: remoteTailHeight,
354+
LastRemoteCommitSecret: lastRemoteSecret,
355+
LocalUnrevokedCommitPoint: PubKeyToString(
356+
localUnrevokedCommitPoint,
357+
),
358+
HistoricalChannel: historicalChannel,
207359
}
208360
}
209361
return dumpChannels, nil

0 commit comments

Comments
 (0)