breacharbiter: fix revoked funds calculation

Since we also must count revoked funds swept from second level revoked
outputs, we move the funds counting into the updateBreachInfo method,
where we already are checking whether the spend is by us or the remote.

We also clean up the logs a bit, to log the incremental sweep of funds
that now can happen.
This commit is contained in:
Johan T. Halseth 2021-04-23 10:44:49 +02:00
parent db0ec12412
commit 02268b8912
No known key found for this signature in database
GPG Key ID: 15BAADA29DA20D26

@ -468,11 +468,15 @@ func (b *breachArbiter) waitForSpendEvent(breachInfo *retributionInfo,
} }
// updateBreachInfo mutates the passed breachInfo by removing or converting any // updateBreachInfo mutates the passed breachInfo by removing or converting any
// outputs among the spends. // outputs among the spends. It also counts the total and revoked funds swept
func updateBreachInfo(breachInfo *retributionInfo, spends []spend) { // by our justice spends.
func updateBreachInfo(breachInfo *retributionInfo, spends []spend) (
btcutil.Amount, btcutil.Amount) {
inputs := breachInfo.breachedOutputs inputs := breachInfo.breachedOutputs
doneOutputs := make(map[int]struct{}) doneOutputs := make(map[int]struct{})
var totalFunds, revokedFunds btcutil.Amount
for _, s := range spends { for _, s := range spends {
breachedOutput := &inputs[s.index] breachedOutput := &inputs[s.index]
txIn := s.detail.SpendingTx.TxIn[s.detail.SpenderInputIndex] txIn := s.detail.SpendingTx.TxIn[s.detail.SpenderInputIndex]
@ -516,6 +520,22 @@ func updateBreachInfo(breachInfo *retributionInfo, spends []spend) {
continue continue
} }
// Now that we have determined the spend is done by us, we
// count the total and revoked funds swept depending on the
// input type.
switch breachedOutput.witnessType {
// If the output being revoked is the remote commitment
// output or an offered HTLC output, it's amount
// contributes to the value of funds being revoked from
// the counter party.
case input.CommitmentRevoke, input.HtlcSecondLevelRevoke,
input.HtlcOfferedRevoke:
revokedFunds += breachedOutput.Amount()
}
totalFunds += breachedOutput.Amount()
brarLog.Infof("Spend on %s(%v) for ChannelPoint(%v) "+ brarLog.Infof("Spend on %s(%v) for ChannelPoint(%v) "+
"transitions output to terminal state, "+ "transitions output to terminal state, "+
"removing input from justice transaction", "removing input from justice transaction",
@ -539,6 +559,7 @@ func updateBreachInfo(breachInfo *retributionInfo, spends []spend) {
// Update our remaining set of outputs before continuing with // Update our remaining set of outputs before continuing with
// another attempt at publication. // another attempt at publication.
breachInfo.breachedOutputs = inputs[:nextIndex] breachInfo.breachedOutputs = inputs[:nextIndex]
return totalFunds, revokedFunds
} }
// exactRetribution is a goroutine which is executed once a contract breach has // exactRetribution is a goroutine which is executed once a contract breach has
@ -639,26 +660,24 @@ Loop:
select { select {
case spends := <-spendChan: case spends := <-spendChan:
// Print the funds swept by the txs. // Update the breach info with the new spends.
for _, s := range spends { t, r := updateBreachInfo(breachInfo, spends)
tx := s.detail.SpendingTx
t, r := countRevokedFunds(breachInfo, tx)
totalFunds += t totalFunds += t
revokedFunds += r revokedFunds += r
}
brarLog.Infof("Justice for ChannelPoint(%v) has "+ brarLog.Infof("%v spends from breach tx for "+
"been served, %v revoked funds (%v total) "+ "ChannelPoint(%v) has been detected, %v "+
"have been claimed", breachInfo.chanPoint, "revoked funds (%v total) have been claimed",
len(spends), breachInfo.chanPoint,
revokedFunds, totalFunds) revokedFunds, totalFunds)
// Update the breach info with the new spends.
updateBreachInfo(breachInfo, spends)
if len(breachInfo.breachedOutputs) == 0 { if len(breachInfo.breachedOutputs) == 0 {
brarLog.Debugf("No more outputs to sweep for "+ brarLog.Infof("Justice for ChannelPoint(%v) "+
"breach, marking ChannelPoint(%v) "+ "has been served, %v revoked funds "+
"fully resolved", breachInfo.chanPoint) "(%v total) have been claimed. No "+
"more outputs to sweep, marking fully "+
"resolved", breachInfo.chanPoint,
revokedFunds, totalFunds)
err = b.cleanupBreach(&breachInfo.chanPoint) err = b.cleanupBreach(&breachInfo.chanPoint)
if err != nil { if err != nil {
@ -669,8 +688,8 @@ Loop:
// TODO(roasbeef): add peer to blacklist? // TODO(roasbeef): add peer to blacklist?
// TODO(roasbeef): close other active channels with offending // TODO(roasbeef): close other active channels
// peer // with offending peer
break Loop break Loop
} }
@ -763,46 +782,6 @@ Loop:
wg.Wait() wg.Wait()
} }
// countRevokedFunds counts the total and revoked funds swept by our justice
// TX.
func countRevokedFunds(breachInfo *retributionInfo,
spendTx *wire.MsgTx) (btcutil.Amount, btcutil.Amount) {
// Compute both the total value of funds being swept and the
// amount of funds that were revoked from the counter party.
var totalFunds, revokedFunds btcutil.Amount
for _, txIn := range spendTx.TxIn {
op := txIn.PreviousOutPoint
// Find the corresponding output in our retribution info.
for _, inp := range breachInfo.breachedOutputs {
// If the spent outpoint is not among the ouputs that
// were breached, we can ignore it.
if inp.outpoint != op {
continue
}
totalFunds += inp.Amount()
// If the output being revoked is the remote commitment
// output or an offered HTLC output, it's amount
// contributes to the value of funds being revoked from
// the counter party.
switch inp.WitnessType() {
case input.CommitmentRevoke:
revokedFunds += inp.Amount()
case input.HtlcOfferedRevoke:
revokedFunds += inp.Amount()
default:
}
break
}
}
return totalFunds, revokedFunds
}
// cleanupBreach marks the given channel point as fully resolved and removes the // cleanupBreach marks the given channel point as fully resolved and removes the
// retribution for that the channel from the retribution store. // retribution for that the channel from the retribution store.
func (b *breachArbiter) cleanupBreach(chanPoint *wire.OutPoint) error { func (b *breachArbiter) cleanupBreach(chanPoint *wire.OutPoint) error {