multi: address lingering TODO by no longer wiping out local HTLCs on remote close

In this commit, we fix a lingering TOOD statement in the channel arb.
Before this commitment, we would simply wipe our our local HTLC set of
the HTLC set that was on the remote commitment transaction on force
close. This was incorrect as if our commitment transaction had an HTLC
that the remote commitment didn't, then we would fail to cancel that
back, and cause both channels to time out on chain.

In order to remedy this, we introduce a new `HtlcSetKey` struct to track
all 3 possible in-flight set of HTLCs: ours, theirs, and their pending.

We also we start to tack on additional data to all the unilateral close
messages we send to subscribers. This new data is the CommitSet, or the
set of valid commitments at channel closure time. This new information
will be used by the channel arb in an upcoming commit to ensure it will
cancel back HTLCs in the case of split commitment state.

Finally, we start to thread through an optional *CommitSet to the
advanceState method. This additional information will give the channel
arb addition information it needs to ensure it properly cancels back
HTLCs that are about to time out or may time out depending on which
commitment is played.

Within the htlcswitch pakage, we modify the `SignNextCommitment` method
to return the new set of pending HTLCs for the remote party's commitment
transaction and `ReceiveRevocation` to return the latest set of
commitment transactions on the remote party's commitment as well. This
is a preparatory change which is part of a larger change to address a
lingering TODO in the cnct.

Additionally, rather than just send of the set of HTLCs after the we
revoke, we'll also send of the set of HTLCs after the remote party
revokes, and we create a pending commitment state for it.
This commit is contained in:
Olaoluwa Osuntokun committed 2019-05-27 14:34:13 -07:00
1 parent 6e102d64b9
commit 5f0fad85be
11 files changed
+449 -237

No files matched your search

+34 -5
View File
@@ -320,7 +320,7 @@ type channelLink struct {
// htlcUpdates is a channel that we'll use to update outside
// sub-systems with the latest set of active HTLC's on our channel.
htlcUpdates chan []channeldb.HTLC
htlcUpdates chan *contractcourt.ContractUpdate
// logCommitTimer is a timer which is sent upon if we go an interval
// without receiving/sending a commitment update. It's role is to
@@ -372,7 +372,7 @@ func NewChannelLink(cfg ChannelLinkConfig,
// TODO(roasbeef): just do reserve here?
logCommitTimer: time.NewTimer(300 * time.Millisecond),
overflowQueue: newPacketQueue(input.MaxHTLCNumber / 2),
htlcUpdates: make(chan []channeldb.HTLC),
htlcUpdates: make(chan *contractcourt.ContractUpdate),
hodlMap: make(map[lntypes.Hash][]hodlHtlc),
hodlQueue: queue.NewConcurrentQueue(10),
quit: make(chan struct{}),
@@ -1721,7 +1721,10 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) {
// of HTLC's on our commitment, so we'll send them over our
// HTLC update channel so any callers can be notified.
select {
case l.htlcUpdates <- currentHtlcs:
case l.htlcUpdates <- &contractcourt.ContractUpdate{
HtlcKey: contractcourt.LocalHtlcSet,
Htlcs: currentHtlcs,
}:
case <-l.quit:
return
}
@@ -1761,7 +1764,9 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) {
// We've received a revocation from the remote chain, if valid,
// this moves the remote chain forward, and expands our
// revocation window.
fwdPkg, adds, settleFails, err := l.channel.ReceiveRevocation(msg)
fwdPkg, adds, settleFails, remoteHTLCs, err := l.channel.ReceiveRevocation(
msg,
)
if err != nil {
// TODO(halseth): force close?
l.fail(LinkFailureError{code: ErrInvalidRevocation},
@@ -1769,6 +1774,18 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) {
return
}
// The remote party now has a new primary commitment, so we'll
// update the contract court to be aware of this new set (the
// prior old remote pending).
select {
case l.htlcUpdates <- &contractcourt.ContractUpdate{
HtlcKey: contractcourt.RemoteHtlcSet,
Htlcs: remoteHTLCs,
}:
case <-l.quit:
return
}
l.processRemoteSettleFails(fwdPkg, settleFails)
needUpdate := l.processRemoteAdds(fwdPkg, adds)
@@ -1894,7 +1911,7 @@ func (l *channelLink) updateCommitTx() error {
return nil
}
theirCommitSig, htlcSigs, err := l.channel.SignNextCommitment()
theirCommitSig, htlcSigs, pendingHTLCs, err := l.channel.SignNextCommitment()
if err == lnwallet.ErrNoWindow {
l.tracef("revocation window exhausted, unable to send: %v, "+
"dangling_opens=%v, dangling_closes%v",
@@ -1910,6 +1927,18 @@ func (l *channelLink) updateCommitTx() error {
return err
}
// The remote party now has a new pending commitment, so we'll update
// the contract court to be aware of this new set (the prior old remote
// pending).
select {
case l.htlcUpdates <- &contractcourt.ContractUpdate{
HtlcKey: contractcourt.RemotePendingHtlcSet,
Htlcs: pendingHTLCs,
}:
case <-l.quit:
return nil
}
if err := l.ackDownStreamPackets(); err != nil {
return err
}
+8 -8
View File
@@ -1761,7 +1761,7 @@ func handleStateUpdate(link *channelLink,
}
link.HandleChannelUpdate(remoteRev)
remoteSig, remoteHtlcSigs, err := remoteChannel.SignNextCommitment()
remoteSig, remoteHtlcSigs, _, err := remoteChannel.SignNextCommitment()
if err != nil {
return err
}
@@ -1782,7 +1782,7 @@ func handleStateUpdate(link *channelLink,
if !ok {
return fmt.Errorf("expected RevokeAndAck got %T", msg)
}
_, _, _, err = remoteChannel.ReceiveRevocation(revoke)
_, _, _, _, err = remoteChannel.ReceiveRevocation(revoke)
if err != nil {
return fmt.Errorf("unable to receive "+
"revocation: %v", err)
@@ -1812,7 +1812,7 @@ func updateState(batchTick chan time.Time, link *channelLink,
// The remote is triggering the state update, emulate this by
// signing and sending CommitSig to the link.
remoteSig, remoteHtlcSigs, err := remoteChannel.SignNextCommitment()
remoteSig, remoteHtlcSigs, _, err := remoteChannel.SignNextCommitment()
if err != nil {
return err
}
@@ -1836,7 +1836,7 @@ func updateState(batchTick chan time.Time, link *channelLink,
return fmt.Errorf("expected RevokeAndAck got %T",
msg)
}
_, _, _, err = remoteChannel.ReceiveRevocation(revoke)
_, _, _, _, err = remoteChannel.ReceiveRevocation(revoke)
if err != nil {
return fmt.Errorf("unable to receive "+
"revocation: %v", err)
@@ -4397,7 +4397,7 @@ func sendCommitSigBobToAlice(t *testing.T, aliceLink ChannelLink,
t.Helper()
sig, htlcSigs, err := bobChannel.SignNextCommitment()
sig, htlcSigs, _, err := bobChannel.SignNextCommitment()
if err != nil {
t.Fatalf("error signing commitment: %v", err)
}
@@ -4435,7 +4435,7 @@ func receiveRevAndAckAliceToBob(t *testing.T, aliceMsgs chan lnwire.Message,
t.Fatalf("expected RevokeAndAck, got %T", msg)
}
_, _, _, err := bobChannel.ReceiveRevocation(rev)
_, _, _, _, err := bobChannel.ReceiveRevocation(rev)
if err != nil {
t.Fatalf("bob failed receiving revocation: %v", err)
}
@@ -5326,7 +5326,7 @@ func TestChannelLinkFail(t *testing.T) {
// Sign a commitment that will include
// signature for the HTLC just sent.
sig, htlcSigs, err :=
sig, htlcSigs, _, err :=
remoteChannel.SignNextCommitment()
if err != nil {
t.Fatalf("error signing commitment: %v",
@@ -5358,7 +5358,7 @@ func TestChannelLinkFail(t *testing.T) {
// Sign a commitment that will include
// signature for the HTLC just sent.
sig, htlcSigs, err :=
sig, htlcSigs, _, err :=
remoteChannel.SignNextCommitment()
if err != nil {
t.Fatalf("error signing commitment: %v",