breacharbiter: sweep commitment output in case of unilateral close

This commit adds a new responsibility to the breach arbiter: the
service is now responsible for sweeping the commitment outputs to-self,
in the case of a unilateral commitment broadcast by the remote party.
In this new commitment design, this output won’t be immediately
recognized by the wallet due to using a tweaked public key. As a
result, we need to sweep this output into the wallet manually.
This commit is contained in:
Olaoluwa Osuntokun 2017-07-30 17:45:39 -07:00
parent 8eadd09403
commit 563fac84cc
No known key found for this signature in database
GPG Key ID: 9CC5B105D03521A2

@ -1,6 +1,7 @@
package main
import (
"fmt"
"sync"
"sync/atomic"
@ -173,6 +174,9 @@ func (b *breachArbiter) Start() error {
brarLog.Infof("ChannelPoint(%v) is fully closed, "+
"at height: %v", chanPoint, confInfo.BlockHeight)
// TODO(roasbeef): need to store UnilateralCloseSummary
// on disk so can possibly sweep output here
if err := b.db.MarkChanFullyClosed(chanPoint); err != nil {
brarLog.Errorf("unable to mark chan as closed: %v", err)
}
@ -460,7 +464,32 @@ func (b *breachArbiter) breachObserver(contract *lnwallet.LightningChannel,
// outbound HTLC's in flight
go waitForChanToClose(uint32(closeInfo.SpendingHeight), b.notifier,
nil, chanPoint, closeInfo.SpenderTxHash, func() {
// As we just detected a channel was closed via
// a unilateral commitment broadcast by the
// remote party, we'll need to sweep our main
// commitment output, and any outstanding
// outgoing HTLC we had as well.
//
// TODO(roasbeef): actually sweep HTLC's *
// ensure reliable confirmation
if closeInfo.SelfOutPoint != nil {
sweepTx, err := b.craftCommitSweepTx(
closeInfo,
)
if err != nil {
brarLog.Errorf("unable to "+
"generate sweep tx: %v", err)
goto close
}
err = b.wallet.PublishTransaction(sweepTx)
if err != nil {
brarLog.Errorf("unable to "+
"broadcast tx: %v", err)
}
}
close:
brarLog.Infof("Force closed ChannelPoint(%v) is "+
"fully closed, updating DB", chanPoint)
@ -641,3 +670,69 @@ func (b *breachArbiter) createJusticeTx(r *retributionInfo) (*wire.MsgTx, error)
return justiceTx, nil
}
// craftCommitmentSweepTx creates a transaction to sweep the non-delayed output
// within the commitment transaction that pays to us. We must manually sweep
// this output as it uses a tweaked public key in its pkScript, so the wallet
// won't immediacy be aware of it.
//
// TODO(roasbeef): alternative options
// * leave the output in the chain, use as input to future funding tx
// * leave output in the chain, extend wallet to add knowledge of how to claim
func (b *breachArbiter) craftCommitSweepTx(closeInfo *lnwallet.UnilateralCloseSummary) (*wire.MsgTx, error) {
// First, we'll fetch a fresh script that we can use to sweep the funds
// under the control of the wallet.
sweepPkScript, err := newSweepPkScript(b.wallet)
if err != nil {
return nil, err
}
// TODO(roasbeef): use proper fees
outputAmt := closeInfo.SelfOutputSignDesc.Output.Value
sweepAmt := int64(outputAmt - 5000)
if sweepAmt <= 0 {
// TODO(roasbeef): add output to special pool, can be swept
// when: funding a channel, sweeping time locked outputs, or
// delivering
// justice after a channel breach
return nil, fmt.Errorf("output to small to sweep in isolation")
}
// With the amount we're sweeping computed, we can now creating the
// sweep transaction itself.
sweepTx := wire.NewMsgTx(1)
sweepTx.AddTxIn(&wire.TxIn{
PreviousOutPoint: *closeInfo.SelfOutPoint,
})
sweepTx.AddTxOut(&wire.TxOut{
PkScript: sweepPkScript,
Value: int64(sweepAmt),
})
// Next, we'll generate the signature required to satisfy the p2wkh
// witness program.
signDesc := closeInfo.SelfOutputSignDesc
signDesc.SigHashes = txscript.NewTxSigHashes(sweepTx)
signDesc.InputIndex = 0
sweepSig, err := b.wallet.Cfg.Signer.SignOutputRaw(sweepTx, signDesc)
if err != nil {
return nil, err
}
// Finally, we'll manually craft the witness. The witness here is the
// exact same as a regular p2wkh witness, but we'll need to ensure that
// we use the tweaked public key as the last item in the witness stack
// which was originally used to created the pkScript we're spending.
witness := make([][]byte, 2)
witness[0] = append(sweepSig, byte(txscript.SigHashAll))
witness[1] = lnwallet.TweakPubKeyWithTweak(
signDesc.PubKey, signDesc.SingleTweak,
).SerializeCompressed()
sweepTx.TxIn[0].Witness = witness
brarLog.Infof("Sweeping commitment output with: %v", spew.Sdump(sweepTx))
return sweepTx, nil
}