Extract SummarizeChannels into library function

pull/17/head
Oliver Gugger 4 years ago
parent 56edf2d850
commit 532a8c1902
No known key found for this signature in database
GPG Key ID: 8E4256593F177720

@ -0,0 +1,154 @@
package btc
import (
"github.com/btcsuite/btclog"
"github.com/guggero/chantools/dataformat"
)
func SummarizeChannels(apiURL string, channels []*dataformat.SummaryEntry,
log btclog.Logger) (*dataformat.SummaryEntryFile, error) {
summaryFile := &dataformat.SummaryEntryFile{
Channels: channels,
}
api := &ExplorerAPI{BaseURL: apiURL}
for idx, channel := range channels {
tx, err := api.Transaction(channel.FundingTXID)
if err == ErrTxNotFound {
log.Errorf("Funding TX %s not found. Ignoring.",
channel.FundingTXID)
channel.ChanExists = false
continue
}
if err != nil {
log.Errorf("Problem with channel %d (%s): %v.",
idx, channel.FundingTXID, err)
return nil, err
}
channel.ChanExists = true
outspend := tx.Vout[channel.FundingTXIndex].Outspend
if outspend.Spent {
summaryFile.ClosedChannels++
channel.ClosingTX = &dataformat.ClosingTX{
TXID: outspend.Txid,
ConfHeight: uint32(outspend.Status.BlockHeight),
}
err := reportOutspend(
api, summaryFile, channel, outspend, log,
)
if err != nil {
log.Errorf("Problem with channel %d (%s): %v.",
idx, channel.FundingTXID, err)
return nil, err
}
} else {
summaryFile.OpenChannels++
summaryFile.FundsOpenChannels += channel.LocalBalance
channel.ClosingTX = nil
channel.HasPotential = true
}
if idx%50 == 0 {
log.Infof("Queried channel %d of %d.", idx,
len(channels))
}
}
return summaryFile, nil
}
func reportOutspend(api *ExplorerAPI,
summaryFile *dataformat.SummaryEntryFile,
entry *dataformat.SummaryEntry, os *Outspend, log btclog.Logger) error {
spendTx, err := api.Transaction(os.Txid)
if err != nil {
return err
}
summaryFile.FundsClosedChannels += entry.LocalBalance
var utxo []*Vout
for _, vout := range spendTx.Vout {
if !vout.Outspend.Spent {
utxo = append(utxo, vout)
}
}
if isCoopClose(spendTx) {
summaryFile.CoopClosedChannels++
summaryFile.FundsCoopClose += entry.LocalBalance
entry.ClosingTX.ForceClose = false
entry.ClosingTX.AllOutsSpent = len(utxo) == 0
entry.HasPotential = entry.LocalBalance > 0 && len(utxo) != 0
return nil
}
summaryFile.ForceClosedChannels++
entry.ClosingTX.ForceClose = true
entry.HasPotential = false
if len(utxo) > 0 {
log.Debugf("Channel %s spent by %s:%d which has %d outputs of "+
"which %d are unspent.", entry.ChannelPoint, os.Txid,
os.Vin, len(spendTx.Vout), len(utxo))
entry.ClosingTX.AllOutsSpent = false
summaryFile.ChannelsWithUnspent++
if couldBeOurs(entry, utxo) {
summaryFile.ChannelsWithPotential++
summaryFile.FundsForceClose += utxo[0].Value
entry.HasPotential = true
// Could maybe be brute forced.
if len(utxo) == 1 &&
utxo[0].ScriptPubkeyType == "v0_p2wpkh" &&
!utxo[0].Outspend.Spent {
entry.ClosingTX.OurAddr = utxo[0].ScriptPubkeyAddr
}
} else {
// It's theirs, ignore.
if entry.LocalBalance == 0 ||
(len(utxo) == 1 &&
utxo[0].Value == entry.RemoteBalance) {
return nil
}
// We don't know what this output is, logging for debug.
for idx, vout := range spendTx.Vout {
if !vout.Outspend.Spent {
log.Debugf("UTXO %d of type %s with "+
"value %d", idx,
vout.ScriptPubkeyType,
vout.Value)
}
}
log.Debugf("Local balance: %d", entry.LocalBalance)
log.Debugf("Remote balance: %d", entry.RemoteBalance)
log.Debugf("Initiator: %v", entry.Initiator)
}
} else {
entry.ClosingTX.AllOutsSpent = true
entry.HasPotential = false
summaryFile.FundsClosedSpent += entry.LocalBalance
summaryFile.FullySpentChannels++
}
return nil
}
func couldBeOurs(entry *dataformat.SummaryEntry, utxo []*Vout) bool {
if len(utxo) == 1 && utxo[0].Value == entry.RemoteBalance {
return false
}
return entry.LocalBalance != 0
}
func isCoopClose(tx *TX) bool {
return tx.Vin[0].Sequence == 0xffffffff
}

@ -26,52 +26,9 @@ func (c *summaryCommand) Execute(_ []string) error {
func summarizeChannels(apiURL string,
channels []*dataformat.SummaryEntry) error {
summaryFile := &dataformat.SummaryEntryFile{
Channels: channels,
}
api := &btc.ExplorerAPI{BaseURL: apiURL}
for idx, channel := range channels {
tx, err := api.Transaction(channel.FundingTXID)
if err == btc.ErrTxNotFound {
log.Errorf("Funding TX %s not found. Ignoring.",
channel.FundingTXID)
channel.ChanExists = false
continue
}
if err != nil {
log.Errorf("Problem with channel %d (%s): %v.",
idx, channel.FundingTXID, err)
return err
}
channel.ChanExists = true
outspend := tx.Vout[channel.FundingTXIndex].Outspend
if outspend.Spent {
summaryFile.ClosedChannels++
channel.ClosingTX = &dataformat.ClosingTX{
TXID: outspend.Txid,
ConfHeight: uint32(outspend.Status.BlockHeight),
}
err := reportOutspend(
api, summaryFile, channel, outspend,
)
if err != nil {
log.Errorf("Problem with channel %d (%s): %v.",
idx, channel.FundingTXID, err)
return err
}
} else {
summaryFile.OpenChannels++
summaryFile.FundsOpenChannels += channel.LocalBalance
channel.ClosingTX = nil
channel.HasPotential = true
}
if idx%50 == 0 {
log.Infof("Queried channel %d of %d.", idx,
len(channels))
}
summaryFile, err := btc.SummarizeChannels(apiURL, channels, log)
if err != nil {
return fmt.Errorf("error running summary: %v", err)
}
log.Info("Finished scanning.")
@ -105,97 +62,3 @@ func summarizeChannels(apiURL string,
log.Infof("Writing result to %s", fileName)
return ioutil.WriteFile(fileName, summaryBytes, 0644)
}
func reportOutspend(api *btc.ExplorerAPI,
summaryFile *dataformat.SummaryEntryFile,
entry *dataformat.SummaryEntry, os *btc.Outspend) error {
spendTx, err := api.Transaction(os.Txid)
if err != nil {
return err
}
summaryFile.FundsClosedChannels += entry.LocalBalance
var utxo []*btc.Vout
for _, vout := range spendTx.Vout {
if !vout.Outspend.Spent {
utxo = append(utxo, vout)
}
}
if isCoopClose(spendTx) {
summaryFile.CoopClosedChannels++
summaryFile.FundsCoopClose += entry.LocalBalance
entry.ClosingTX.ForceClose = false
entry.ClosingTX.AllOutsSpent = len(utxo) == 0
entry.HasPotential = entry.LocalBalance > 0 && len(utxo) != 0
return nil
}
summaryFile.ForceClosedChannels++
entry.ClosingTX.ForceClose = true
entry.HasPotential = false
if len(utxo) > 0 {
log.Debugf("Channel %s spent by %s:%d which has %d outputs of "+
"which %d are unspent.", entry.ChannelPoint, os.Txid,
os.Vin, len(spendTx.Vout), len(utxo))
entry.ClosingTX.AllOutsSpent = false
summaryFile.ChannelsWithUnspent++
if couldBeOurs(entry, utxo) {
summaryFile.ChannelsWithPotential++
summaryFile.FundsForceClose += utxo[0].Value
entry.HasPotential = true
// Could maybe be brute forced.
if len(utxo) == 1 &&
utxo[0].ScriptPubkeyType == "v0_p2wpkh" &&
!utxo[0].Outspend.Spent {
entry.ClosingTX.OurAddr = utxo[0].ScriptPubkeyAddr
}
} else {
// It's theirs, ignore.
if entry.LocalBalance == 0 ||
(len(utxo) == 1 &&
utxo[0].Value == entry.RemoteBalance) {
return nil
}
// We don't know what this output is, logging for debug.
for idx, vout := range spendTx.Vout {
if !vout.Outspend.Spent {
log.Debugf("UTXO %d of type %s with "+
"value %d", idx,
vout.ScriptPubkeyType,
vout.Value)
}
}
log.Debugf("Local balance: %d", entry.LocalBalance)
log.Debugf("Remote balance: %d", entry.RemoteBalance)
log.Debugf("Initiator: %v", entry.Initiator)
}
} else {
entry.ClosingTX.AllOutsSpent = true
entry.HasPotential = false
summaryFile.FundsClosedSpent += entry.LocalBalance
summaryFile.FullySpentChannels++
}
return nil
}
func couldBeOurs(entry *dataformat.SummaryEntry, utxo []*btc.Vout) bool {
if len(utxo) == 1 && utxo[0].Value == entry.RemoteBalance {
return false
}
return entry.LocalBalance != 0
}
func isCoopClose(tx *btc.TX) bool {
return tx.Vin[0].Sequence == 0xffffffff
}

Loading…
Cancel
Save