dump+dumpchannels: add additional debug info to channels

pull/79/head
Oliver Gugger 8 months ago
parent 4926ae2db8
commit bae1bff7e2
No known key found for this signature in database
GPG Key ID: 8E4256593F177720

@ -107,7 +107,30 @@ func dumpClosedChannelInfo(chanDb *channeldb.ChannelStateDB) error {
return err return err
} }
dumpChannels, err := dump.ClosedChannelDump(channels, chainParams) historicalChannels := make([]*channeldb.OpenChannel, len(channels))
for idx := range channels {
closedChan := channels[idx]
histChan, err := chanDb.FetchHistoricalChannel(
&closedChan.ChanPoint,
)
switch err {
// The channel was closed in a pre-historic version of lnd.
// Ignore the error.
case channeldb.ErrNoHistoricalBucket:
case channeldb.ErrChannelNotFound:
case nil:
historicalChannels[idx] = histChan
// Non-nil error not due to older versions of lnd.
default:
return err
}
}
dumpChannels, err := dump.ClosedChannelDump(
channels, historicalChannels, chainParams,
)
if err != nil { if err != nil {
return fmt.Errorf("error converting to dump format: %w", err) return fmt.Errorf("error converting to dump format: %w", err)
} }

@ -10,10 +10,12 @@ import (
"github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
"github.com/lightningnetwork/lnd/chanbackup" "github.com/lightningnetwork/lnd/chanbackup"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
) )
@ -21,14 +23,14 @@ const (
lndInternalDerivationPath = "m/1017'/%d'/%d'/0/%d" lndInternalDerivationPath = "m/1017'/%d'/%d'/0/%d"
) )
// BackupSingle is the information we want to dump from an lnd channel backup // BackupMulti is the information we want to dump from a lnd channel backup
// multi file. See `chanbackup.Multi` for information about the fields. // multi file. See `chanbackup.Multi` for information about the fields.
type BackupMulti struct { type BackupMulti struct {
Version chanbackup.MultiBackupVersion Version chanbackup.MultiBackupVersion
StaticBackups []BackupSingle StaticBackups []BackupSingle
} }
// BackupSingle is the information we want to dump from an lnd channel backup. // BackupSingle is the information we want to dump from a lnd channel backup.
// See `chanbackup.Single` for information about the fields. // See `chanbackup.Single` for information about the fields.
type BackupSingle struct { type BackupSingle struct {
Version chanbackup.SingleBackupVersion Version chanbackup.SingleBackupVersion
@ -57,6 +59,7 @@ type OpenChannel struct {
FundingBroadcastHeight uint32 FundingBroadcastHeight uint32
NumConfsRequired uint16 NumConfsRequired uint16
ChannelFlags lnwire.FundingFlag ChannelFlags lnwire.FundingFlag
ThawHeight uint32
IdentityPub string IdentityPub string
Capacity btcutil.Amount Capacity btcutil.Amount
TotalMSatSent lnwire.MilliSatoshi TotalMSatSent lnwire.MilliSatoshi
@ -66,6 +69,8 @@ type OpenChannel struct {
RemoteChanCfg ChannelConfig RemoteChanCfg ChannelConfig
LocalCommitment channeldb.ChannelCommitment LocalCommitment channeldb.ChannelCommitment
RemoteCommitment channeldb.ChannelCommitment RemoteCommitment channeldb.ChannelCommitment
LocalCommitmentDebug ChannelDebugInfo
RemoteCommitmentDebug ChannelDebugInfo
RemoteCurrentRevocation string RemoteCurrentRevocation string
RemoteNextRevocation string RemoteNextRevocation string
FundingTxn string FundingTxn string
@ -73,24 +78,38 @@ type OpenChannel struct {
RemoteShutdownScript lnwire.DeliveryAddress RemoteShutdownScript lnwire.DeliveryAddress
} }
// ChannelDebugInfo is a struct that holds additional information about an open
// or pending channel that is useful for debugging.
type ChannelDebugInfo struct {
ToLocalScript string
ToLocalAddr string
ToRemoteScript string
ToRemoteAddr string
}
// ClosedChannel is the information we want to dump from a closed channel in // ClosedChannel is the information we want to dump from a closed channel in
// lnd's channel DB. See `channeldb.ChannelCloseSummary` for information about // lnd's channel DB. See `channeldb.ChannelCloseSummary` for information about
// the fields. // the fields.
type ClosedChannel struct { type ClosedChannel struct {
ChanPoint string ChanPoint string
ShortChanID lnwire.ShortChannelID ShortChanID lnwire.ShortChannelID
ChainHash chainhash.Hash ChainHash chainhash.Hash
ClosingTXID string ClosingTXID string
RemotePub string RemotePub string
Capacity btcutil.Amount Capacity btcutil.Amount
CloseHeight uint32 CloseHeight uint32
SettledBalance btcutil.Amount SettledBalance btcutil.Amount
TimeLockedBalance btcutil.Amount TimeLockedBalance btcutil.Amount
CloseType string CloseType string
IsPending bool IsPending bool
RemoteCurrentRevocation string RemoteCurrentRevocation string
RemoteNextRevocation string RemoteNextRevocation string
LocalChanConfig ChannelConfig LocalChanConfig ChannelConfig
NextLocalCommitHeight uint64
RemoteCommitTailHeight uint64
LastRemoteCommitSecret string
LocalUnrevokedCommitPoint string
HistoricalChannel *OpenChannel
} }
// ChannelConfig is the information we want to dump from a channel // ChannelConfig is the information we want to dump from a channel
@ -119,68 +138,194 @@ func OpenChannelDump(channels []*channeldb.OpenChannel,
dumpChannels := make([]OpenChannel, len(channels)) dumpChannels := make([]OpenChannel, len(channels))
for idx, channel := range channels { for idx, channel := range channels {
var buf bytes.Buffer openChan, err := openChannelDump(channel, params)
if channel.FundingTxn != nil { if err != nil {
err := channel.FundingTxn.Serialize(&buf) return nil, fmt.Errorf("error converting to dump "+
if err != nil { "format: %w", err)
return nil, err
}
} }
revPreimage, err := channel.RevocationProducer.AtIndex( dumpChannels[idx] = *openChan
channel.LocalCommitment.CommitHeight, }
) return dumpChannels, nil
}
func openChannelDump(channel *channeldb.OpenChannel,
params *chaincfg.Params) (*OpenChannel, error) {
var buf bytes.Buffer
if channel.FundingTxn != nil {
err := channel.FundingTxn.Serialize(&buf)
if err != nil { if err != nil {
return nil, err return nil, err
} }
perCommitPoint := input.ComputeCommitmentPoint(revPreimage[:])
dumpChannels[idx] = OpenChannel{
ChanType: channel.ChanType,
ChainHash: channel.ChainHash,
FundingOutpoint: channel.FundingOutpoint.String(),
ShortChannelID: channel.ShortChannelID,
IsPending: channel.IsPending,
IsInitiator: channel.IsInitiator,
ChanStatus: channel.ChanStatus(),
FundingBroadcastHeight: channel.FundingBroadcastHeight,
NumConfsRequired: channel.NumConfsRequired,
ChannelFlags: channel.ChannelFlags,
IdentityPub: PubKeyToString(
channel.IdentityPub,
),
Capacity: channel.Capacity,
TotalMSatSent: channel.TotalMSatSent,
TotalMSatReceived: channel.TotalMSatReceived,
PerCommitPoint: PubKeyToString(perCommitPoint),
LocalChanCfg: ToChannelConfig(
params, channel.LocalChanCfg,
),
RemoteChanCfg: ToChannelConfig(
params, channel.RemoteChanCfg,
),
LocalCommitment: channel.LocalCommitment,
RemoteCommitment: channel.RemoteCommitment,
RemoteCurrentRevocation: PubKeyToString(
channel.RemoteCurrentRevocation,
),
RemoteNextRevocation: PubKeyToString(
channel.RemoteNextRevocation,
),
FundingTxn: hex.EncodeToString(buf.Bytes()),
LocalShutdownScript: channel.LocalShutdownScript,
RemoteShutdownScript: channel.RemoteShutdownScript,
}
} }
return dumpChannels, nil revPreimage, err := channel.RevocationProducer.AtIndex(
channel.LocalCommitment.CommitHeight,
)
if err != nil {
return nil, err
}
perCommitPoint := input.ComputeCommitmentPoint(revPreimage[:])
openChan := &OpenChannel{
ChanType: channel.ChanType,
ChainHash: channel.ChainHash,
FundingOutpoint: channel.FundingOutpoint.String(),
ShortChannelID: channel.ShortChannelID,
IsPending: channel.IsPending,
IsInitiator: channel.IsInitiator,
ChanStatus: channel.ChanStatus(),
FundingBroadcastHeight: channel.FundingBroadcastHeight,
NumConfsRequired: channel.NumConfsRequired,
ChannelFlags: channel.ChannelFlags,
ThawHeight: channel.ThawHeight,
IdentityPub: PubKeyToString(
channel.IdentityPub,
),
Capacity: channel.Capacity,
TotalMSatSent: channel.TotalMSatSent,
TotalMSatReceived: channel.TotalMSatReceived,
PerCommitPoint: PubKeyToString(perCommitPoint),
LocalChanCfg: ToChannelConfig(
params, channel.LocalChanCfg,
),
RemoteChanCfg: ToChannelConfig(
params, channel.RemoteChanCfg,
),
LocalCommitment: channel.LocalCommitment,
RemoteCommitment: channel.RemoteCommitment,
RemoteCurrentRevocation: PubKeyToString(
channel.RemoteCurrentRevocation,
),
RemoteNextRevocation: PubKeyToString(
channel.RemoteNextRevocation,
),
FundingTxn: hex.EncodeToString(buf.Bytes()),
LocalShutdownScript: channel.LocalShutdownScript,
RemoteShutdownScript: channel.RemoteShutdownScript,
}
localDebug, err := CollectDebugInfo(
channel, perCommitPoint, true, channel.IsInitiator, params,
)
if err != nil {
return nil, fmt.Errorf("error collecting local debug info: %w",
err)
}
remoteDebug, err := CollectDebugInfo(
channel, channel.RemoteCurrentRevocation, false,
!channel.IsInitiator, params,
)
if err != nil {
return nil, fmt.Errorf("error collecting remote debug info: %w",
err)
}
openChan.LocalCommitmentDebug = *localDebug
openChan.RemoteCommitmentDebug = *remoteDebug
return openChan, nil
}
// CollectDebugInfo collects the additional debug information for the given
// channel.
func CollectDebugInfo(channel *channeldb.OpenChannel,
commitPoint *btcec.PublicKey, ourCommit, initiator bool,
params *chaincfg.Params) (*ChannelDebugInfo, error) {
chanType := channel.ChanType
ourChanCfg := &channel.LocalChanCfg
theirChanCfg := &channel.RemoteChanCfg
leaseExpiry := channel.ThawHeight
keyRing := lnwallet.DeriveCommitmentKeys(
commitPoint, ourCommit, chanType, ourChanCfg, theirChanCfg,
)
// First, we create the script for the delayed "pay-to-self" output.
// This output has 2 main redemption clauses: either we can redeem the
// output after a relative block delay, or the remote node can claim
// the funds with the revocation key if we broadcast a revoked
// commitment transaction.
toLocalScript, err := lnwallet.CommitScriptToSelf(
chanType, initiator, keyRing.ToLocalKey, keyRing.RevocationKey,
uint32(ourChanCfg.CsvDelay), leaseExpiry,
)
if err != nil {
return nil, err
}
// Next, we create the script paying to the remote.
toRemoteScript, _, err := lnwallet.CommitScriptToRemote(
chanType, initiator, keyRing.ToRemoteKey, leaseExpiry,
)
if err != nil {
return nil, err
}
toLocalPkScript, err := txscript.ParsePkScript(toLocalScript.PkScript)
if err != nil {
return nil, err
}
toLocalAddr, err := toLocalPkScript.Address(params)
if err != nil {
return nil, err
}
toRemotePkScript, err := txscript.ParsePkScript(toRemoteScript.PkScript)
if err != nil {
return nil, err
}
toRemoteAddr, err := toRemotePkScript.Address(params)
if err != nil {
return nil, err
}
return &ChannelDebugInfo{
ToLocalScript: hex.EncodeToString(toLocalScript.WitnessScript),
ToLocalAddr: toLocalAddr.String(),
ToRemoteScript: hex.EncodeToString(
toRemoteScript.WitnessScript,
),
ToRemoteAddr: toRemoteAddr.String(),
}, nil
} }
// ClosedChannelDump converts the closed channels in the given channel DB into a // ClosedChannelDump converts the closed channels in the given channel DB into a
// dumpable format. // dumpable format.
func ClosedChannelDump(channels []*channeldb.ChannelCloseSummary, func ClosedChannelDump(channels []*channeldb.ChannelCloseSummary,
historicalChannels []*channeldb.OpenChannel,
params *chaincfg.Params) ([]ClosedChannel, error) { params *chaincfg.Params) ([]ClosedChannel, error) {
dumpChannels := make([]ClosedChannel, len(channels)) dumpChannels := make([]ClosedChannel, len(channels))
for idx, channel := range channels { for idx, channel := range channels {
var (
nextLocalHeight, remoteTailHeight uint64
lastRemoteSecret string
localUnrevokedCommitPoint *btcec.PublicKey
historicalChannel *OpenChannel
)
if channel.LastChanSyncMsg != nil {
msg := channel.LastChanSyncMsg
nextLocalHeight = msg.NextLocalCommitHeight
remoteTailHeight = msg.RemoteCommitTailHeight
lastRemoteSecret = hex.EncodeToString(
msg.LastRemoteCommitSecret[:],
)
localUnrevokedCommitPoint = msg.LocalUnrevokedCommitPoint
}
histChan := historicalChannels[idx]
if histChan != nil {
openChan, err := openChannelDump(histChan, params)
if err != nil {
return nil, fmt.Errorf("error converting to "+
"dump format: %w", err)
}
historicalChannel = openChan
}
dumpChannels[idx] = ClosedChannel{ dumpChannels[idx] = ClosedChannel{
ChanPoint: channel.ChanPoint.String(), ChanPoint: channel.ChanPoint.String(),
ShortChanID: channel.ShortChanID, ShortChanID: channel.ShortChanID,
@ -204,6 +349,13 @@ func ClosedChannelDump(channels []*channeldb.ChannelCloseSummary,
LocalChanConfig: ToChannelConfig( LocalChanConfig: ToChannelConfig(
params, channel.LocalChanConfig, params, channel.LocalChanConfig,
), ),
NextLocalCommitHeight: nextLocalHeight,
RemoteCommitTailHeight: remoteTailHeight,
LastRemoteCommitSecret: lastRemoteSecret,
LocalUnrevokedCommitPoint: PubKeyToString(
localUnrevokedCommitPoint,
),
HistoricalChannel: historicalChannel,
} }
} }
return dumpChannels, nil return dumpChannels, nil

Loading…
Cancel
Save