Skip to content

Commit aae3990

Browse files
committed
main: add dumphistoricalchannel command.
1 parent a5a884b commit aae3990

File tree

2 files changed

+172
-0
lines changed

2 files changed

+172
-0
lines changed
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
package main
2+
3+
import (
4+
"crypto/sha256"
5+
"errors"
6+
"fmt"
7+
"strconv"
8+
"strings"
9+
10+
"github.com/btcsuite/btcd/chaincfg/chainhash"
11+
"github.com/btcsuite/btcd/wire"
12+
"github.com/davecgh/go-spew/spew"
13+
"github.com/lightninglabs/chantools/dump"
14+
"github.com/lightninglabs/chantools/lnd"
15+
"github.com/lightningnetwork/lnd/channeldb"
16+
"github.com/lightningnetwork/lnd/lnrpc"
17+
"github.com/spf13/cobra"
18+
)
19+
20+
type summaryHTLC struct {
21+
ChannelDB string
22+
ChannelPoint string
23+
24+
rootKey *rootKey
25+
cmd *cobra.Command
26+
}
27+
28+
var errBadChanPoint = errors.New("expecting chan_point to be in format of: " +
29+
"txid:index")
30+
31+
func parseChanPoint(s string) (*lnrpc.ChannelPoint, error) {
32+
split := strings.Split(s, ":")
33+
if len(split) != 2 || len(split[0]) == 0 || len(split[1]) == 0 {
34+
return nil, errBadChanPoint
35+
}
36+
37+
index, err := strconv.ParseInt(split[1], 10, 64)
38+
if err != nil {
39+
return nil, fmt.Errorf("unable to decode output index: %v", err)
40+
}
41+
42+
txid, err := chainhash.NewHashFromStr(split[0])
43+
if err != nil {
44+
return nil, fmt.Errorf("unable to parse hex string: %v", err)
45+
}
46+
47+
return &lnrpc.ChannelPoint{
48+
FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{
49+
FundingTxidBytes: txid[:],
50+
},
51+
OutputIndex: uint32(index),
52+
}, nil
53+
}
54+
55+
func dumphistoricalchan() *cobra.Command {
56+
cc := &summaryHTLC{}
57+
cc.cmd = &cobra.Command{
58+
Use: "dumphistoricalchannel",
59+
Short: "dump all information of a closed channel's last " +
60+
"commitment state",
61+
Long: `when a channel is closed we delete all the data from the
62+
openchannel bkt and move the last commitment state to
63+
the historical bkt. This command aims to dump all the
64+
necessary data from that last state.`,
65+
Example: `chantools dumphtlcsummary \
66+
--channeldb ~/.lnd/data/graph/mainnet/channel.db`,
67+
RunE: cc.Execute,
68+
}
69+
cc.cmd.Flags().StringVar(
70+
&cc.ChannelDB, "channeldb", "", "lnd channel.db file to dump "+
71+
"channels from",
72+
)
73+
cc.cmd.Flags().StringVar(
74+
&cc.ChannelPoint, "chan_point", "", "",
75+
)
76+
77+
cc.rootKey = newRootKey(cc.cmd, "deriving keys")
78+
79+
return cc.cmd
80+
}
81+
82+
func (c *summaryHTLC) Execute(_ *cobra.Command, _ []string) error {
83+
// Check that we have a channel DB.
84+
if c.ChannelDB == "" {
85+
return fmt.Errorf("channel DB is required")
86+
}
87+
db, err := lnd.OpenDB(c.ChannelDB, true)
88+
if err != nil {
89+
return fmt.Errorf("error opening rescue DB: %w", err)
90+
}
91+
defer func() { _ = db.Close() }()
92+
93+
return dumpHistoricalChanInfos(db.ChannelStateDB(), c.ChannelPoint)
94+
}
95+
96+
func dumpHistoricalChanInfos(chanDb *channeldb.ChannelStateDB, channel string) error {
97+
98+
chanPoint, err := parseChanPoint(channel)
99+
if err != nil {
100+
return err
101+
}
102+
103+
//Open the Historical Bucket
104+
fundingHash, err := chainhash.NewHash(chanPoint.GetFundingTxidBytes())
105+
106+
fmt.Println("Test", fundingHash.String())
107+
108+
if err != nil {
109+
return err
110+
}
111+
outPoint := wire.NewOutPoint(fundingHash, chanPoint.OutputIndex)
112+
113+
dbChannel, err := chanDb.FetchHistoricalChannel(outPoint)
114+
if err != nil {
115+
return err
116+
}
117+
118+
channels := []*channeldb.OpenChannel{
119+
dbChannel,
120+
}
121+
122+
dumpChannels, err := dump.OpenChannelDump(channels, chainParams)
123+
if err != nil {
124+
return fmt.Errorf("error converting to dump format: %w", err)
125+
}
126+
127+
spew.Dump(dumpChannels)
128+
129+
// For the tests, also log as trace level which is disabled by default.
130+
log.Tracef(spew.Sdump(dumpChannels))
131+
132+
// Go also through all the HTLCs and calculate the sha of the onionblob.
133+
log.Debug("===========================================================")
134+
local := dbChannel.LocalCommitment
135+
remote := dbChannel.RemoteCommitment
136+
log.Debugf("RemoteCommitment: height=%v", remote.CommitHeight)
137+
log.Debugf("LocalCommitment: height=%v", local.CommitHeight)
138+
139+
remoteHtlcs := make(map[[32]byte]struct{})
140+
for _, htlc := range remote.Htlcs {
141+
log.Debugf("RemoteCommitment has htlc: id=%v, update=%v "+
142+
"incoming=%v", htlc.HtlcIndex, htlc.LogIndex,
143+
htlc.Incoming)
144+
145+
onionHash := sha256.Sum256(htlc.OnionBlob[:])
146+
remoteHtlcs[onionHash] = struct{}{}
147+
148+
}
149+
150+
// as active if *we* know them as well.
151+
activeHtlcs := make([]channeldb.HTLC, 0, len(remoteHtlcs))
152+
for _, htlc := range local.Htlcs {
153+
log.Debugf("LocalCommitment has htlc: id=%v, update=%v "+
154+
"incoming=%v", htlc.HtlcIndex, htlc.LogIndex,
155+
htlc.Incoming)
156+
157+
onionHash := sha256.Sum256(htlc.OnionBlob[:])
158+
if _, ok := remoteHtlcs[onionHash]; !ok {
159+
log.Debugf("Skipped htlc due to onion has not "+
160+
"matched: id=%v, update=%v incoming=%v",
161+
htlc.HtlcIndex, htlc.LogIndex, htlc.Incoming)
162+
163+
continue
164+
}
165+
activeHtlcs = append(activeHtlcs, htlc)
166+
}
167+
168+
spew.Dump(activeHtlcs)
169+
170+
return nil
171+
}

cmd/chantools/root.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ func main() {
128128
newVanityGenCommand(),
129129
newWalletInfoCommand(),
130130
newZombieRecoveryCommand(),
131+
dumphistoricalchan(),
131132
)
132133

133134
if err := rootCmd.Execute(); err != nil {

0 commit comments

Comments
 (0)