lnrpc: add BatchOpenChannel RPC
This commit is contained in:
1 parent
37d0d651c4
commit
c5c956bbef
8 files changed
+649
-262
No files matched your search
+87
-1
@@ -52,6 +52,7 @@ import (
|
||||
"github.com/lightningnetwork/lnd/lnrpc"
|
||||
"github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
|
||||
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
|
||||
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
|
||||
"github.com/lightningnetwork/lnd/lntypes"
|
||||
"github.com/lightningnetwork/lnd/lnwallet"
|
||||
"github.com/lightningnetwork/lnd/lnwallet/btcwallet"
|
||||
@@ -300,6 +301,13 @@ func MainRPCServerPermissions() map[string][]bakery.Op {
|
||||
Entity: "offchain",
|
||||
Action: "write",
|
||||
}},
|
||||
"/lnrpc.Lightning/BatchOpenChannel": {{
|
||||
Entity: "onchain",
|
||||
Action: "write",
|
||||
}, {
|
||||
Entity: "offchain",
|
||||
Action: "write",
|
||||
}},
|
||||
"/lnrpc.Lightning/OpenChannelSync": {{
|
||||
Entity: "onchain",
|
||||
Action: "write",
|
||||
@@ -1788,7 +1796,7 @@ func (r *rpcServer) canOpenChannel() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// praseOpenChannelReq parses an OpenChannelRequest message into an InitFundingMsg
|
||||
// parseOpenChannelReq parses an OpenChannelRequest message into an InitFundingMsg
|
||||
// struct. The logic is abstracted so that it can be shared between OpenChannel
|
||||
// and OpenChannelSync.
|
||||
func (r *rpcServer) parseOpenChannelReq(in *lnrpc.OpenChannelRequest,
|
||||
@@ -2107,6 +2115,84 @@ func (r *rpcServer) OpenChannelSync(ctx context.Context,
|
||||
}
|
||||
}
|
||||
|
||||
// BatchOpenChannel attempts to open multiple single-funded channels in a
|
||||
// single transaction in an atomic way. This means either all channel open
|
||||
// requests succeed at once or all attempts are aborted if any of them fail.
|
||||
// This is the safer variant of using PSBTs to manually fund a batch of
|
||||
// channels through the OpenChannel RPC.
|
||||
func (r *rpcServer) BatchOpenChannel(ctx context.Context,
|
||||
in *lnrpc.BatchOpenChannelRequest) (*lnrpc.BatchOpenChannelResponse,
|
||||
error) {
|
||||
|
||||
if err := r.canOpenChannel(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We need the wallet kit server to do the heavy lifting on the PSBT
|
||||
// part. If we didn't rely on re-using the wallet kit server's logic we
|
||||
// would need to re-implement everything here. Since we deliver lnd with
|
||||
// the wallet kit server enabled by default we can assume it's okay to
|
||||
// make this functionality dependent on that server being active.
|
||||
var walletKitServer walletrpc.WalletKitServer
|
||||
for _, subServer := range r.subServers {
|
||||
if subServer.Name() == walletrpc.SubServerName {
|
||||
walletKitServer = subServer.(walletrpc.WalletKitServer)
|
||||
}
|
||||
}
|
||||
if walletKitServer == nil {
|
||||
return nil, fmt.Errorf("batch channel open is only possible " +
|
||||
"if walletrpc subserver is active")
|
||||
}
|
||||
|
||||
rpcsLog.Debugf("[batchopenchannel] request to open batch of %d "+
|
||||
"channels", len(in.Channels))
|
||||
|
||||
// Make sure there is at least one channel to open. We could say we want
|
||||
// at least two channels for a batch. But maybe it's nice if developers
|
||||
// can use the same API for a single channel as well as a batch of
|
||||
// channels.
|
||||
if len(in.Channels) == 0 {
|
||||
return nil, fmt.Errorf("specify at least one channel")
|
||||
}
|
||||
|
||||
// In case we remove a pending channel from the database, we need to set
|
||||
// a close height, so we'll just use the current best known height.
|
||||
_, bestHeight, err := r.server.cc.ChainIO.GetBestBlock()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error fetching best block: %v", err)
|
||||
}
|
||||
|
||||
// So far everything looks good and we can now start the heavy lifting
|
||||
// that's done in the funding package.
|
||||
requestParser := func(req *lnrpc.OpenChannelRequest) (
|
||||
*funding.InitFundingMsg, error) {
|
||||
|
||||
return r.parseOpenChannelReq(req, false)
|
||||
}
|
||||
channelAbandoner := func(point *wire.OutPoint) error {
|
||||
return r.abandonChan(point, uint32(bestHeight))
|
||||
}
|
||||
batcher := funding.NewBatcher(&funding.BatchConfig{
|
||||
RequestParser: requestParser,
|
||||
ChannelAbandoner: channelAbandoner,
|
||||
ChannelOpener: r.server.OpenChannel,
|
||||
WalletKitServer: walletKitServer,
|
||||
Wallet: r.server.cc.Wallet,
|
||||
NetParams: &r.server.cc.Wallet.Cfg.NetParams,
|
||||
Quit: r.quit,
|
||||
})
|
||||
rpcPoints, err := batcher.BatchFund(ctx, in)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("batch funding failed: %v", err)
|
||||
}
|
||||
|
||||
// Now all that's left to do is send back the response with the channel
|
||||
// points we created.
|
||||
return &lnrpc.BatchOpenChannelResponse{
|
||||
PendingChannels: rpcPoints,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CloseChannel attempts to close an active channel identified by its channel
|
||||
// point. The actions of this method can additionally be augmented to attempt
|
||||
// a force close after a timeout period in the case of an inactive peer.
|
||||
|
||||
Reference in new issue
Block a user