Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion loopd/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"sync"
"sync/atomic"

"github.com/coreos/bbolt"
proxy "github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/lightninglabs/lndclient"
"github.com/lightninglabs/loop"
Expand Down Expand Up @@ -128,6 +129,13 @@ func (d *Daemon) Start() error {
// and error handlers. If this fails, then nothing has been started yet
// and we can just return the error.
err = d.initialize()
if errors.Is(err, bbolt.ErrTimeout) {
// We're trying to be started as a standalone Loop daemon, most
// likely LiT is already running and blocking the DB
return fmt.Errorf("%v: make sure no other loop daemon "+
"process (standalone or embedded in "+
"lightning-terminal) is running", err)
}
if err != nil {
return err
}
Expand Down Expand Up @@ -168,7 +176,14 @@ func (d *Daemon) StartAsSubserver(lndGrpc *lndclient.GrpcLndServices) error {
// the swap server client, the RPC server instance and our main swap
// handlers. If this fails, then nothing has been started yet and we can
// just return the error.
return d.initialize()
err := d.initialize()
if errors.Is(err, bbolt.ErrTimeout) {
// We're trying to be started inside LiT so there most likely is
// another standalone Loop process blocking the DB.
return fmt.Errorf("%v: make sure no other loop daemon "+
"process is running", err)
}
return err
}

// ValidateMacaroon extracts the macaroon from the context's gRPC metadata,
Expand Down
15 changes: 9 additions & 6 deletions loopd/macaroons.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import (
"fmt"
"io/ioutil"
"os"
"time"

"github.com/coreos/bbolt"
"github.com/lightninglabs/loop/loopdb"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/macaroons"
"google.golang.org/grpc"
Expand All @@ -17,10 +18,6 @@ const (
// loopMacaroonLocation is the value we use for the loopd macaroons'
// "Location" field when baking them.
loopMacaroonLocation = "loop"

// macDatabaseOpenTimeout is how long we wait for acquiring the lock on
// the macaroon database before we give up with an error.
macDatabaseOpenTimeout = time.Second * 5
)

var (
Expand Down Expand Up @@ -150,8 +147,14 @@ func (d *Daemon) startMacaroonService() error {
var err error
d.macaroonService, err = macaroons.NewService(
d.cfg.DataDir, loopMacaroonLocation, false,
macDatabaseOpenTimeout, macaroons.IPLockChecker,
loopdb.DefaultLoopDBTimeout, macaroons.IPLockChecker,
)
if err == bbolt.ErrTimeout {
return fmt.Errorf("%w: couldn't obtain exclusive lock on "+
"%s/%s, timed out after %v", bbolt.ErrTimeout,
d.cfg.DataDir, "macaroons.db",
loopdb.DefaultLoopDBTimeout)
}
if err != nil {
return fmt.Errorf("unable to set up macaroon authentication: "+
"%v", err)
Expand Down
24 changes: 20 additions & 4 deletions loopdb/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,18 @@ var (
keyLength = 33
)

// DefaultLoopOutHtlcConfirmations is the default number of confirmations we
// set for a loop out htlc.
const DefaultLoopOutHtlcConfirmations uint32 = 1
const (
// DefaultLoopOutHtlcConfirmations is the default number of
// confirmations we set for a loop out htlc.
DefaultLoopOutHtlcConfirmations uint32 = 1

// DefaultLoopDBTimeout is the default maximum time we wait for the
// Loop bbolt database to be opened. If the database is already opened
// by another process, the unique lock cannot be obtained. With the
// timeout we error out after the given time instead of just blocking
// for forever.
DefaultLoopDBTimeout = 5 * time.Second
)

// fileExists returns true if the file exists, and false otherwise.
func fileExists(path string) bool {
Expand Down Expand Up @@ -139,7 +148,14 @@ func NewBoltSwapStore(dbPath string, chainParams *chaincfg.Params) (
// Now that we know that path exists, we'll open up bolt, which
// implements our default swap store.
path := filepath.Join(dbPath, dbFileName)
bdb, err := bbolt.Open(path, 0600, nil)
bdb, err := bbolt.Open(path, 0600, &bbolt.Options{
Timeout: DefaultLoopDBTimeout,
})
if err == bbolt.ErrTimeout {
return nil, fmt.Errorf("%w: couldn't obtain exclusive lock on "+
"%s, timed out after %v", bbolt.ErrTimeout, path,
DefaultLoopDBTimeout)
}
if err != nil {
return nil, err
}
Expand Down
3 changes: 3 additions & 0 deletions release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@ This file tracks release notes for the loop client.
#### Breaking Changes

#### Bug Fixes
- Instead of just blocking for forever without any apparent reason if another
Loop daemon process is already running, we now exit with an error after 5
seconds if acquiring the unique lock on the Loop `bbolt` DB fails.