Merge pull request #3039 from Crypt-iQ/predicate_channel_accept_0428

rpc: bi-directional streaming for predicate-based channel acceptance
This commit is contained in:
Olaoluwa Osuntokun authored and GitHub committed 2019-09-25 16:29:48 -07:00
commit e0d7854432
12 files changed
+1571 -652

No files matched your search

+156
View File
@@ -0,0 +1,156 @@
package chanacceptor
import (
"bytes"
"sync/atomic"
"testing"
"time"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/btcsuite/btcd/btcec"
"github.com/lightningnetwork/lnd/lnwire"
)
func randKey(t *testing.T) *btcec.PublicKey {
t.Helper()
priv, err := btcec.NewPrivateKey(btcec.S256())
if err != nil {
t.Fatalf("unable to generate new public key")
}
return priv.PubKey()
}
// requestInfo encapsulates the information sent from the RPCAcceptor to the
// receiver on the other end of the stream.
type requestInfo struct {
chanReq *ChannelAcceptRequest
responseChan chan lnrpc.ChannelAcceptResponse
}
var defaultAcceptTimeout = 5 * time.Second
func acceptAndIncrementCtr(rpc ChannelAcceptor, req *ChannelAcceptRequest,
ctr *uint32, success chan struct{}) {
result := rpc.Accept(req)
if !result {
return
}
val := atomic.AddUint32(ctr, 1)
if val == 3 {
success <- struct{}{}
}
}
// TestMultipleRPCClients tests that the RPCAcceptor is able to handle multiple
// callers to its Accept method and respond to them correctly.
func TestRPCMultipleAcceptClients(t *testing.T) {
var (
node = randKey(t)
firstOpenReq = &ChannelAcceptRequest{
Node: node,
OpenChanMsg: &lnwire.OpenChannel{
PendingChannelID: [32]byte{0},
},
}
secondOpenReq = &ChannelAcceptRequest{
Node: node,
OpenChanMsg: &lnwire.OpenChannel{
PendingChannelID: [32]byte{1},
},
}
thirdOpenReq = &ChannelAcceptRequest{
Node: node,
OpenChanMsg: &lnwire.OpenChannel{
PendingChannelID: [32]byte{2},
},
}
counter uint32
)
quit := make(chan struct{})
defer close(quit)
// Create channels to handle requests and successes.
requests := make(chan *requestInfo)
successChan := make(chan struct{})
errChan := make(chan struct{}, 4)
// demultiplexReq is a closure used to abstract the RPCAcceptor's request
// and response logic.
demultiplexReq := func(req *ChannelAcceptRequest) bool {
respChan := make(chan lnrpc.ChannelAcceptResponse, 1)
newRequest := &requestInfo{
chanReq: req,
responseChan: respChan,
}
// Send the newRequest to the requests channel.
select {
case requests <- newRequest:
case <-quit:
return false
}
// Receive the response and verify that the PendingChanId matches
// the ID found in the ChannelAcceptRequest. If no response has been
// received in defaultAcceptTimeout, then return false.
select {
case resp := <-respChan:
pendingID := req.OpenChanMsg.PendingChannelID
if !bytes.Equal(pendingID[:], resp.PendingChanId) {
errChan <- struct{}{}
return false
}
return resp.Accept
case <-time.After(defaultAcceptTimeout):
errChan <- struct{}{}
return false
case <-quit:
return false
}
}
rpcAcceptor := NewRPCAcceptor(demultiplexReq)
// Now we call the Accept method for each request.
go func() {
acceptAndIncrementCtr(rpcAcceptor, firstOpenReq, &counter, successChan)
}()
go func() {
acceptAndIncrementCtr(rpcAcceptor, secondOpenReq, &counter, successChan)
}()
go func() {
acceptAndIncrementCtr(rpcAcceptor, thirdOpenReq, &counter, successChan)
}()
for {
select {
case newRequest := <-requests:
newResponse := lnrpc.ChannelAcceptResponse{
Accept: true,
PendingChanId: newRequest.chanReq.OpenChanMsg.PendingChannelID[:],
}
newRequest.responseChan <- newResponse
case <-errChan:
t.Fatalf("unable to accept ChannelAcceptRequest")
case <-successChan:
return
case <-quit:
}
}
}
+65
View File
@@ -0,0 +1,65 @@
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) bool {
result := true
c.acceptorsMtx.RLock()
for _, acceptor := range c.acceptors {
// We call Accept first in case any acceptor (perhaps an RPCAcceptor)
// wishes to be notified about ChannelAcceptRequest.
result = acceptor.Accept(req) && result
}
c.acceptorsMtx.RUnlock()
return result
}
// A compile-time constraint to ensure ChainedAcceptor implements the
// ChannelAcceptor interface.
var _ ChannelAcceptor = (*ChainedAcceptor)(nil)
+25
View File
@@ -0,0 +1,25 @@
package chanacceptor
import (
"github.com/btcsuite/btcd/btcec"
"github.com/lightningnetwork/lnd/lnwire"
)
// ChannelAcceptRequest is a struct containing the requesting node's public key
// along with the lnwire.OpenChannel message that they sent when requesting an
// inbound channel. This information is provided to each acceptor so that they
// can each leverage their own decision-making with this information.
type ChannelAcceptRequest struct {
// Node is the public key of the node requesting to open a channel.
Node *btcec.PublicKey
// OpenChanMsg is the actual OpenChannel protocol message that the peer
// sent to us.
OpenChanMsg *lnwire.OpenChannel
}
// ChannelAcceptor is an interface that represents a predicate on the data
// contained in ChannelAcceptRequest.
type ChannelAcceptor interface {
Accept(req *ChannelAcceptRequest) bool
}
+27
View File
@@ -0,0 +1,27 @@
package chanacceptor
// RPCAcceptor represents the RPC-controlled variant of the ChannelAcceptor.
// One RPCAcceptor allows one RPC client.
type RPCAcceptor struct {
acceptClosure func(req *ChannelAcceptRequest) bool
}
// Accept is a predicate on the ChannelAcceptRequest which is sent to the RPC
// client who will respond with the ultimate decision. This assumes an accept
// closure has been specified during creation.
//
// NOTE: Part of the ChannelAcceptor interface.
func (r *RPCAcceptor) Accept(req *ChannelAcceptRequest) bool {
return r.acceptClosure(req)
}
// NewRPCAcceptor creates and returns an instance of the RPCAcceptor.
func NewRPCAcceptor(closure func(*ChannelAcceptRequest) bool) *RPCAcceptor {
return &RPCAcceptor{
acceptClosure: closure,
}
}
// A compile-time constraint to ensure RPCAcceptor implements the ChannelAcceptor
// interface.
var _ ChannelAcceptor = (*RPCAcceptor)(nil)
+23 -1
View File
@@ -15,6 +15,7 @@ import (
"github.com/davecgh/go-spew/spew" "github.com/davecgh/go-spew/spew"
"github.com/go-errors/errors" "github.com/go-errors/errors"
"github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/chanacceptor"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/discovery" "github.com/lightningnetwork/lnd/discovery"
"github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/htlcswitch"
@@ -338,6 +339,11 @@ type fundingConfig struct {
// NotifyOpenChannelEvent informs the ChannelNotifier when channels // NotifyOpenChannelEvent informs the ChannelNotifier when channels
// transition from pending open to open. // transition from pending open to open.
NotifyOpenChannelEvent func(wire.OutPoint) NotifyOpenChannelEvent func(wire.OutPoint)
// OpenChannelPredicate is a predicate on the lnwire.OpenChannel message
// and on the requesting node's public key that returns a bool which tells
// the funding manager whether or not to accept the channel.
OpenChannelPredicate chanacceptor.ChannelAcceptor
} }
// fundingManager acts as an orchestrator/bridge between the wallet's // fundingManager acts as an orchestrator/bridge between the wallet's
@@ -1057,7 +1063,23 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) {
if f.cfg.RejectPush && msg.PushAmount > 0 { if f.cfg.RejectPush && msg.PushAmount > 0 {
f.failFundingFlow( f.failFundingFlow(
fmsg.peer, fmsg.msg.PendingChannelID, fmsg.peer, fmsg.msg.PendingChannelID,
lnwallet.ErrNonZeroPushAmount()) lnwallet.ErrNonZeroPushAmount(),
)
return
}
// Send the OpenChannel request to the ChannelAcceptor to determine whether
// this node will accept the channel.
chanReq := &chanacceptor.ChannelAcceptRequest{
Node: fmsg.peer.IdentityKey(),
OpenChanMsg: fmsg.msg,
}
if !f.cfg.OpenChannelPredicate.Accept(chanReq) {
f.failFundingFlow(
fmsg.peer, fmsg.msg.PendingChannelID,
fmt.Errorf("open channel request rejected"),
)
return return
} }
+7
View File
@@ -23,6 +23,7 @@ import (
"github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/chanacceptor"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/discovery" "github.com/lightningnetwork/lnd/discovery"
"github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/htlcswitch"
@@ -281,6 +282,8 @@ func createTestFundingManager(t *testing.T, privKey *btcec.PrivateKey,
var chanIDSeed [32]byte var chanIDSeed [32]byte
chainedAcceptor := chanacceptor.NewChainedAcceptor()
fundingCfg := fundingConfig{ fundingCfg := fundingConfig{
IDKey: privKey.PubKey(), IDKey: privKey.PubKey(),
Wallet: lnw, Wallet: lnw,
@@ -364,6 +367,7 @@ func createTestFundingManager(t *testing.T, privKey *btcec.PrivateKey,
ReservationTimeout: 1 * time.Nanosecond, ReservationTimeout: 1 * time.Nanosecond,
MaxPendingChannels: DefaultMaxPendingChannels, MaxPendingChannels: DefaultMaxPendingChannels,
NotifyOpenChannelEvent: func(wire.OutPoint) {}, NotifyOpenChannelEvent: func(wire.OutPoint) {},
OpenChannelPredicate: chainedAcceptor,
} }
for _, op := range options { for _, op := range options {
@@ -414,6 +418,8 @@ func recreateAliceFundingManager(t *testing.T, alice *testNode) {
oldCfg := alice.fundingMgr.cfg oldCfg := alice.fundingMgr.cfg
chainedAcceptor := chanacceptor.NewChainedAcceptor()
f, err := newFundingManager(fundingConfig{ f, err := newFundingManager(fundingConfig{
IDKey: oldCfg.IDKey, IDKey: oldCfg.IDKey,
Wallet: oldCfg.Wallet, Wallet: oldCfg.Wallet,
@@ -458,6 +464,7 @@ func recreateAliceFundingManager(t *testing.T, alice *testNode) {
}, },
ZombieSweeperInterval: oldCfg.ZombieSweeperInterval, ZombieSweeperInterval: oldCfg.ZombieSweeperInterval,
ReservationTimeout: oldCfg.ReservationTimeout, ReservationTimeout: oldCfg.ReservationTimeout,
OpenChannelPredicate: chainedAcceptor,
}) })
if err != nil { if err != nil {
t.Fatalf("failed recreating aliceFundingManager: %v", err) t.Fatalf("failed recreating aliceFundingManager: %v", err)
+6 -2
View File
@@ -43,6 +43,7 @@ import (
"github.com/lightningnetwork/lnd/autopilot" "github.com/lightningnetwork/lnd/autopilot"
"github.com/lightningnetwork/lnd/build" "github.com/lightningnetwork/lnd/build"
"github.com/lightningnetwork/lnd/chanacceptor"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/lncfg"
@@ -488,11 +489,14 @@ func Main(lisCfg ListenerCfg) error {
} }
} }
// Initialize the ChainedAcceptor.
chainedAcceptor := chanacceptor.NewChainedAcceptor()
// Set up the core server which will listen for incoming peer // Set up the core server which will listen for incoming peer
// connections. // connections.
server, err := newServer( server, err := newServer(
cfg.Listeners, chanDB, towerClientDB, activeChainControl, cfg.Listeners, chanDB, towerClientDB, activeChainControl,
idPrivKey, walletInitParams.ChansToRestore, idPrivKey, walletInitParams.ChansToRestore, chainedAcceptor,
) )
if err != nil { if err != nil {
err := fmt.Errorf("Unable to create server: %v", err) err := fmt.Errorf("Unable to create server: %v", err)
@@ -547,7 +551,7 @@ func Main(lisCfg ListenerCfg) error {
rpcServer, err := newRPCServer( rpcServer, err := newRPCServer(
server, macaroonService, cfg.SubRPCServers, restDialOpts, server, macaroonService, cfg.SubRPCServers, restDialOpts,
restProxyDest, atplManager, server.invoices, tower, tlsCfg, restProxyDest, atplManager, server.invoices, tower, tlsCfg,
rpcListeners, rpcListeners, chainedAcceptor,
) )
if err != nil { if err != nil {
err := fmt.Errorf("Unable to create RPC server: %v", err) err := fmt.Errorf("Unable to create RPC server: %v", err)
+940 -647
View File
File diff suppressed because it is too large. Load diff
+61
View File
@@ -430,6 +430,15 @@ service Lightning {
*/ */
rpc OpenChannel (OpenChannelRequest) returns (stream OpenStatusUpdate); rpc OpenChannel (OpenChannelRequest) returns (stream OpenStatusUpdate);
/**
ChannelAcceptor dispatches a bi-directional streaming RPC in which
OpenChannel requests are sent to the client and the client responds with
a boolean that tells LND whether or not to accept the channel. This allows
node operators to specify their own criteria for accepting inbound channels
through a single persistent connection.
*/
rpc ChannelAcceptor (stream ChannelAcceptResponse) returns (stream ChannelAcceptRequest);
/** lncli: `closechannel` /** lncli: `closechannel`
CloseChannel attempts to close an active channel identified by its channel CloseChannel attempts to close an active channel identified by its channel
outpoint (ChannelPoint). The actions of this method can additionally be outpoint (ChannelPoint). The actions of this method can additionally be
@@ -912,6 +921,58 @@ message SendToRouteRequest {
Route route = 4; Route route = 4;
} }
message ChannelAcceptRequest {
/// The pubkey of the node that wishes to open an inbound channel.
bytes node_pubkey = 1;
/// The hash of the genesis block that the proposed channel resides in.
bytes chain_hash = 2;
/// The pending channel id.
bytes pending_chan_id = 3;
/// The funding amount in satoshis that initiator wishes to use in the channel.
uint64 funding_amt = 4;
/// The push amount of the proposed channel in millisatoshis.
uint64 push_amt = 5;
/// The dust limit of the initiator's commitment tx.
uint64 dust_limit = 6;
/// The maximum amount of coins in millisatoshis that can be pending in this channel.
uint64 max_value_in_flight = 7;
/// The minimum amount of satoshis the initiator requires us to have at all times.
uint64 channel_reserve = 8;
/// The smallest HTLC in millisatoshis that the initiator will accept.
uint64 min_htlc = 9;
/// The initial fee rate that the initiator suggests for both commitment transactions.
uint64 fee_per_kw = 10;
/**
The number of blocks to use for the relative time lock in the pay-to-self output
of both commitment transactions.
*/
uint32 csv_delay = 11;
/// The total number of incoming HTLC's that the initiator will accept.
uint32 max_accepted_htlcs = 12;
/// A bit-field which the initiator uses to specify proposed channel behavior.
uint32 channel_flags = 13;
}
message ChannelAcceptResponse {
/// Whether or not the client accepts the channel.
bool accept = 1;
/// The pending channel id to which this response applies.
bytes pending_chan_id = 2;
}
message ChannelPoint { message ChannelPoint {
oneof funding_txid { oneof funding_txid {
/// Txid of the funding transaction /// Txid of the funding transaction
+70
View File
@@ -1657,6 +1657,76 @@
} }
} }
}, },
"lnrpcChannelAcceptRequest": {
"type": "object",
"properties": {
"node_pubkey": {
"type": "string",
"format": "byte",
"description": "/ The pubkey of the node that wishes to open an inbound channel."
},
"chain_hash": {
"type": "string",
"format": "byte",
"description": "/ The hash of the genesis block that the proposed channel resides in."
},
"pending_chan_id": {
"type": "string",
"format": "byte",
"description": "/ The pending channel id."
},
"funding_amt": {
"type": "string",
"format": "uint64",
"description": "/ The funding amount in satoshis that initiator wishes to use in the channel."
},
"push_amt": {
"type": "string",
"format": "uint64",
"description": "/ The push amount of the proposed channel in millisatoshis."
},
"dust_limit": {
"type": "string",
"format": "uint64",
"description": "/ The dust limit of the initiator's commitment tx."
},
"max_value_in_flight": {
"type": "string",
"format": "uint64",
"description": "/ The maximum amount of coins in millisatoshis that can be pending in this channel."
},
"channel_reserve": {
"type": "string",
"format": "uint64",
"description": "/ The minimum amount of satoshis the initiator requires us to have at all times."
},
"min_htlc": {
"type": "string",
"format": "uint64",
"description": "/ The smallest HTLC in millisatoshis that the initiator will accept."
},
"fee_per_kw": {
"type": "string",
"format": "uint64",
"description": "/ The initial fee rate that the initiator suggests for both commitment transactions."
},
"csv_delay": {
"type": "integer",
"format": "int64",
"description": "*\nThe number of blocks to use for the relative time lock in the pay-to-self output\nof both commitment transactions."
},
"max_accepted_htlcs": {
"type": "integer",
"format": "int64",
"description": "/ The total number of incoming HTLC's that the initiator will accept."
},
"channel_flags": {
"type": "integer",
"format": "int64",
"description": "/ A bit-field which the initiator uses to specify proposed channel behavior."
}
}
},
"lnrpcChannelBackup": { "lnrpcChannelBackup": {
"type": "object", "type": "object",
"properties": { "properties": {
+186 -1
View File
@@ -16,6 +16,7 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/lightningnetwork/lnd/chanacceptor"
"github.com/lightningnetwork/lnd/lnrpc/routerrpc" "github.com/lightningnetwork/lnd/lnrpc/routerrpc"
"github.com/lightningnetwork/lnd/routing/route" "github.com/lightningnetwork/lnd/routing/route"
"github.com/lightningnetwork/lnd/tlv" "github.com/lightningnetwork/lnd/tlv"
@@ -76,6 +77,12 @@ var (
// It is set to the value under the Bitcoin chain as default. // It is set to the value under the Bitcoin chain as default.
MaxPaymentMSat = maxBtcPaymentMSat MaxPaymentMSat = maxBtcPaymentMSat
// defaultAcceptorTimeout is the time after which an RPCAcceptor will time
// out and return false if it hasn't yet received a response.
//
// TODO: Make this configurable
defaultAcceptorTimeout = 15 * time.Second
// readPermissions is a slice of all entities that allow read // readPermissions is a slice of all entities that allow read
// permissions for authorization purposes, all lowercase. // permissions for authorization purposes, all lowercase.
readPermissions = []bakery.Op{ readPermissions = []bakery.Op{
@@ -382,6 +389,13 @@ func mainRPCServerPermissions() map[string][]bakery.Op {
Entity: "offchain", Entity: "offchain",
Action: "read", Action: "read",
}}, }},
"/lnrpc.Lightning/ChannelAcceptor": {{
Entity: "onchain",
Action: "write",
}, {
Entity: "offchain",
Action: "write",
}},
} }
} }
@@ -430,6 +444,10 @@ type rpcServer struct {
// rpc sub server. // rpc sub server.
routerBackend *routerrpc.RouterBackend routerBackend *routerrpc.RouterBackend
// chanPredicate is used in the bidirectional ChannelAcceptor streaming
// method.
chanPredicate *chanacceptor.ChainedAcceptor
quit chan struct{} quit chan struct{}
} }
@@ -446,7 +464,8 @@ func newRPCServer(s *server, macService *macaroons.Service,
subServerCgs *subRPCServerConfigs, restDialOpts []grpc.DialOption, subServerCgs *subRPCServerConfigs, restDialOpts []grpc.DialOption,
restProxyDest string, atpl *autopilot.Manager, restProxyDest string, atpl *autopilot.Manager,
invoiceRegistry *invoices.InvoiceRegistry, tower *watchtower.Standalone, invoiceRegistry *invoices.InvoiceRegistry, tower *watchtower.Standalone,
tlsCfg *tls.Config, getListeners rpcListeners) (*rpcServer, error) { tlsCfg *tls.Config, getListeners rpcListeners,
chanPredicate *chanacceptor.ChainedAcceptor) (*rpcServer, error) {
// Set up router rpc backend. // Set up router rpc backend.
channelGraph := s.chanDB.ChannelGraph() channelGraph := s.chanDB.ChannelGraph()
@@ -601,6 +620,7 @@ func newRPCServer(s *server, macService *macaroons.Service,
grpcServer: grpcServer, grpcServer: grpcServer,
server: s, server: s,
routerBackend: routerBackend, routerBackend: routerBackend,
chanPredicate: chanPredicate,
quit: make(chan struct{}, 1), quit: make(chan struct{}, 1),
} }
lnrpc.RegisterLightningServer(grpcServer, rootRPCServer) lnrpc.RegisterLightningServer(grpcServer, rootRPCServer)
@@ -5051,3 +5071,168 @@ func (r *rpcServer) SubscribeChannelBackups(req *lnrpc.ChannelBackupSubscription
} }
} }
} }
// chanAcceptInfo is used in the ChannelAcceptor bidirectional stream and
// encapsulates the request information sent from the RPCAcceptor to the
// RPCServer.
type chanAcceptInfo struct {
chanReq *chanacceptor.ChannelAcceptRequest
responseChan chan bool
}
// ChannelAcceptor dispatches a bi-directional streaming RPC in which
// OpenChannel requests are sent to the client and the client responds with
// a boolean that tells LND whether or not to accept the channel. This allows
// node operators to specify their own criteria for accepting inbound channels
// through a single persistent connection.
func (r *rpcServer) ChannelAcceptor(stream lnrpc.Lightning_ChannelAcceptorServer) error {
chainedAcceptor := r.chanPredicate
// Create two channels to handle requests and responses respectively.
newRequests := make(chan *chanAcceptInfo)
responses := make(chan lnrpc.ChannelAcceptResponse)
// Define a quit channel that will be used to signal to the RPCAcceptor's
// closure whether the stream still exists.
quit := make(chan struct{})
defer close(quit)
// demultiplexReq is a closure that will be passed to the RPCAcceptor and
// acts as an intermediary between the RPCAcceptor and the RPCServer.
demultiplexReq := func(req *chanacceptor.ChannelAcceptRequest) bool {
respChan := make(chan bool, 1)
newRequest := &chanAcceptInfo{
chanReq: req,
responseChan: respChan,
}
// timeout is the time after which ChannelAcceptRequests expire.
timeout := time.After(defaultAcceptorTimeout)
// Send the request to the newRequests channel.
select {
case newRequests <- newRequest:
case <-timeout:
rpcsLog.Errorf("RPCAcceptor returned false - reached timeout of %d",
defaultAcceptorTimeout)
return false
case <-quit:
return false
case <-r.quit:
return false
}
// Receive the response and return it. If no response has been received
// in defaultAcceptorTimeout, then return false.
select {
case resp := <-respChan:
return resp
case <-timeout:
rpcsLog.Errorf("RPCAcceptor returned false - reached timeout of %d",
defaultAcceptorTimeout)
return false
case <-quit:
return false
case <-r.quit:
return false
}
}
// Create a new RPCAcceptor via the NewRPCAcceptor method.
rpcAcceptor := chanacceptor.NewRPCAcceptor(demultiplexReq)
// Add the RPCAcceptor to the ChainedAcceptor and defer its removal.
id := chainedAcceptor.AddAcceptor(rpcAcceptor)
defer chainedAcceptor.RemoveAcceptor(id)
// errChan is used by the receive loop to signal any errors that occur
// during reading from the stream. This is primarily used to shutdown the
// send loop in the case of an RPC client disconnecting.
errChan := make(chan error, 1)
// We need to have the stream.Recv() in a goroutine since the call is
// blocking and would prevent us from sending more ChannelAcceptRequests to
// the RPC client.
go func() {
for {
resp, err := stream.Recv()
if err != nil {
errChan <- err
return
}
var pendingID [32]byte
copy(pendingID[:], resp.PendingChanId)
openChanResp := lnrpc.ChannelAcceptResponse{
Accept: resp.Accept,
PendingChanId: pendingID[:],
}
// Now that we have the response from the RPC client, send it to
// the responses chan.
select {
case responses <- openChanResp:
case <-quit:
return
case <-r.quit:
return
}
}
}()
acceptRequests := make(map[[32]byte]chan bool)
for {
select {
case newRequest := <-newRequests:
req := newRequest.chanReq
pendingChanID := req.OpenChanMsg.PendingChannelID
acceptRequests[pendingChanID] = newRequest.responseChan
// A ChannelAcceptRequest has been received, send it to the client.
chanAcceptReq := &lnrpc.ChannelAcceptRequest{
NodePubkey: req.Node.SerializeCompressed(),
ChainHash: req.OpenChanMsg.ChainHash[:],
PendingChanId: req.OpenChanMsg.PendingChannelID[:],
FundingAmt: uint64(req.OpenChanMsg.FundingAmount),
PushAmt: uint64(req.OpenChanMsg.PushAmount),
DustLimit: uint64(req.OpenChanMsg.DustLimit),
MaxValueInFlight: uint64(req.OpenChanMsg.MaxValueInFlight),
ChannelReserve: uint64(req.OpenChanMsg.ChannelReserve),
MinHtlc: uint64(req.OpenChanMsg.HtlcMinimum),
FeePerKw: uint64(req.OpenChanMsg.FeePerKiloWeight),
CsvDelay: uint32(req.OpenChanMsg.CsvDelay),
MaxAcceptedHtlcs: uint32(req.OpenChanMsg.MaxAcceptedHTLCs),
ChannelFlags: uint32(req.OpenChanMsg.ChannelFlags),
}
if err := stream.Send(chanAcceptReq); err != nil {
return err
}
case resp := <-responses:
// Look up the appropriate channel to send on given the pending ID.
// If a channel is found, send the response over it.
var pendingID [32]byte
copy(pendingID[:], resp.PendingChanId)
respChan, ok := acceptRequests[pendingID]
if !ok {
continue
}
// Send the response boolean over the buffered response channel.
respChan <- resp.Accept
// Delete the channel from the acceptRequests map.
delete(acceptRequests, pendingID)
case err := <-errChan:
rpcsLog.Errorf("Received an error: %v, shutting down", err)
return err
case <-r.quit:
return fmt.Errorf("RPC server is shutting down")
}
}
}
+5 -1
View File
@@ -28,6 +28,7 @@ import (
sphinx "github.com/lightningnetwork/lightning-onion" sphinx "github.com/lightningnetwork/lightning-onion"
"github.com/lightningnetwork/lnd/autopilot" "github.com/lightningnetwork/lnd/autopilot"
"github.com/lightningnetwork/lnd/brontide" "github.com/lightningnetwork/lnd/brontide"
"github.com/lightningnetwork/lnd/chanacceptor"
"github.com/lightningnetwork/lnd/chanbackup" "github.com/lightningnetwork/lnd/chanbackup"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/channelnotifier" "github.com/lightningnetwork/lnd/channelnotifier"
@@ -297,7 +298,8 @@ func noiseDial(idPriv *btcec.PrivateKey) func(net.Addr) (net.Conn, error) {
func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB,
towerClientDB *wtdb.ClientDB, cc *chainControl, towerClientDB *wtdb.ClientDB, cc *chainControl,
privKey *btcec.PrivateKey, privKey *btcec.PrivateKey,
chansToRestore walletunlocker.ChannelsToRecover) (*server, error) { chansToRestore walletunlocker.ChannelsToRecover,
chanPredicate chanacceptor.ChannelAcceptor) (*server, error) {
var err error var err error
@@ -908,6 +910,7 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB,
if _, err := rand.Read(chanIDSeed[:]); err != nil { if _, err := rand.Read(chanIDSeed[:]); err != nil {
return nil, err return nil, err
} }
s.fundingMgr, err = newFundingManager(fundingConfig{ s.fundingMgr, err = newFundingManager(fundingConfig{
IDKey: privKey.PubKey(), IDKey: privKey.PubKey(),
Wallet: cc.wallet, Wallet: cc.wallet,
@@ -1069,6 +1072,7 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB,
MaxPendingChannels: cfg.MaxPendingChannels, MaxPendingChannels: cfg.MaxPendingChannels,
RejectPush: cfg.RejectPush, RejectPush: cfg.RejectPush,
NotifyOpenChannelEvent: s.channelNotifier.NotifyOpenChannelEvent, NotifyOpenChannelEvent: s.channelNotifier.NotifyOpenChannelEvent,
OpenChannelPredicate: chanPredicate,
}) })
if err != nil { if err != nil {
return nil, err return nil, err