lnwallet: add distinct CommitOutputResolution for resolving commit outputs on-chain

In this commit, we modify both the ForceCloseSummary, and the
UnilateralClosureSummary to return the items needed to sweep the
commitment output distinctly. By doing this, it’s now possible to pass
a dedicated struct to a sub-system in order to allow it to sweep a
commitment output. As the maturity delay is a part of this new struct,
this tells the caller if this was on the local commitment (CSV
required) or on the remote commitment (no CSV required).
This commit is contained in:
Olaoluwa Osuntokun 2018-01-16 18:17:18 -08:00
parent 42cd2fa5be
commit ecf6d758a2
No known key found for this signature in database
GPG Key ID: 964EA263DD637C21

@ -2065,6 +2065,7 @@ func (lc *LightningChannel) closeObserver(channelCloseNtfn *chainntnfs.SpendEven
// //
// TODO(roasbeef): include HTLC's // TODO(roasbeef): include HTLC's
// * and time-locked balance, NEED TO??? // * and time-locked balance, NEED TO???
localBalance := lc.channelState.LocalCommitment.LocalBalance.ToSatoshis()
closeSummary := channeldb.ChannelCloseSummary{ closeSummary := channeldb.ChannelCloseSummary{
ChanPoint: lc.channelState.FundingOutpoint, ChanPoint: lc.channelState.FundingOutpoint,
ChainHash: lc.channelState.ChainHash, ChainHash: lc.channelState.ChainHash,
@ -2072,7 +2073,7 @@ func (lc *LightningChannel) closeObserver(channelCloseNtfn *chainntnfs.SpendEven
CloseHeight: spendHeight, CloseHeight: spendHeight,
RemotePub: lc.channelState.IdentityPub, RemotePub: lc.channelState.IdentityPub,
Capacity: lc.Capacity, Capacity: lc.Capacity,
SettledBalance: lc.channelState.LocalCommitment.LocalBalance.ToSatoshis(), SettledBalance: localBalance,
CloseType: channeldb.ForceClose, CloseType: channeldb.ForceClose,
IsPending: true, IsPending: true,
} }
@ -2127,31 +2128,32 @@ func (lc *LightningChannel) closeObserver(channelCloseNtfn *chainntnfs.SpendEven
// With the HTLC's taken care of, we'll generate the sign // With the HTLC's taken care of, we'll generate the sign
// descriptor necessary to sweep our commitment output, but // descriptor necessary to sweep our commitment output, but
// only if we had a non-trimmed balance. // only if we had a non-trimmed balance.
var selfSignDesc *SignDescriptor var commitResolution *CommitOutputResolution
if selfPoint != nil { if selfPoint != nil {
localPayBase := lc.localChanCfg.PaymentBasePoint localPayBase := lc.localChanCfg.PaymentBasePoint
localBalance := lc.channelState.LocalCommitment.LocalBalance.ToSatoshis() localBalance := lc.channelState.LocalCommitment.LocalBalance.ToSatoshis()
selfSignDesc = &SignDescriptor{ commitResolution = &CommitOutputResolution{
PubKey: localPayBase, SelfOutPoint: *selfPoint,
SingleTweak: keyRing.localCommitKeyTweak, SelfOutputSignDesc: SignDescriptor{
WitnessScript: selfP2WKH, PubKey: localPayBase,
Output: &wire.TxOut{ SingleTweak: keyRing.localCommitKeyTweak,
Value: int64(localBalance), WitnessScript: selfP2WKH,
PkScript: selfP2WKH, Output: &wire.TxOut{
Value: int64(localBalance),
PkScript: selfP2WKH,
},
HashType: txscript.SigHashAll,
}, },
HashType: txscript.SigHashAll, MaturityDelay: 0,
} }
} }
// We'll also send all the details necessary to re-claim funds uniCloseSummary := &UnilateralCloseSummary{
// that are suspended within any contracts.
unilateralCloseSummary := &UnilateralCloseSummary{
SpendDetail: commitSpend, SpendDetail: commitSpend,
ChannelCloseSummary: closeSummary, ChannelCloseSummary: closeSummary,
SelfOutPoint: selfPoint, CommitResolution: commitResolution,
SelfOutputSignDesc: selfSignDesc,
MaturityDelay: uint32(lc.remoteChanCfg.CsvDelay),
HtlcResolutions: htlcResolutions, HtlcResolutions: htlcResolutions,
ChanSnapshot: *lc.channelState.Snapshot(),
} }
// TODO(roasbeef): send msg before writing to disk // TODO(roasbeef): send msg before writing to disk
@ -2163,8 +2165,10 @@ func (lc *LightningChannel) closeObserver(channelCloseNtfn *chainntnfs.SpendEven
// commitment transaction broadcast. // commitment transaction broadcast.
close(lc.UnilateralCloseSignal) close(lc.UnilateralCloseSignal)
// We'll also send all the details necessary to re-claim funds
// that are suspended within any contracts.
select { select {
case lc.UnilateralClose <- unilateralCloseSummary: case lc.UnilateralClose <- uniCloseSummary:
case <-lc.observerQuit: case <-lc.observerQuit:
walletLog.Errorf("channel shutting down") walletLog.Errorf("channel shutting down")
return return
@ -2240,6 +2244,7 @@ func (lc *LightningChannel) closeObserver(channelCloseNtfn *chainntnfs.SpendEven
SettledBalance: settledBalance, SettledBalance: settledBalance,
CloseType: channeldb.BreachClose, CloseType: channeldb.BreachClose,
IsPending: true, IsPending: true,
ShortChanID: lc.channelState.ShortChanID,
} }
err = lc.DeleteState(&closeSummary) err = lc.DeleteState(&closeSummary)
@ -4366,6 +4371,25 @@ func (lc *LightningChannel) getSignedCommitTx() (*wire.MsgTx, error) {
return commitTx, nil return commitTx, nil
} }
// CommitOutputResolution carries the necessary information required to allow
// us to sweep our direct commitment output in the case that either party goes
// to chain.
type CommitOutputResolution struct {
// SelfOutPoint is the full outpoint that points to out pay-to-self
// output within the closing commitment transaction.
SelfOutPoint wire.OutPoint
// SelfOutputSignDesc is a fully populated sign descriptor capable of
// generating a valid signature to sweep the output paying to us.
SelfOutputSignDesc SignDescriptor
// MaturityDelay is the relative time-lock, in blocks for all outputs
// that pay to the local party within the broadcast commitment
// transaction. This value will be non-zero iff, this output was on our
// commitment transaction.
MaturityDelay uint32
}
// UnilateralCloseSummary describes the details of a detected unilateral // UnilateralCloseSummary describes the details of a detected unilateral
// channel closure. This includes the information about with which // channel closure. This includes the information about with which
// transactions, and block the channel was unilaterally closed, as well as // transactions, and block the channel was unilaterally closed, as well as
@ -4384,19 +4408,21 @@ type UnilateralCloseSummary struct {
// channel and in which state is was closed. // channel and in which state is was closed.
channeldb.ChannelCloseSummary channeldb.ChannelCloseSummary
// SelfOutPoint is the full outpoint that points to our non-delayed // CommitResolution contains all the data required to sweep the output
// pay-to-self output within the commitment transaction of the remote // to ourselves. If this is our commitment transaction, then we'll need
// party. // to wait a time delay before we can sweep the output.
SelfOutPoint *wire.OutPoint //
// NOTE: If our commitment delivery output is below the dust limit,
// then this will be nil.
CommitResolution *CommitOutputResolution
// ChanSnapshot is a snapshot of the final state of the channel at the
// time it was closed.
ChanSnapshot channeldb.ChannelSnapshot
}
// SelfOutputSignDesc is a fully populated sign descriptor capable of
// generating a valid signature to sweep the output paying to us
SelfOutputSignDesc *SignDescriptor
// MaturityDelay is the relative time-lock, in blocks for all outputs
// that pay to the local party within the broadcast commitment
// transaction.
MaturityDelay uint32
// HtlcResolutions is a slice of HTLC resolutions which allows the // HtlcResolutions is a slice of HTLC resolutions which allows the
// local node to sweep any outgoing HTLC"s after the timeout period has // local node to sweep any outgoing HTLC"s after the timeout period has
@ -4577,31 +4603,28 @@ type ForceCloseSummary struct {
// force closed. // force closed.
ChanPoint wire.OutPoint ChanPoint wire.OutPoint
// SelfOutpoint is the output created by the above close tx which is
// spendable by us after a relative time delay.
SelfOutpoint wire.OutPoint
// CloseTx is the transaction which closed the channel on-chain. If we // CloseTx is the transaction which closed the channel on-chain. If we
// initiate the force close, then this'll be our latest commitment // initiate the force close, then this'll be our latest commitment
// state. Otherwise, this'll be the state that the remote peer // state. Otherwise, this'll be the state that the remote peer
// broadcasted on-chain. // broadcasted on-chain.
CloseTx *wire.MsgTx CloseTx *wire.MsgTx
// SelfOutputSignDesc is a fully populated sign descriptor capable of // CommitResolution contains all the data required to sweep the output
// generating a valid signature to sweep the self output. // to ourselves. If this is our commitment transaction, then we'll need
// to wait a time delay before we can sweep the output.
// //
// NOTE: If the commitment delivery output of the force closing party // NOTE: If our commitment delivery output is below the dust limit,
// is below the dust limit, then this will be nil. // then this will be nil.
SelfOutputSignDesc *SignDescriptor CommitResolution *CommitOutputResolution
// SelfOutputMaturity is the relative maturity period before the above // HtlcResolutions contains all the data required to sweep any outgoing
// output can be claimed. // HTLC's and incoming HTLc's we now the preimage to. For each of these
SelfOutputMaturity uint32 // HTLC's, we'll need to go to the second level to sweep them fully.
HtlcResolutions *HtlcResolutions
// HtlcResolutions is a slice of HTLC resolutions which allows the // ChanSnapshot is a snapshot of the final state of the channel at the
// local node to sweep any outgoing HTLC"s after the timeout period has // time it was closed.
// passed. ChanSnapshot channeldb.ChannelSnapshot
HtlcResolutions []OutgoingHtlcResolution
} }
// ForceClose executes a unilateral closure of the transaction at the current // ForceClose executes a unilateral closure of the transaction at the current
@ -4655,9 +4678,8 @@ func (lc *LightningChannel) ForceClose() (*ForceCloseSummary, error) {
// We'll return the details of this output to the caller so they can // We'll return the details of this output to the caller so they can
// sweep it once it's mature. // sweep it once it's mature.
var ( var (
delayIndex uint32 delayIndex uint32
delayScript []byte delayScript []byte
selfSignDesc *SignDescriptor
) )
for i, txOut := range commitTx.TxOut { for i, txOut := range commitTx.TxOut {
if !bytes.Equal(payToUsScriptHash, txOut.PkScript) { if !bytes.Equal(payToUsScriptHash, txOut.PkScript) {
@ -4677,19 +4699,27 @@ func (lc *LightningChannel) ForceClose() (*ForceCloseSummary, error) {
// set as the caller will decide these values once sweeping the output. // set as the caller will decide these values once sweeping the output.
// If the output is non-existent (dust), have the sign descriptor be // If the output is non-existent (dust), have the sign descriptor be
// nil. // nil.
var commitResolution *CommitOutputResolution
if len(delayScript) != 0 { if len(delayScript) != 0 {
singleTweak := SingleTweakBytes(commitPoint, singleTweak := SingleTweakBytes(commitPoint,
lc.localChanCfg.DelayBasePoint) lc.localChanCfg.DelayBasePoint)
localBalance := localCommitment.LocalBalance localBalance := localCommitment.LocalBalance
selfSignDesc = &SignDescriptor{ commitResolution = &CommitOutputResolution{
PubKey: lc.localChanCfg.DelayBasePoint, SelfOutPoint: wire.OutPoint{
SingleTweak: singleTweak, Hash: commitTx.TxHash(),
WitnessScript: selfScript, Index: delayIndex,
Output: &wire.TxOut{
PkScript: delayScript,
Value: int64(localBalance.ToSatoshis()),
}, },
HashType: txscript.SigHashAll, SelfOutputSignDesc: SignDescriptor{
PubKey: lc.localChanCfg.DelayBasePoint,
SingleTweak: singleTweak,
WitnessScript: selfScript,
Output: &wire.TxOut{
PkScript: delayScript,
Value: int64(localBalance.ToSatoshis()),
},
HashType: txscript.SigHashAll,
},
MaturityDelay: csvTimeout,
} }
} }
@ -4711,15 +4741,11 @@ func (lc *LightningChannel) ForceClose() (*ForceCloseSummary, error) {
close(lc.ForceCloseSignal) close(lc.ForceCloseSignal)
return &ForceCloseSummary{ return &ForceCloseSummary{
ChanPoint: lc.channelState.FundingOutpoint, ChanPoint: lc.channelState.FundingOutpoint,
SelfOutpoint: wire.OutPoint{ CloseTx: commitTx,
Hash: commitTx.TxHash(), CommitResolution: commitResolution,
Index: delayIndex, HtlcResolutions: htlcResolutions,
}, ChanSnapshot: *lc.channelState.Snapshot(),
CloseTx: commitTx,
SelfOutputSignDesc: selfSignDesc,
SelfOutputMaturity: csvTimeout,
HtlcResolutions: htlcResolutions,
}, nil }, nil
} }
@ -5300,7 +5326,7 @@ func (lc *LightningChannel) ActiveHtlcs() []channeldb.HTLC {
remoteHtlcs[onionHash] = struct{}{} remoteHtlcs[onionHash] = struct{}{}
} }
// Now tht we know which HTLC's they have, we'll only mark the HTLC's // Now that we know which HTLC's they have, we'll only mark the HTLC's
// as active if *we* know them as well. // as active if *we* know them as well.
activeHtlcs := make([]channeldb.HTLC, 0, len(remoteHtlcs)) activeHtlcs := make([]channeldb.HTLC, 0, len(remoteHtlcs))
for _, htlc := range lc.channelState.LocalCommitment.Htlcs { for _, htlc := range lc.channelState.LocalCommitment.Htlcs {