Skip to content

Commit a2b4f4d

Browse files
committed
add dumphtlcsummary command.
1 parent b0097ad commit a2b4f4d

File tree

2 files changed

+167
-0
lines changed

2 files changed

+167
-0
lines changed

cmd/chantools/root.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ func main() {
119119
newVanityGenCommand(),
120120
newWalletInfoCommand(),
121121
newZombieRecoveryCommand(),
122+
dumphtlcsummary(),
122123
)
123124

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

cmd/chantools/summaryhtlc.go

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
package main
2+
3+
import (
4+
"crypto/sha256"
5+
"encoding/hex"
6+
"encoding/json"
7+
"errors"
8+
"fmt"
9+
"strconv"
10+
"strings"
11+
12+
"github.com/btcsuite/btcd/btcutil"
13+
"github.com/btcsuite/btcd/chaincfg/chainhash"
14+
"github.com/btcsuite/btcd/wire"
15+
"github.com/lightninglabs/chantools/lnd"
16+
"github.com/lightningnetwork/lnd/channeldb"
17+
"github.com/lightningnetwork/lnd/input"
18+
"github.com/lightningnetwork/lnd/lnrpc"
19+
"github.com/lightningnetwork/lnd/lnwallet"
20+
"github.com/spf13/cobra"
21+
)
22+
23+
type summaryHTLC struct {
24+
ChannelDB string
25+
ChannelPoint string
26+
27+
rootKey *rootKey
28+
cmd *cobra.Command
29+
}
30+
31+
var errBadChanPoint = errors.New("expecting chan_point to be in format of: " +
32+
"txid:index")
33+
34+
func parseChanPoint(s string) (*lnrpc.ChannelPoint, error) {
35+
split := strings.Split(s, ":")
36+
if len(split) != 2 || len(split[0]) == 0 || len(split[1]) == 0 {
37+
return nil, errBadChanPoint
38+
}
39+
40+
index, err := strconv.ParseInt(split[1], 10, 64)
41+
if err != nil {
42+
return nil, fmt.Errorf("unable to decode output index: %v", err)
43+
}
44+
45+
txid, err := chainhash.NewHashFromStr(split[0])
46+
if err != nil {
47+
return nil, fmt.Errorf("unable to parse hex string: %v", err)
48+
}
49+
50+
return &lnrpc.ChannelPoint{
51+
FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{
52+
FundingTxidBytes: txid[:],
53+
},
54+
OutputIndex: uint32(index),
55+
}, nil
56+
}
57+
58+
func dumphtlcsummary() *cobra.Command {
59+
cc := &summaryHTLC{}
60+
cc.cmd = &cobra.Command{
61+
Use: "dumphtlcsummary",
62+
Short: "dump all the necessary htlc information which are " +
63+
"needed for the peer to recover his funds",
64+
Long: `...`,
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, "ChannelPoint", "", "",
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 dumpHtlcInfos(db.ChannelStateDB(), c.ChannelPoint)
94+
}
95+
96+
type hltcInfo struct {
97+
HtlcAddress string
98+
WitnessScript string
99+
CommitPoint string
100+
}
101+
102+
func dumpHtlcInfos(chanDb *channeldb.ChannelStateDB, channel string) error {
103+
104+
var htlcs []hltcInfo
105+
106+
chanPoint, err := parseChanPoint(channel)
107+
if err != nil {
108+
return err
109+
}
110+
111+
//Open the Historical Bucket
112+
fundingHash, err := chainhash.NewHash(chanPoint.GetFundingTxidBytes())
113+
114+
if err != nil {
115+
return err
116+
}
117+
outPoint := wire.NewOutPoint(fundingHash, chanPoint.OutputIndex)
118+
119+
dbChannel, err := chanDb.FetchHistoricalChannel(outPoint)
120+
if err != nil {
121+
return err
122+
}
123+
124+
for _, htlc := range dbChannel.LocalCommitment.Htlcs {
125+
// Only Incoming HTLCs for now.
126+
if !htlc.Incoming {
127+
continue
128+
}
129+
130+
revocationPreimage, err := dbChannel.RevocationProducer.AtIndex(
131+
dbChannel.LocalCommitment.CommitHeight,
132+
)
133+
if err != nil {
134+
return err
135+
}
136+
localCommitPoint := input.ComputeCommitmentPoint(revocationPreimage[:])
137+
138+
keyRing := lnwallet.DeriveCommitmentKeys(
139+
localCommitPoint, true, dbChannel.ChanType,
140+
&dbChannel.LocalChanCfg,
141+
&dbChannel.RemoteChanCfg,
142+
)
143+
144+
witnessScript, err := input.ReceiverHTLCScript(
145+
htlc.RefundTimeout, keyRing.RemoteHtlcKey, keyRing.LocalHtlcKey,
146+
keyRing.RevocationKey, htlc.RHash[:], dbChannel.ChanType.HasAnchors(),
147+
)
148+
witnessScriptHash := sha256.Sum256(witnessScript)
149+
htlcAddr, err := btcutil.NewAddressWitnessScriptHash(
150+
witnessScriptHash[:], chainParams,
151+
)
152+
153+
htlcs = append(htlcs, hltcInfo{
154+
HtlcAddress: htlcAddr.String(),
155+
WitnessScript: hex.EncodeToString(witnessScript),
156+
CommitPoint: hex.EncodeToString(localCommitPoint.SerializeCompressed()),
157+
})
158+
}
159+
160+
data, err := json.MarshalIndent(htlcs, "", " ")
161+
162+
fmt.Printf("%v", string(data))
163+
164+
return nil
165+
166+
}

0 commit comments

Comments
 (0)