Skip to content

mobile: Add ServiceStatus for retrieving started up status of lnd - #4595

Open
hsjoberg wants to merge 1 commit into
lightningnetwork:masterfrom
hsjoberg:lndmobile-status
Open

mobile: Add ServiceStatus for retrieving started up status of lnd#4595
hsjoberg wants to merge 1 commit into
lightningnetwork:masterfrom
hsjoberg:lndmobile-status

Conversation

@hsjoberg

@hsjoberg hsjoberg commented Sep 4, 2020

Copy link
Copy Markdown
Contributor

This PR adds a new exported gomobile function ServiceStatus that can be used to figure out lnd's runtime state (as in whether it's considered started or not).
This is helpful in certain situations, see my comments below (#4595 (comment)).

Cheers

@cfromknecht cfromknecht left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hsjoberg thanks for the PR! no major comments, just one small nit about avoiding race conditions

Comment thread mobile/bindings.go Outdated

// LndStatus holds the current status of lnd.
type LndStatus struct {
// lndStarted tells whether the lnd is started or not.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it looks these fields could use a mutex, since it's modified and read concurrently

Comment thread mobile/bindings.go Outdated
}

func GetStatus(callback LndStatusCallback) {
callback.OnResponse(lndStatus.lndStarted, lndStatus.walletUnlocked)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the application going to poll by calling GetStatus from time to time?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bhandras No that's not what I had in mind -- rather call GetStatus once on app startup.
But sure an application could also possibly poll this one if needed.

@halseth

halseth commented Oct 9, 2020

Copy link
Copy Markdown
Contributor

@hsjoberg This is something we def want, but I'm hesitant to expose it as a mobile only API. Since we want this functionality also for LND in general, I think we rather should work on it as a "real" gRPC API (something I've started in the work with #3982)

@hsjoberg

hsjoberg commented Oct 9, 2020

Copy link
Copy Markdown
Contributor Author

@halseth What I'm trying to accomplish is checking the state of lnd regardless if a gRPC server is active or not. So using a gRPC server here defeats the purpose.

Compared to running as a daemon/in terminal, when using mobile bindings you call lndmobile.Start to start up lnd and thus the gPRC server(s), so knowing beforehand if one should run Start becomes important (see OP for more info).

@halseth

halseth commented Oct 9, 2020

Copy link
Copy Markdown
Contributor

@hsjoberg I see, thanks for explaining! Would an alternative solution be to make Start idempotent, and return immediately if it notices that lnd already is running? (that way we don't have to add/change the API)

@hsjoberg

hsjoberg commented Oct 9, 2020

Copy link
Copy Markdown
Contributor Author

@halseth Yes possibly if we can get #3982 rolling.
Well maybe not return, the first callback is being used to signal that lnd and the (as of now) walletunlocker rpc is ready, so that would have to be called immediately.

@halseth

halseth commented Feb 10, 2021

Copy link
Copy Markdown
Contributor

Aiming to solve something similar with #5005

@hsjoberg

Copy link
Copy Markdown
Contributor Author

Closing this in favor of #5010 and #5005.

@hsjoberg hsjoberg closed this Apr 15, 2021
@djkazic

djkazic commented Jul 23, 2023

Copy link
Copy Markdown
Contributor

I think we should revisit this. Currently, there is a chicken-or-egg problem when it comes to starting Blixt.

  1. Start Blixt, set some local variable lndStarted = true
  2. Background Blixt app
  3. Now, Blixt app has lost tracked state of if lnd is running (react-native does not cleanly resume state when backgrounded app is resumed)
  4. If we treat startLnd() as idempotent, we don't progress because it's already running and the callback is never fired
  5. If we track state locally in a react-native variable outside of the mobile bindings context, we risk desyncing where the app's boolean flag is set to false for lndStarted due to reset app contexts on resume -- but it in fact is running. This can then lead to a collision where code that is only supposed to run when lnd is not running runs

For these reasons, I think it makes sense to expose status in a non-grpc manner, so mobile clients and other embedded-lnd usecases can track state reliably. GRPC is unusable at this stage of the app "booting up".

Also proposing a rename here of GetStatus -> ServiceStatus or something similar to clarify the purpose of the binding.

@hsjoberg

hsjoberg commented Feb 19, 2026

Copy link
Copy Markdown
Contributor Author

Reopening this one as there are certain situations where it's beneficial or needed to know whether is considered started up or not, without querying the gRPC API itself.

In Blixt Wallet for Android, our background sync job calls stopDaemon in order to shutdown lnd and exit the job.
However, stopDaemon returns immediately and does not wait for the gRPCs to close down (kinda catch-22 if it would).
Android will then suspend the job, but preserving the go runtime in-process, causing issues the next time the user opens Blixt.
Therefore knowing from outside the gRPC whether lnd is started or not is needed, so that we can wait with exiting the job until after all gRPC servers are fully closed down.

Another potential scenario could be if the app needs to close down lnd, for example in order to change the configuration.
As aforementioned, response stopDaemon does not wait until lnd is completely shut down, so there is no good way to tell when it's possible to start lnd up again.

@hsjoberg

hsjoberg commented Feb 19, 2026

Copy link
Copy Markdown
Contributor Author

Greatly simplified the implementation and changed the name of the function to ServiceStatus as per @djkazic's suggestion.

Also, this code has been used in production in Blixt for some years now, so it is battle-tested.

@hsjoberg hsjoberg changed the title mobile: Add GetStatus for retrieving current status of lnd mobile: Add ServiceStatus for retrieving started up status of lnd Feb 19, 2026
@hsjoberg

Copy link
Copy Markdown
Contributor Author

/gemini review

@litbot-9000 litbot-9000 added the severity-medium Focused review required label Feb 19, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a ServiceStatus function to the mobile bindings, allowing the host application to check if the lnd service has been started. This is a useful addition for mobile applications to manage the lnd lifecycle correctly.

My review includes two main points:

  1. A minor style guide fix for the function comment to adhere to the repository's standards.
  2. A more critical fix for a potential data race by ensuring an atomic read of the status variable.

After addressing these points, the change should be good to merge.

Comment thread mobile/bindings.go Outdated
Comment thread mobile/bindings.go Outdated
@hsjoberg

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a ServiceStatus function to allow mobile clients to query the status of the lnd service. This is a useful addition for mobile applications to handle state synchronization with the lnd process, especially in scenarios like service crashes.

My main feedback is to make the returned status more comprehensive by including the wallet's lock state, as mentioned in your pull request description. The current implementation only returns whether lnd has started. Expanding the status information would greatly increase the utility of this new API for mobile clients. I've provided a suggestion on how to structure this.

Comment thread mobile/bindings.go
Comment on lines +160 to +173
type LndStatusCallback interface {
OnResponse(lndStarted int32)
}

// ServiceStatus returns 1 if lnd has started up successfully, allowing the
// caller to interact with the in-memory gRPC servers. If 0 is returned,
// the caller should invoke `Start` to initialize lnd.
//
// ServiceStatus can also be used to determine when lnd has shut down, since
// `stopDaemon` returns immediately and does not wait for the gRPC servers to be
// fully closed.
func ServiceStatus(callback LndStatusCallback) {
callback.OnResponse(atomic.LoadInt32(&lndStarted))
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Your PR description mentions that the status should include walletUnlocked, which is a great idea for mobile clients. The current implementation only returns lndStarted. To make this function more useful and align with your description, I suggest returning a structured status object.

This makes the API more extensible for future status fields. You could then use the generated gRPC bindings for the StateService to query the wallet's state when lnd is running.

Here is a suggested structure that you can build upon. Note that you'll also need to import the encoding/json package.

// LndStatus represents the current status of the lnd daemon.
type LndStatus struct {
	LndStarted bool `json:"lnd_started"`

	// WalletState is the current state of the wallet, matching the states
	// from the `lnrpc.WalletState` enum. A value of 0 indicates that the
	// state is unknown, which is the case if lnd is not running.
	WalletState int `json:"wallet_state"`
}

// LndStatusCallback is an interface that must be implemented by the caller
// to retrieve the service status.
type LndStatusCallback interface {
	// OnResponse is called with the status of lnd, serialized as a JSON
	// byte slice. An error is returned as a string if one occurred.
	OnResponse(statusBytes []byte, errStr string)
}

// ServiceStatus retrieves the current status of the lnd daemon, including
// whether it has started and the wallet's current state. The status is passed
// to the provided callback as a JSON-encoded byte slice.
func ServiceStatus(callback LndStatusCallback) {
	// NOTE: This implementation only returns the lndStarted status.
	// A future commit should expand this to query the wallet state via the
	// State gRPC service when lnd is running.
	status := &LndStatus{
		LndStarted: atomic.LoadInt32(&lndStarted) == 1,
	}

	statusBytes, err := json.Marshal(status)
	if err != nil {
		callback.OnResponse(nil, "error marshalling status: "+err.Error())
		return
	}

	callback.OnResponse(statusBytes, "")
}

@hsjoberg hsjoberg Feb 19, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated the PR description now.

The old implementation had walletUnlocked bool.
But to keep the scope low and functionality targeted, I suggest checking wallet state in the gRPC server later on once lnd is running. This can be done with subscribeState. It's basically a necessity for wallet apps to subscribe to this stream anyway.
Figuring out wallet state is a solved problem. However figuring out whether lnd is started is not.

Used for checking whether lnd is started or not.
ServiceStatus can also be used to determine when lnd has shut down, since
`stopDaemon` returns immediately and does not wait for the gRPC servers
to be fully closed.

@Roasbeef Roasbeef left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🐛

@bhandras bhandras left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! 🎉

@litbot-9000

Copy link
Copy Markdown
Collaborator

@hsjoberg, remember to re-request review from reviewers when ready

2 similar comments
@litbot-9000

Copy link
Copy Markdown
Collaborator

@hsjoberg, remember to re-request review from reviewers when ready

@litbot-9000

Copy link
Copy Markdown
Collaborator

@hsjoberg, remember to re-request review from reviewers when ready

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

severity-medium Focused review required

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants