|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/guggero/chantools/btc"
|
|
|
|
"github.com/guggero/chantools/dataformat"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
)
|
|
|
|
|
|
|
|
type summaryCommand struct {
|
|
|
|
APIURL string
|
|
|
|
|
|
|
|
inputs *inputFlags
|
|
|
|
cmd *cobra.Command
|
|
|
|
}
|
|
|
|
|
|
|
|
func newSummaryCommand() *cobra.Command {
|
|
|
|
cc := &summaryCommand{}
|
|
|
|
cc.cmd = &cobra.Command{
|
|
|
|
Use: "summary",
|
|
|
|
Short: "Compile a summary about the current state of " +
|
|
|
|
"channels",
|
|
|
|
Long: `From a list of channels, find out what their state is by
|
|
|
|
querying the funding transaction on a block explorer API.`,
|
|
|
|
Example: `lncli listchannels | chantools summary --listchannels -
|
|
|
|
|
|
|
|
chantools summary --fromchanneldb ~/.lnd/data/graph/mainnet/channel.db`,
|
|
|
|
RunE: cc.Execute,
|
|
|
|
}
|
|
|
|
cc.cmd.Flags().StringVar(
|
|
|
|
&cc.APIURL, "apiurl", defaultAPIURL, "API URL to use (must "+
|
|
|
|
"be esplora compatible)",
|
|
|
|
)
|
|
|
|
|
|
|
|
cc.inputs = newInputFlags(cc.cmd)
|
|
|
|
|
|
|
|
return cc.cmd
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *summaryCommand) Execute(_ *cobra.Command, _ []string) error {
|
|
|
|
// Parse channel entries from any of the possible input files.
|
|
|
|
entries, err := c.inputs.parseInputType()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return summarizeChannels(c.APIURL, entries)
|
|
|
|
}
|
|
|
|
|
|
|
|
func summarizeChannels(apiURL string,
|
|
|
|
channels []*dataformat.SummaryEntry) error {
|
|
|
|
|
|
|
|
summaryFile, err := btc.SummarizeChannels(apiURL, channels, log)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error running summary: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Info("Finished scanning.")
|
|
|
|
log.Infof("Open channels: %d", summaryFile.OpenChannels)
|
|
|
|
log.Infof("Sats in open channels: %d", summaryFile.FundsOpenChannels)
|
|
|
|
log.Infof("Closed channels: %d", summaryFile.ClosedChannels)
|
|
|
|
log.Infof(" --> force closed channels: %d",
|
|
|
|
summaryFile.ForceClosedChannels)
|
|
|
|
log.Infof(" --> coop closed channels: %d",
|
|
|
|
summaryFile.CoopClosedChannels)
|
|
|
|
log.Infof(" --> closed channels with all outputs spent: %d",
|
|
|
|
summaryFile.FullySpentChannels)
|
|
|
|
log.Infof(" --> closed channels with unspent outputs: %d",
|
|
|
|
summaryFile.ChannelsWithUnspent)
|
|
|
|
log.Infof(" --> closed channels with potentially our outputs: %d",
|
|
|
|
summaryFile.ChannelsWithPotential)
|
|
|
|
log.Infof("Sats in closed channels: %d", summaryFile.FundsClosedChannels)
|
|
|
|
log.Infof(" --> closed channel sats that have been swept/spent: %d",
|
|
|
|
summaryFile.FundsClosedSpent)
|
|
|
|
log.Infof(" --> closed channel sats that are in force-close outputs: %d",
|
|
|
|
summaryFile.FundsForceClose)
|
|
|
|
log.Infof(" --> closed channel sats that are in coop close outputs: %d",
|
|
|
|
summaryFile.FundsCoopClose)
|
|
|
|
|
|
|
|
summaryBytes, err := json.MarshalIndent(summaryFile, "", " ")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
fileName := fmt.Sprintf("results/summary-%s.json",
|
|
|
|
time.Now().Format("2006-01-02-15-04-05"))
|
|
|
|
log.Infof("Writing result to %s", fileName)
|
|
|
|
return ioutil.WriteFile(fileName, summaryBytes, 0644)
|
|
|
|
}
|