htlcswitch: defer processRemoteAdds when quiescent

In this commit we defer processRemoteAdds using a new mechanism on
the quiescer where we capture a closure that needs to be run. We
do this because we need to avoid the scenario where we send back
immediate resolutions to the newly added HTLCs when quiescent as
it is a protocol violation. It is not enough for us to simply defer
sending the messages since the purpose of quiescence itself is to
have well-defined and agreed upon channel state. If, for whatever
reason, the node (or connection) is restarted between when these
hooks are captured and when they are ultimately run, they will
be resolved by the resolveFwdPkgs logic when the link comes back
up.

In a future commit we will explicitly call the quiescer's resume
method when it is OK for htlc traffic to commence.
This commit is contained in:
Keagan McClelland
2024-04-08 18:17:00 -07:00
parent 77fd8c6a21
commit 4fbab45a5f
3 changed files with 94 additions and 1 deletions

View File

@@ -90,6 +90,11 @@ type Quiescer struct {
// resolve when we complete quiescence.
activeQuiescenceReq fn.Option[StfuReq]
// resumeQueue is a slice of hooks that will be called when the quiescer
// is resumed. These are actions that needed to be deferred while the
// channel was quiescent.
resumeQueue []func()
sync.RWMutex
}
@@ -400,3 +405,42 @@ func (q *Quiescer) initStfu(req StfuReq) {
q.localInit = true
q.activeQuiescenceReq = fn.Some(req)
}
// OnResume accepts a no return closure that will run when the quiescer is
// resumed.
func (q *Quiescer) OnResume(hook func()) {
q.Lock()
defer q.Unlock()
q.onResume(hook)
}
// onResume accepts a no return closure that will run when the quiescer is
// resumed.
func (q *Quiescer) onResume(hook func()) {
q.resumeQueue = append(q.resumeQueue, hook)
}
// Resume runs all of the deferred actions that have accumulated while the
// channel has been quiescent and then resets the quiescer state to its initial
// state.
func (q *Quiescer) Resume() {
q.Lock()
defer q.Unlock()
q.resume()
}
// resume runs all of the deferred actions that have accumulated while the
// channel has been quiescent and then resets the quiescer state to its initial
// state.
func (q *Quiescer) resume() {
for _, hook := range q.resumeQueue {
hook()
}
q.localInit = false
q.remoteInit = false
q.sent = false
q.received = false
q.resumeQueue = nil
}