htlcswitch: add processRemoteRevokeAndAck
This commit is contained in:
1 file changed
+107
-97
+107
-97
@@ -1961,107 +1961,12 @@ func (l *channelLink) handleUpstreamMsg(ctx context.Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
case *lnwire.RevokeAndAck:
|
case *lnwire.RevokeAndAck:
|
||||||
// We've received a revocation from the remote chain, if valid,
|
err := l.processRemoteRevokeAndAck(ctx, msg)
|
||||||
// this moves the remote chain forward, and expands our
|
|
||||||
// revocation window.
|
|
||||||
|
|
||||||
// We now process the message and advance our remote commit
|
|
||||||
// chain.
|
|
||||||
fwdPkg, remoteHTLCs, err := l.channel.ReceiveRevocation(msg)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO(halseth): force close?
|
l.log.Errorf("failed to process remote revoke: %v", err)
|
||||||
l.failf(
|
|
||||||
LinkFailureError{
|
|
||||||
code: ErrInvalidRevocation,
|
|
||||||
FailureAction: LinkFailureDisconnect,
|
|
||||||
},
|
|
||||||
"unable to accept revocation: %v", err,
|
|
||||||
)
|
|
||||||
return
|
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).
|
|
||||||
newUpdate := &contractcourt.ContractUpdate{
|
|
||||||
HtlcKey: contractcourt.RemoteHtlcSet,
|
|
||||||
Htlcs: remoteHTLCs,
|
|
||||||
}
|
|
||||||
err = l.cfg.NotifyContractUpdate(newUpdate)
|
|
||||||
if err != nil {
|
|
||||||
l.log.Errorf("unable to notify contract update: %v",
|
|
||||||
err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-l.cg.Done():
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have a tower client for this channel type, we'll
|
|
||||||
// create a backup for the current state.
|
|
||||||
if l.cfg.TowerClient != nil {
|
|
||||||
state := l.channel.State()
|
|
||||||
chanID := l.ChanID()
|
|
||||||
|
|
||||||
err = l.cfg.TowerClient.BackupState(
|
|
||||||
&chanID, state.RemoteCommitment.CommitHeight-1,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
l.failf(LinkFailureError{
|
|
||||||
code: ErrInternalError,
|
|
||||||
}, "unable to queue breach backup: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we can send updates then we can process adds in case we
|
|
||||||
// are the exit hop and need to send back resolutions, or in
|
|
||||||
// case there are validity issues with the packets. Otherwise
|
|
||||||
// we defer the action until resume.
|
|
||||||
//
|
|
||||||
// We are free to process the settles and fails without this
|
|
||||||
// check since processing those can't result in further updates
|
|
||||||
// to this channel link.
|
|
||||||
if l.quiescer.CanSendUpdates() {
|
|
||||||
l.processRemoteAdds(fwdPkg)
|
|
||||||
} else {
|
|
||||||
l.quiescer.OnResume(func() {
|
|
||||||
l.processRemoteAdds(fwdPkg)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
l.processRemoteSettleFails(fwdPkg)
|
|
||||||
|
|
||||||
// If the link failed during processing the adds, we must
|
|
||||||
// return to ensure we won't attempted to update the state
|
|
||||||
// further.
|
|
||||||
if l.failed {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// The revocation window opened up. If there are pending local
|
|
||||||
// updates, try to update the commit tx. Pending updates could
|
|
||||||
// already have been present because of a previously failed
|
|
||||||
// update to the commit tx or freshly added in by
|
|
||||||
// processRemoteAdds. Also in case there are no local updates,
|
|
||||||
// but there are still remote updates that are not in the remote
|
|
||||||
// commit tx yet, send out an update.
|
|
||||||
if l.channel.OweCommitment() {
|
|
||||||
if !l.updateCommitTxOrFail(ctx) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now that we have finished processing the RevokeAndAck, we
|
|
||||||
// can invoke the flushHooks if the channel state is clean.
|
|
||||||
l.RWMutex.Lock()
|
|
||||||
if l.channel.IsChannelClean() {
|
|
||||||
l.flushHooks.invoke()
|
|
||||||
}
|
|
||||||
l.RWMutex.Unlock()
|
|
||||||
|
|
||||||
case *lnwire.UpdateFee:
|
case *lnwire.UpdateFee:
|
||||||
// Check and see if their proposed fee-rate would make us
|
// Check and see if their proposed fee-rate would make us
|
||||||
// exceed the fee threshold.
|
// exceed the fee threshold.
|
||||||
@@ -4701,3 +4606,108 @@ func (l *channelLink) processRemoteCommitSig(ctx context.Context,
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// processRemoteRevokeAndAck takes a `RevokeAndAck` msg sent from the remote and
|
||||||
|
// processes it.
|
||||||
|
func (l *channelLink) processRemoteRevokeAndAck(ctx context.Context,
|
||||||
|
msg *lnwire.RevokeAndAck) error {
|
||||||
|
|
||||||
|
// We've received a revocation from the remote chain, if valid, this
|
||||||
|
// moves the remote chain forward, and expands our revocation window.
|
||||||
|
|
||||||
|
// We now process the message and advance our remote commit chain.
|
||||||
|
fwdPkg, remoteHTLCs, err := l.channel.ReceiveRevocation(msg)
|
||||||
|
if err != nil {
|
||||||
|
// TODO(halseth): force close?
|
||||||
|
l.failf(
|
||||||
|
LinkFailureError{
|
||||||
|
code: ErrInvalidRevocation,
|
||||||
|
FailureAction: LinkFailureDisconnect,
|
||||||
|
},
|
||||||
|
"unable to accept revocation: %v", err,
|
||||||
|
)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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).
|
||||||
|
newUpdate := &contractcourt.ContractUpdate{
|
||||||
|
HtlcKey: contractcourt.RemoteHtlcSet,
|
||||||
|
Htlcs: remoteHTLCs,
|
||||||
|
}
|
||||||
|
err = l.cfg.NotifyContractUpdate(newUpdate)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to notify contract update: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-l.cg.Done():
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have a tower client for this channel type, we'll create a
|
||||||
|
// backup for the current state.
|
||||||
|
if l.cfg.TowerClient != nil {
|
||||||
|
state := l.channel.State()
|
||||||
|
chanID := l.ChanID()
|
||||||
|
|
||||||
|
err = l.cfg.TowerClient.BackupState(
|
||||||
|
&chanID, state.RemoteCommitment.CommitHeight-1,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
l.failf(LinkFailureError{
|
||||||
|
code: ErrInternalError,
|
||||||
|
}, "unable to queue breach backup: %v", err)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we can send updates then we can process adds in case we are the
|
||||||
|
// exit hop and need to send back resolutions, or in case there are
|
||||||
|
// validity issues with the packets. Otherwise we defer the action until
|
||||||
|
// resume.
|
||||||
|
//
|
||||||
|
// We are free to process the settles and fails without this check since
|
||||||
|
// processing those can't result in further updates to this channel
|
||||||
|
// link.
|
||||||
|
if l.quiescer.CanSendUpdates() {
|
||||||
|
l.processRemoteAdds(fwdPkg)
|
||||||
|
} else {
|
||||||
|
l.quiescer.OnResume(func() {
|
||||||
|
l.processRemoteAdds(fwdPkg)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
l.processRemoteSettleFails(fwdPkg)
|
||||||
|
|
||||||
|
// If the link failed during processing the adds, we must return to
|
||||||
|
// ensure we won't attempted to update the state further.
|
||||||
|
if l.failed {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// The revocation window opened up. If there are pending local updates,
|
||||||
|
// try to update the commit tx. Pending updates could already have been
|
||||||
|
// present because of a previously failed update to the commit tx or
|
||||||
|
// freshly added in by processRemoteAdds. Also in case there are no
|
||||||
|
// local updates, but there are still remote updates that are not in the
|
||||||
|
// remote commit tx yet, send out an update.
|
||||||
|
if l.channel.OweCommitment() {
|
||||||
|
if !l.updateCommitTxOrFail(ctx) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now that we have finished processing the RevokeAndAck, we can invoke
|
||||||
|
// the flushHooks if the channel state is clean.
|
||||||
|
l.RWMutex.Lock()
|
||||||
|
if l.channel.IsChannelClean() {
|
||||||
|
l.flushHooks.invoke()
|
||||||
|
}
|
||||||
|
l.RWMutex.Unlock()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
Reference in new issue
Block a user