diff --git a/cmd_derivekey.go b/cmd_derivekey.go index 2aab76d..86af459 100644 --- a/cmd_derivekey.go +++ b/cmd_derivekey.go @@ -2,6 +2,7 @@ package chantools import ( "fmt" + "github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil/hdkeychain" ) diff --git a/cmd_forceclose.go b/cmd_forceclose.go index fddccca..b3f140e 100644 --- a/cmd_forceclose.go +++ b/cmd_forceclose.go @@ -13,12 +13,14 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil/hdkeychain" "github.com/guggero/chantools/chain" + "github.com/guggero/chantools/dataformat" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/input" ) func forceCloseChannels(extendedKey *hdkeychain.ExtendedKey, - entries []*SummaryEntry, chanDb *channeldb.DB, publish bool) error { + entries []*dataformat.SummaryEntry, chanDb *channeldb.DB, + publish bool) error { channels, err := chanDb.FetchAllChannels() if err != nil { @@ -31,7 +33,7 @@ func forceCloseChannels(extendedKey *hdkeychain.ExtendedKey, // publish their local commitment TX. for _, channel := range channels { channelPoint := channel.FundingOutpoint.String() - var channelEntry *SummaryEntry + var channelEntry *dataformat.SummaryEntry for _, entry := range entries { if entry.ChannelPoint == channelPoint { channelEntry = entry @@ -90,17 +92,17 @@ func forceCloseChannels(extendedKey *hdkeychain.ExtendedKey, // Store all information that we collected into the channel // entry file so we don't need to use the channel.db file for // the next step. - channelEntry.ForceClose = &ForceClose{ + channelEntry.ForceClose = &dataformat.ForceClose{ TXID: hash.String(), Serialized: serialized, - DelayBasePoint: &BasePoint{ + DelayBasePoint: &dataformat.BasePoint{ Family: uint16(basepoint.Family), Index: basepoint.Index, PubKey: hex.EncodeToString( basepoint.PubKey.SerializeCompressed(), ), }, - RevocationBasePoint: &BasePoint{ + RevocationBasePoint: &dataformat.BasePoint{ PubKey: hex.EncodeToString( revpoint.PubKey.SerializeCompressed(), ), @@ -108,7 +110,9 @@ func forceCloseChannels(extendedKey *hdkeychain.ExtendedKey, CommitPoint: hex.EncodeToString( point.SerializeCompressed(), ), - Outs: make([]*Out, len(localCommitTx.TxOut)), + Outs: make( + []*dataformat.Out, len(localCommitTx.TxOut), + ), CSVDelay: channel.LocalChanCfg.CsvDelay, } for idx, out := range localCommitTx.TxOut { @@ -116,7 +120,7 @@ func forceCloseChannels(extendedKey *hdkeychain.ExtendedKey, if err != nil { return err } - channelEntry.ForceClose.Outs[idx] = &Out{ + channelEntry.ForceClose.Outs[idx] = &dataformat.Out{ Script: hex.EncodeToString(out.PkScript), ScriptAsm: script, Value: uint64(out.Value), @@ -134,7 +138,7 @@ func forceCloseChannels(extendedKey *hdkeychain.ExtendedKey, } } - summaryBytes, err := json.MarshalIndent(&SummaryEntryFile{ + summaryBytes, err := json.MarshalIndent(&dataformat.SummaryEntryFile{ Channels: entries, }, "", " ") if err != nil { diff --git a/cmd_rescueclosed.go b/cmd_rescueclosed.go index 017119f..ffba586 100644 --- a/cmd_rescueclosed.go +++ b/cmd_rescueclosed.go @@ -11,6 +11,7 @@ import ( "github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil/hdkeychain" + "github.com/guggero/chantools/dataformat" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/input" "github.com/lightningnetwork/lnd/keychain" @@ -29,7 +30,7 @@ type cacheEntry struct { } func rescueClosedChannels(extendedKey *hdkeychain.ExtendedKey, - entries []*SummaryEntry, chanDb *channeldb.DB) error { + entries []*dataformat.SummaryEntry, chanDb *channeldb.DB) error { err := fillCache(extendedKey) if err != nil { @@ -44,7 +45,7 @@ func rescueClosedChannels(extendedKey *hdkeychain.ExtendedKey, // Try naive/lucky guess with information from channel DB. for _, channel := range channels { channelPoint := channel.FundingOutpoint.String() - var channelEntry *SummaryEntry + var channelEntry *dataformat.SummaryEntry for _, entry := range entries { if entry.ChannelPoint == channelPoint { channelEntry = entry @@ -93,7 +94,7 @@ func rescueClosedChannels(extendedKey *hdkeychain.ExtendedKey, } } - summaryBytes, err := json.MarshalIndent(&SummaryEntryFile{ + summaryBytes, err := json.MarshalIndent(&dataformat.SummaryEntryFile{ Channels: entries, }, "", " ") if err != nil { diff --git a/cmd_summary.go b/cmd_summary.go index 3b671cc..ce7922d 100644 --- a/cmd_summary.go +++ b/cmd_summary.go @@ -7,10 +7,13 @@ import ( "time" "github.com/guggero/chantools/chain" + "github.com/guggero/chantools/dataformat" ) -func summarizeChannels(apiUrl string, channels []*SummaryEntry) error { - summaryFile := &SummaryEntryFile{ +func summarizeChannels(apiUrl string, + channels []*dataformat.SummaryEntry) error { + + summaryFile := &dataformat.SummaryEntryFile{ Channels: channels, } chainApi := &chain.Api{BaseUrl: apiUrl} @@ -32,7 +35,7 @@ func summarizeChannels(apiUrl string, channels []*SummaryEntry) error { outspend := tx.Vout[channel.FundingTXIndex].Outspend if outspend.Spent { summaryFile.ClosedChannels++ - channel.ClosingTX = &ClosingTX{ + channel.ClosingTX = &dataformat.ClosingTX{ TXID: outspend.Txid, ConfHeight: uint32(outspend.Status.BlockHeight), } @@ -90,8 +93,8 @@ func summarizeChannels(apiUrl string, channels []*SummaryEntry) error { return ioutil.WriteFile(fileName, summaryBytes, 0644) } -func reportOutspend(api *chain.Api, summaryFile *SummaryEntryFile, - entry *SummaryEntry, os *chain.Outspend) error { +func reportOutspend(api *chain.Api, summaryFile *dataformat.SummaryEntryFile, + entry *dataformat.SummaryEntry, os *chain.Outspend) error { spendTx, err := api.Transaction(os.Txid) if err != nil { @@ -171,7 +174,7 @@ func reportOutspend(api *chain.Api, summaryFile *SummaryEntryFile, return nil } -func couldBeOurs(entry *SummaryEntry, utxo []*chain.Vout) bool { +func couldBeOurs(entry *dataformat.SummaryEntry, utxo []*chain.Vout) bool { if len(utxo) == 1 && utxo[0].Value == entry.RemoteBalance { return false } diff --git a/cmd_sweeptimelock.go b/cmd_sweeptimelock.go index d29ca7e..192641b 100644 --- a/cmd_sweeptimelock.go +++ b/cmd_sweeptimelock.go @@ -11,6 +11,7 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil/hdkeychain" "github.com/guggero/chantools/chain" + "github.com/guggero/chantools/dataformat" "github.com/lightningnetwork/lnd/input" ) @@ -19,7 +20,7 @@ const ( ) func sweepTimeLock(extendedKey *hdkeychain.ExtendedKey, apiUrl string, - entries []*SummaryEntry, sweepAddr string, maxCsvTimeout int, + entries []*dataformat.SummaryEntry, sweepAddr string, maxCsvTimeout int, publish bool) error { // Create signer and transaction template. @@ -76,7 +77,7 @@ func sweepTimeLock(extendedKey *hdkeychain.ExtendedKey, apiUrl string, if err != nil { return fmt.Errorf("error parsing commit point: %v", err) } - delayDesc := fc.DelayBasePoint.toDesc() + delayDesc := fc.DelayBasePoint.Desc() delayPrivKey, err := signer.fetchPrivKey(delayDesc) if err != nil { return fmt.Errorf("error getting private key: %v", err) diff --git a/input.go b/dataformat/input.go similarity index 67% rename from input.go rename to dataformat/input.go index 3f15c9d..262f6dc 100644 --- a/input.go +++ b/dataformat/input.go @@ -1,13 +1,10 @@ -package chantools +package dataformat import ( - "bytes" "encoding/hex" "encoding/json" "fmt" "github.com/lightningnetwork/lnd/channeldb" - "io/ioutil" - "os" "strconv" "strings" ) @@ -38,62 +35,11 @@ type Input interface { AsSummaryEntry() *SummaryEntry } -func ParseInput(cfg *config) ([]*SummaryEntry, error) { - var ( - content []byte - err error - target InputFile - ) - - switch { - case cfg.ListChannels != "": - content, err = readInput(cfg.ListChannels) - target = &listChannelsFile{} - - case cfg.PendingChannels != "": - content, err = readInput(cfg.PendingChannels) - target = &pendingChannelsFile{} - - case cfg.FromSummary != "": - content, err = readInput(cfg.FromSummary) - target = &SummaryEntryFile{} - - case cfg.FromChannelDB != "": - db, err := channeldb.Open(cfg.FromChannelDB) - if err != nil { - return nil, fmt.Errorf("error opening channel DB: %v", - err) - } - target = &channelDBFile{db: db} - return target.AsSummaryEntries() - - default: - return nil, fmt.Errorf("an input file must be specified") - } - - if err != nil { - return nil, err - } - decoder := json.NewDecoder(bytes.NewReader(content)) - err = decoder.Decode(&target) - if err != nil { - return nil, err - } - return target.AsSummaryEntries() -} - -func readInput(input string) ([]byte, error) { - if strings.TrimSpace(input) == "-" { - return ioutil.ReadAll(os.Stdin) - } - return ioutil.ReadFile(input) -} - -type listChannelsFile struct { - Channels []*listChannelsChannel `json:"channels"` +type ListChannelsFile struct { + Channels []*ListChannelsChannel `json:"channels"` } -func (f *listChannelsFile) AsSummaryEntries() ([]*SummaryEntry, error) { +func (f *ListChannelsFile) AsSummaryEntries() ([]*SummaryEntry, error) { result := make([]*SummaryEntry, len(f.Channels)) for idx, entry := range f.Channels { result[idx] = entry.AsSummaryEntry() @@ -101,7 +47,7 @@ func (f *listChannelsFile) AsSummaryEntries() ([]*SummaryEntry, error) { return result, nil } -type listChannelsChannel struct { +type ListChannelsChannel struct { RemotePubkey string `json:"remote_pubkey"` ChannelPoint string `json:"channel_point"` Capacity NumberString `json:"capacity"` @@ -110,7 +56,7 @@ type listChannelsChannel struct { RemoteBalance NumberString `json:"remote_balance"` } -func (c *listChannelsChannel) AsSummaryEntry() *SummaryEntry { +func (c *ListChannelsChannel) AsSummaryEntry() *SummaryEntry { return &SummaryEntry{ RemotePubkey: c.RemotePubkey, ChannelPoint: c.ChannelPoint, @@ -123,14 +69,14 @@ func (c *listChannelsChannel) AsSummaryEntry() *SummaryEntry { } } -type pendingChannelsFile struct { - PendingOpen []*pendingChannelsChannel `json:"pending_open_channels"` - PendingClosing []*pendingChannelsChannel `json:"pending_closing_channels"` - PendingForceClosing []*pendingChannelsChannel `json:"pending_force_closing_channels"` - WaitingClose []*pendingChannelsChannel `json:"waiting_close_channels"` +type PendingChannelsFile struct { + PendingOpen []*PendingChannelsChannel `json:"pending_open_channels"` + PendingClosing []*PendingChannelsChannel `json:"pending_closing_channels"` + PendingForceClosing []*PendingChannelsChannel `json:"pending_force_closing_channels"` + WaitingClose []*PendingChannelsChannel `json:"waiting_close_channels"` } -func (f *pendingChannelsFile) AsSummaryEntries() ([]*SummaryEntry, error) { +func (f *PendingChannelsFile) AsSummaryEntries() ([]*SummaryEntry, error) { numChannels := len(f.PendingOpen) + len(f.PendingClosing) + len(f.PendingForceClosing) + len(f.WaitingClose) result := make([]*SummaryEntry, numChannels) @@ -154,7 +100,7 @@ func (f *pendingChannelsFile) AsSummaryEntries() ([]*SummaryEntry, error) { return result, nil } -type pendingChannelsChannel struct { +type PendingChannelsChannel struct { Channel struct { RemotePubkey string `json:"remote_node_pub"` ChannelPoint string `json:"channel_point"` @@ -164,7 +110,7 @@ type pendingChannelsChannel struct { } `json:"channel"` } -func (c *pendingChannelsChannel) AsSummaryEntry() *SummaryEntry { +func (c *PendingChannelsChannel) AsSummaryEntry() *SummaryEntry { return &SummaryEntry{ RemotePubkey: c.Channel.RemotePubkey, ChannelPoint: c.Channel.ChannelPoint, @@ -177,12 +123,12 @@ func (c *pendingChannelsChannel) AsSummaryEntry() *SummaryEntry { } } -type channelDBFile struct { - db *channeldb.DB +type ChannelDBFile struct { + DB *channeldb.DB } -func (c *channelDBFile) AsSummaryEntries() ([]*SummaryEntry, error) { - channels, err := c.db.FetchAllChannels() +func (c *ChannelDBFile) AsSummaryEntries() ([]*SummaryEntry, error) { + channels, err := c.DB.FetchAllChannels() if err != nil { return nil, fmt.Errorf("error fetching channels: %v", err) } diff --git a/entry.go b/dataformat/summary.go similarity index 97% rename from entry.go rename to dataformat/summary.go index 437c45f..4891ba8 100644 --- a/entry.go +++ b/dataformat/summary.go @@ -1,4 +1,4 @@ -package chantools +package dataformat import "github.com/lightningnetwork/lnd/keychain" @@ -17,7 +17,7 @@ type BasePoint struct { PubKey string `json:"pubkey"` } -func (b *BasePoint) toDesc() *keychain.KeyDescriptor { +func (b *BasePoint) Desc() *keychain.KeyDescriptor { return &keychain.KeyDescriptor{ KeyLocator: keychain.KeyLocator{ Family: keychain.KeyFamily(b.Family), diff --git a/main.go b/main.go index fc1e4dc..d87e17e 100644 --- a/main.go +++ b/main.go @@ -2,7 +2,10 @@ package chantools import ( "bufio" + "bytes" + "encoding/json" "fmt" + "io/ioutil" "os" "path" "strings" @@ -10,6 +13,7 @@ import ( "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcutil/hdkeychain" + "github.com/guggero/chantools/dataformat" "github.com/jessevdk/go-flags" "github.com/lightningnetwork/lnd/aezeed" "github.com/lightningnetwork/lnd/build" @@ -318,6 +322,57 @@ func (c *deriveKeyCommand) Execute(_ []string) error { return deriveKey(extendedKey, c.Path, c.Neuter) } +func ParseInput(cfg *config) ([]*dataformat.SummaryEntry, error) { + var ( + content []byte + err error + target dataformat.InputFile + ) + + switch { + case cfg.ListChannels != "": + content, err = readInput(cfg.ListChannels) + target = &dataformat.ListChannelsFile{} + + case cfg.PendingChannels != "": + content, err = readInput(cfg.PendingChannels) + target = &dataformat.PendingChannelsFile{} + + case cfg.FromSummary != "": + content, err = readInput(cfg.FromSummary) + target = &dataformat.SummaryEntryFile{} + + case cfg.FromChannelDB != "": + db, err := channeldb.Open(cfg.FromChannelDB) + if err != nil { + return nil, fmt.Errorf("error opening channel DB: %v", + err) + } + target = &dataformat.ChannelDBFile{DB: db} + return target.AsSummaryEntries() + + default: + return nil, fmt.Errorf("an input file must be specified") + } + + if err != nil { + return nil, err + } + decoder := json.NewDecoder(bytes.NewReader(content)) + err = decoder.Decode(&target) + if err != nil { + return nil, err + } + return target.AsSummaryEntries() +} + +func readInput(input string) ([]byte, error) { + if strings.TrimSpace(input) == "-" { + return ioutil.ReadAll(os.Stdin) + } + return ioutil.ReadFile(input) +} + func setupChainParams(cfg *config) { if cfg.Testnet { chainParams = &chaincfg.TestNet3Params