lnd.xprv/chanacceptor/chainedacceptor.go
carla 5679dde1bc
multi: add channel open parameters to channel acceptor
Add more fields to channel acceptor response so that users can have more
fine grained control over their incoming channels. With our chained
acceptor, it is possible that we get inconsistent responses from
multiple chained acceptors. We create a conjugate repsponse from all the
set fields in our various responses, but fail if we get different, non-
zero responses from our various acceptors. Separate merge functions are
used per type so that we avoid unexpected outcomes comparing interfaces
(panic on comparing types that aren't comparable), with casting used
where applicable to avoid code duplication.
2020-11-10 21:09:10 +02:00

96 lines
2.9 KiB
Go

package chanacceptor
import (
"sync"
"sync/atomic"
)
// ChainedAcceptor represents a conjunction of ChannelAcceptor results.
type ChainedAcceptor struct {
// acceptors is a map of ChannelAcceptors that will be evaluated when
// the ChainedAcceptor's Accept method is called.
acceptors map[uint64]ChannelAcceptor
acceptorsMtx sync.RWMutex
acceptorID uint64 // To be used atomically.
}
// NewChainedAcceptor initializes a ChainedAcceptor.
func NewChainedAcceptor() *ChainedAcceptor {
return &ChainedAcceptor{
acceptors: make(map[uint64]ChannelAcceptor),
}
}
// AddAcceptor adds a ChannelAcceptor to this ChainedAcceptor.
func (c *ChainedAcceptor) AddAcceptor(acceptor ChannelAcceptor) uint64 {
id := atomic.AddUint64(&c.acceptorID, 1)
c.acceptorsMtx.Lock()
c.acceptors[id] = acceptor
c.acceptorsMtx.Unlock()
// Return the id so that a caller can call RemoveAcceptor.
return id
}
// RemoveAcceptor removes a ChannelAcceptor from this ChainedAcceptor given
// an ID.
func (c *ChainedAcceptor) RemoveAcceptor(id uint64) {
c.acceptorsMtx.Lock()
delete(c.acceptors, id)
c.acceptorsMtx.Unlock()
}
// Accept evaluates the results of all ChannelAcceptors in the acceptors map
// and returns the conjunction of all these predicates.
//
// NOTE: Part of the ChannelAcceptor interface.
func (c *ChainedAcceptor) Accept(req *ChannelAcceptRequest) *ChannelAcceptResponse {
c.acceptorsMtx.RLock()
defer c.acceptorsMtx.RUnlock()
var finalResp ChannelAcceptResponse
for _, acceptor := range c.acceptors {
// Call our acceptor to determine whether we want to accept this
// channel.
acceptorResponse := acceptor.Accept(req)
// If we should reject the channel, we can just exit early. This
// has the effect of returning the error belonging to our first
// failed acceptor.
if acceptorResponse.RejectChannel() {
return acceptorResponse
}
// If we have accepted the channel, we need to set the other
// fields that were set in the response. However, since we are
// dealing with multiple responses, we need to make sure that we
// have not received inconsistent values (eg a csv delay of 1
// from one acceptor, and a delay of 120 from another). We
// set each value on our final response if it has not been set
// yet, and allow duplicate sets if the value is the same. If
// we cannot set a field, we return an error response.
var err error
finalResp, err = mergeResponse(finalResp, *acceptorResponse)
if err != nil {
log.Errorf("response for: %x has inconsistent values: %v",
req.OpenChanMsg.PendingChannelID, err)
return NewChannelAcceptResponse(
false, errChannelRejected, nil, 0, 0,
0, 0, 0, 0,
)
}
}
// If we have gone through all of our acceptors with no objections, we
// can return an acceptor with a nil error.
return &finalResp
}
// A compile-time constraint to ensure ChainedAcceptor implements the
// ChannelAcceptor interface.
var _ ChannelAcceptor = (*ChainedAcceptor)(nil)