multi: add custom error messages to channel acceptor
This commit adds an optional error message to the channel acceptor's reponse to allow operators to inform (or insult) unsuccessful channel initiators as to the reason for their rejection. This field is added in addition to the existing accept field to maintain backwards compatibity. If we were to deprecate accept and interpret a non-nil error as rejecting the channel, then received a response with accept=false and a nil error, the server cannot tell whether this is a legacy rejection or new mesage type acceptance (due to nil error), so we keep both fields.
This commit is contained in:
9 files changed
+1003
-715
No files matched your search
+79
-12
@@ -2,19 +2,36 @@ package chanacceptor
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
)
|
||||
|
||||
var errShuttingDown = errors.New("server shutting down")
|
||||
var (
|
||||
errShuttingDown = errors.New("server shutting down")
|
||||
|
||||
// errCustomLength is returned when our custom error's length exceeds
|
||||
// our maximum.
|
||||
errCustomLength = fmt.Errorf("custom error message exceeds length "+
|
||||
"limit: %v", maxErrorLength)
|
||||
|
||||
// errAcceptWithError is returned when we get a response which accepts
|
||||
// a channel but ambiguously also sets a custom error message.
|
||||
errAcceptWithError = errors.New("channel acceptor response accepts " +
|
||||
"channel, but also includes custom error")
|
||||
|
||||
// maxErrorLength is the maximum error length we allow the error we
|
||||
// send to our peer to be.
|
||||
maxErrorLength = 500
|
||||
)
|
||||
|
||||
// chanAcceptInfo contains a request for a channel acceptor decision, and a
|
||||
// channel that the response should be sent on.
|
||||
type chanAcceptInfo struct {
|
||||
request *ChannelAcceptRequest
|
||||
response chan bool
|
||||
response chan *ChannelAcceptResponse
|
||||
}
|
||||
|
||||
// RPCAcceptor represents the RPC-controlled variant of the ChannelAcceptor.
|
||||
@@ -52,8 +69,8 @@ type RPCAcceptor struct {
|
||||
// receives, failing the request if the timeout elapses.
|
||||
//
|
||||
// NOTE: Part of the ChannelAcceptor interface.
|
||||
func (r *RPCAcceptor) Accept(req *ChannelAcceptRequest) bool {
|
||||
respChan := make(chan bool, 1)
|
||||
func (r *RPCAcceptor) Accept(req *ChannelAcceptRequest) *ChannelAcceptResponse {
|
||||
respChan := make(chan *ChannelAcceptResponse, 1)
|
||||
|
||||
newRequest := &chanAcceptInfo{
|
||||
request: req,
|
||||
@@ -63,6 +80,12 @@ func (r *RPCAcceptor) Accept(req *ChannelAcceptRequest) bool {
|
||||
// timeout is the time after which ChannelAcceptRequests expire.
|
||||
timeout := time.After(r.timeout)
|
||||
|
||||
// Create a rejection response which we can use for the cases where we
|
||||
// reject the channel.
|
||||
rejectChannel := NewChannelAcceptResponse(
|
||||
false, errChannelRejected,
|
||||
)
|
||||
|
||||
// Send the request to the newRequests channel.
|
||||
select {
|
||||
case r.requests <- newRequest:
|
||||
@@ -70,13 +93,13 @@ func (r *RPCAcceptor) Accept(req *ChannelAcceptRequest) bool {
|
||||
case <-timeout:
|
||||
log.Errorf("RPCAcceptor returned false - reached timeout of %v",
|
||||
r.timeout)
|
||||
return false
|
||||
return rejectChannel
|
||||
|
||||
case <-r.done:
|
||||
return false
|
||||
return rejectChannel
|
||||
|
||||
case <-r.quit:
|
||||
return false
|
||||
return rejectChannel
|
||||
}
|
||||
|
||||
// Receive the response and return it. If no response has been received
|
||||
@@ -88,13 +111,13 @@ func (r *RPCAcceptor) Accept(req *ChannelAcceptRequest) bool {
|
||||
case <-timeout:
|
||||
log.Errorf("RPCAcceptor returned false - reached timeout of %v",
|
||||
r.timeout)
|
||||
return false
|
||||
return rejectChannel
|
||||
|
||||
case <-r.done:
|
||||
return false
|
||||
return rejectChannel
|
||||
|
||||
case <-r.quit:
|
||||
return false
|
||||
return rejectChannel
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,6 +183,7 @@ func (r *RPCAcceptor) receiveResponses(errChan chan error,
|
||||
openChanResp := lnrpc.ChannelAcceptResponse{
|
||||
Accept: resp.Accept,
|
||||
PendingChanId: pendingID[:],
|
||||
Error: resp.Error,
|
||||
}
|
||||
|
||||
// We have received a decision for one of our channel
|
||||
@@ -186,7 +210,7 @@ func (r *RPCAcceptor) sendAcceptRequests(errChan chan error,
|
||||
// listening and any in-progress requests should be terminated.
|
||||
defer close(r.done)
|
||||
|
||||
acceptRequests := make(map[[32]byte]chan bool)
|
||||
acceptRequests := make(map[[32]byte]chan *ChannelAcceptResponse)
|
||||
|
||||
for {
|
||||
select {
|
||||
@@ -234,9 +258,17 @@ func (r *RPCAcceptor) sendAcceptRequests(errChan chan error,
|
||||
continue
|
||||
}
|
||||
|
||||
// Validate the response we have received. If it is not
|
||||
// valid, we log our error and proceed to deliver the
|
||||
// rejection.
|
||||
accept, acceptErr, err := validateAcceptorResponse(resp)
|
||||
if err != nil {
|
||||
log.Errorf("Invalid acceptor response: %v", err)
|
||||
}
|
||||
|
||||
// Send the response boolean over the buffered response
|
||||
// channel.
|
||||
respChan <- resp.Accept
|
||||
respChan <- NewChannelAcceptResponse(accept, acceptErr)
|
||||
|
||||
// Delete the channel from the acceptRequests map.
|
||||
delete(acceptRequests, pendingID)
|
||||
@@ -253,6 +285,41 @@ func (r *RPCAcceptor) sendAcceptRequests(errChan chan error,
|
||||
}
|
||||
}
|
||||
|
||||
// validateAcceptorResponse validates the response we get from the channel
|
||||
// acceptor, returning a boolean indicating whether to accept the channel, an
|
||||
// error to send to the peer, and any validation errors that occurred.
|
||||
func validateAcceptorResponse(req lnrpc.ChannelAcceptResponse) (bool, error,
|
||||
error) {
|
||||
|
||||
// Check that the custom error provided is valid.
|
||||
if len(req.Error) > maxErrorLength {
|
||||
return false, errChannelRejected, errCustomLength
|
||||
}
|
||||
|
||||
var haveCustomError = len(req.Error) != 0
|
||||
|
||||
switch {
|
||||
// If accept is true, but we also have an error specified, we fail
|
||||
// because this result is ambiguous.
|
||||
case req.Accept && haveCustomError:
|
||||
return false, errChannelRejected, errAcceptWithError
|
||||
|
||||
// If we accept without an error message, we can just return a nil
|
||||
// error.
|
||||
case req.Accept:
|
||||
return true, nil, nil
|
||||
|
||||
// If we reject the channel, and have a custom error, then we use it.
|
||||
case haveCustomError:
|
||||
return false, fmt.Errorf(req.Error), nil
|
||||
|
||||
// Otherwise, we have rejected the channel with no custom error, so we
|
||||
// just use a generic error to fail the channel.
|
||||
default:
|
||||
return false, errChannelRejected, nil
|
||||
}
|
||||
}
|
||||
|
||||
// A compile-time constraint to ensure RPCAcceptor implements the ChannelAcceptor
|
||||
// interface.
|
||||
var _ ChannelAcceptor = (*RPCAcceptor)(nil)
|
||||
Reference in new issue
Block a user