Skip to content
This repository has been archived by the owner on Jun 25, 2024. It is now read-only.

Commit

Permalink
Add sidecar acceptor option to lnd
Browse files Browse the repository at this point in the history
  • Loading branch information
orbitalturtle committed Dec 15, 2021
1 parent 04b66b6 commit dbb1fbb
Show file tree
Hide file tree
Showing 7 changed files with 250 additions and 4 deletions.
17 changes: 17 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const (
defaultChainSubDirname = "chain"
defaultGraphSubDirname = "graph"
defaultTowerSubDirname = "watchtower"
defaultPoolDirname = "pool"
defaultTLSCertFilename = "tls.cert"
defaultTLSKeyFilename = "tls.key"
defaultAdminMacFilename = "admin.macaroon"
Expand Down Expand Up @@ -188,6 +189,7 @@ var (
defaultDataDir = filepath.Join(DefaultLndDir, defaultDataDirname)
defaultLogDir = filepath.Join(DefaultLndDir, defaultLogDirname)

defaultPoolDir = filepath.Join(DefaultLndDir, defaultPoolDirname)
defaultTowerDir = filepath.Join(defaultDataDir, defaultTowerSubDirname)

defaultTLSCertPath = filepath.Join(DefaultLndDir, defaultTLSCertFilename)
Expand Down Expand Up @@ -241,6 +243,7 @@ type Config struct {
ReadMacPath string `long:"readonlymacaroonpath" description:"Path to write the read-only macaroon for lnd's RPC and REST services if it doesn't exist"`
InvoiceMacPath string `long:"invoicemacaroonpath" description:"Path to the invoice-only macaroon for lnd's RPC and REST services if it doesn't exist"`
LogDir string `long:"logdir" description:"Directory to log output."`
PoolDir string `long:"pooldir" description:"DIrectory to store sidecar ticket data."`
MaxLogFiles int `long:"maxlogfiles" description:"Maximum logfiles to keep (0 for no rotation)"`
MaxLogFileSize int `long:"maxlogfilesize" description:"Maximum logfile size in MB"`
AcceptorTimeout time.Duration `long:"acceptortimeout" description:"Time after which an RPCAcceptor will time out and return false if it hasn't yet received a response"`
Expand Down Expand Up @@ -368,6 +371,8 @@ type Config struct {

DustThreshold uint64 `long:"dust-threshold" description:"Sets the dust sum threshold in satoshis for a channel after which dust HTLC's will be failed."`

SidecarAcceptor bool `long:"sidecar-acceptor" description:"If true, we run a sidecar acceptor alongside lnd"`

Invoices *lncfg.Invoices `group:"invoices" namespace:"invoices"`

Routing *lncfg.Routing `group:"routing" namespace:"routing"`
Expand Down Expand Up @@ -532,6 +537,7 @@ func DefaultConfig() Config {
Watchtower: &lncfg.Watchtower{
TowerDir: defaultTowerDir,
},
PoolDir: defaultPoolDir,
HealthChecks: &lncfg.HealthCheckConfig{
ChainCheck: &lncfg.CheckConfig{
Interval: defaultChainInterval,
Expand Down Expand Up @@ -682,6 +688,7 @@ func ValidateConfig(cfg Config, usageMessage string,
cfg.TLSCertPath = filepath.Join(lndDir, defaultTLSCertFilename)
cfg.TLSKeyPath = filepath.Join(lndDir, defaultTLSKeyFilename)
cfg.LogDir = filepath.Join(lndDir, defaultLogDirname)
cfg.PoolDir = filepath.Join(lndDir, defaultPoolDirname)

// If the watchtower's directory is set to the default, i.e. the
// user has not requested a different location, we'll move the
Expand All @@ -690,6 +697,11 @@ func ValidateConfig(cfg Config, usageMessage string,
cfg.Watchtower.TowerDir =
filepath.Join(cfg.DataDir, defaultTowerSubDirname)
}

if cfg.PoolDir == defaultPoolDir {
cfg.PoolDir =
filepath.Join(cfg.DataDir, defaultPoolDirname)
}
}

funcName := "loadConfig"
Expand Down Expand Up @@ -769,6 +781,7 @@ func ValidateConfig(cfg Config, usageMessage string,
cfg.ReadMacPath = CleanAndExpandPath(cfg.ReadMacPath)
cfg.InvoiceMacPath = CleanAndExpandPath(cfg.InvoiceMacPath)
cfg.LogDir = CleanAndExpandPath(cfg.LogDir)
cfg.PoolDir = CleanAndExpandPath(cfg.PoolDir)
cfg.BtcdMode.Dir = CleanAndExpandPath(cfg.BtcdMode.Dir)
cfg.LtcdMode.Dir = CleanAndExpandPath(cfg.LtcdMode.Dir)
cfg.BitcoindMode.Dir = CleanAndExpandPath(cfg.BitcoindMode.Dir)
Expand Down Expand Up @@ -1301,6 +1314,10 @@ func ValidateConfig(cfg Config, usageMessage string,
lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name),
)

cfg.PoolDir = filepath.Join(cfg.PoolDir,
cfg.registeredChains.PrimaryChain().String(),
lncfg.NormalizeNetwork(cfg.ActiveNetParams.Name))

// We need to make sure the default network directory exists for when we
// try to create our default macaroons there.
if err := makeDirectory(cfg.networkDir); err != nil {
Expand Down
29 changes: 25 additions & 4 deletions lnd.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,22 @@ const (
//
// NOTE: This should only be called after the RPCListener has signaled it is
// ready.
func AdminAuthOptions(cfg *Config, skipMacaroons bool) ([]grpc.DialOption, error) {
creds, err := credentials.NewClientTLSFromFile(cfg.TLSCertPath, "")
if err != nil {
return nil, fmt.Errorf("unable to read TLS cert: %v", err)
func AdminAuthOptions(cfg *Config, skipMacaroons, insecure bool) ([]grpc.DialOption, error) {
var (
creds credentials.TransportCredentials
err error
)


if insecure {
creds = credentials.NewTLS(&tls.Config{
InsecureSkipVerify: true, // nolint:gosec
})
} else {
creds, err = credentials.NewClientTLSFromFile(cfg.TLSCertPath, "")
if err != nil {
return nil, fmt.Errorf("unable to read TLS cert: %v", err)
}
}

// Create a dial options array.
Expand Down Expand Up @@ -658,6 +670,15 @@ func Main(cfg *Config, lisCfg ListenerCfg, implCfg *ImplementationCfg,
ltndLog.Infof("Chain backend is fully synced (end_height=%v)!",
bestHeight)

if cfg.SidecarAcceptor {
acceptor, err := StartSidecarAcceptor(cfg)
if err != nil {
ltndLog.Error(err)
return err
}
server.sidecarAcceptor = acceptor
}

// With all the relevant chains initialized, we can finally start the
// server itself.
if err := server.Start(); err != nil {
Expand Down
4 changes: 4 additions & 0 deletions rpcperms/interceptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ var (
// before we can check macaroons, so we whitelist it.
"/lnrpc.State/SubscribeState": {},
"/lnrpc.State/GetState": {},

// Let the register sidecar endpoint be unauthenticated
// so we can register tickets for the user.
"/lnrpc.Lightning/RegisterSidecar": {},
}
)

Expand Down
50 changes: 50 additions & 0 deletions rpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"github.com/btcsuite/btcwallet/wallet/txauthor"
"github.com/davecgh/go-spew/spew"
proxy "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/lightninglabs/pool/sidecar"
"github.com/lightningnetwork/lnd/autopilot"
"github.com/lightningnetwork/lnd/build"
"github.com/lightningnetwork/lnd/chainreg"
Expand Down Expand Up @@ -7402,3 +7403,52 @@ func (r *rpcServer) SubscribeCustomMessages(req *lnrpc.SubscribeCustomMessagesRe
}
}
}


// RegisterSidecar is step 2/4 of the sidecar negotiation between the provider
// (the trader submitting the bid order) and the recipient (the trader receiving
// the sidecar channel).
// This step must be run by the recipient. The result is a sidecar ticket with
// the recipient's node information and channel funding multisig pubkey filled
// in. The ticket returned by this call will have the state "registered".
func (r *rpcServer) RegisterSidecar(ctx context.Context,
req *lnrpc.RegisterSidecarRequest) (*lnrpc.SidecarTicket, error) {

if r.server.sidecarAcceptor == nil {
return nil, errors.New("Cannot register sidecar until sidecar"+
" acceptor is set up. Make sure sidecar-acceptor flag"+
" is set, or wait until node is fully synced.")
}

// Parse the ticket from its string encoded representation.
ticket, err := sidecar.DecodeString(req.Ticket)
if err != nil {
return nil, fmt.Errorf("error decoding ticket: %v", err)
}

// The sidecar acceptor will add all required information and add the
// ticket to our DB.
registeredTicket, err := r.server.sidecarAcceptor.RegisterSidecar(
ctx, *ticket,
)
if err != nil {
return nil, err
}

// At this point, we'll now check if the ticket specifies that
// automated negotiation is to be sued, if so then we'll hand things
// off to the sidecar acceptor to finish the process.
if registeredTicket.Offer.Auto {
err := r.server.sidecarAcceptor.AutoAcceptSidecar(registeredTicket)
if err != nil {
return nil, fmt.Errorf("unable to start ticket auto "+
"negotiation: %v", err)
}
}

ticketStr, err := sidecar.EncodeToString(registeredTicket)
if err != nil {
return nil, err
}
return &lnrpc.SidecarTicket{Ticket: ticketStr}, nil
}
2 changes: 2 additions & 0 deletions sample-lnd.conf
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,8 @@
; intelligence services.
; color=#3399FF

; Set if we want to spin up a sidecar acceptor.
; sidecar-acceptor=true

[Bitcoin]

Expand Down
27 changes: 27 additions & 0 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
"github.com/go-errors/errors"
"github.com/lightninglabs/pool/acceptor"
sphinx "github.com/lightningnetwork/lightning-onion"
"github.com/lightningnetwork/lnd/autopilot"
"github.com/lightningnetwork/lnd/brontide"
Expand Down Expand Up @@ -283,6 +284,8 @@ type server struct {

readPool *pool.Read

sidecarAcceptor *acceptor.SidecarAcceptor

// featureMgr dispatches feature vectors for various contexts within the
// daemon.
featureMgr *feature.Manager
Expand Down Expand Up @@ -1771,6 +1774,21 @@ func (s *server) Start() error {
}
cleanup = cleanup.add(s.authGossiper.Stop)

if s.cfg.SidecarAcceptor {
if err := s.sidecarAcceptor.FundingManager.Start(); err != nil {
startErr = err
return
}
cleanup = cleanup.add(s.sidecarAcceptor.FundingManager.Stop)

var testErrChan = make(chan error)
if err := s.sidecarAcceptor.Start(testErrChan); err != nil {
startErr = err
return
}
cleanup = cleanup.add(s.sidecarAcceptor.Stop)
}

if err := s.chanRouter.Start(); err != nil {
startErr = err
return
Expand Down Expand Up @@ -2059,6 +2077,15 @@ func (s *server) Stop() error {
}
s.chanEventStore.Stop()
s.missionControl.StopStoreTicker()
if s.cfg.SidecarAcceptor {
if err := s.sidecarAcceptor.FundingManager.Stop(); err != nil {
srvrLog.Warnf("Unable to stop funding manager: %v", err)
}

if err := s.sidecarAcceptor.Stop(); err != nil {
srvrLog.Warnf("Unable to stop sidecarAcceptor: %v", err)
}
}

// Disconnect from each active peers to ensure that
// peerTerminationWatchers signal completion to each peer.
Expand Down
125 changes: 125 additions & 0 deletions start_sidecar.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package lnd

import (
"context"
"errors"
"fmt"
"time"

"github.com/btcsuite/btcd/btcec"
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/pool/acceptor"
"github.com/lightninglabs/pool/auctioneer"
"github.com/lightninglabs/pool/clientdb"
"github.com/lightninglabs/pool/funding"
"github.com/lightninglabs/pool/order"
"github.com/lightningnetwork/lnd/lnrpc"

"google.golang.org/grpc"
)

func StartSidecarAcceptor(cfg *Config) (*acceptor.SidecarAcceptor, error) {
opts, err := AdminAuthOptions(cfg, false, true)
if err != nil {
return nil, err
}

host := cfg.RPCListeners[0].String()
conn, err := grpc.Dial(host, opts...)
if err != nil {
return nil, fmt.Errorf("unable to connect to RPC server: %v", err)
}

network := lndclient.Network(cfg.ActiveNetParams.Params.Name)
if network == "testnet3" {
network = "testnet"
}

ctxc, cancel := context.WithCancel(context.Background())
defer cancel()

lndServices, err := lndclient.NewLndServices(&lndclient.LndServicesConfig{
LndAddress: host,
Network: network,
TLSPath: cfg.TLSCertPath,
CustomMacaroonPath: cfg.AdminMacPath,
BlockUntilChainSynced: false,
BlockUntilUnlocked: true,
CallerCtx: ctxc,
})
if err != nil {
return nil, err
}

db, err := clientdb.New(cfg.PoolDir, clientdb.DBFilename)
if err != nil {
return nil, err
}

// Parse our lnd node's public key.
nodePubKey, err := btcec.ParsePubKey(
lndServices.NodePubkey[:], btcec.S256(),
)
if err != nil {
return nil, fmt.Errorf("unable to parse node pubkey: %v", err)
}

lnClient := lnrpc.NewLightningClient(conn)

channelAcceptor := acceptor.NewChannelAcceptor(lndServices.Client)
fundingManager := funding.NewManager(&funding.ManagerConfig{
DB: db,
WalletKit: lndServices.WalletKit,
LightningClient: lndServices.Client,
SignerClient: lndServices.Signer,
BaseClient: lnClient,
NodePubKey: nodePubKey,
BatchStepTimeout: order.DefaultBatchStepTimeout,
NewNodesOnly: false,
NotifyShimCreated: channelAcceptor.ShimRegistered,
})

var auctionServer string
// Use the default addresses for mainnet and testnet auction servers.
switch {
case cfg.Bitcoin.MainNet:
auctionServer = "pool.lightning.finance:12010"
case cfg.Bitcoin.TestNet3:
auctionServer = "test.pool.lightning.finance:12010"
default:
return nil, errors.New("no auction server address specified")
}

clientCfg := &auctioneer.Config{
ServerAddress: auctionServer,
ProxyAddress: "",
Insecure: false,
TLSPathServer: "",
DialOpts: make([]grpc.DialOption, 0),
Signer: lndServices.Signer,
MinBackoff: time.Millisecond * 100,
MaxBackoff: time.Minute,
BatchSource: db,
BatchCleaner: fundingManager,
GenUserAgent: func(ctx context.Context) string {
return acceptor.UserAgent(acceptor.InitiatorFromContext(ctx))
},
}

acceptor := acceptor.NewSidecarAcceptor(&acceptor.SidecarAcceptorConfig{
SidecarDB: db,
AcctDB: &acceptor.AccountStore{DB: db},
BaseClient: lnClient,
Acceptor: channelAcceptor,
Signer: lndServices.Signer,
Wallet: lndServices.WalletKit,
NodePubKey: nodePubKey,
ClientCfg: *clientCfg,
FundingManager: fundingManager,
FetchSidecarBid: db.SidecarBidTemplate,
})

acceptor.FundingManager = fundingManager

return acceptor, nil
}

0 comments on commit dbb1fbb

Please sign in to comment.