mirror of https://github.com/guggero/chantools
Extract SummarizeChannels into library function
parent
56edf2d850
commit
532a8c1902
@ -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
|
||||
}
|
Loading…
Reference in New Issue