status-go/cmd/status-backend
Patryk Osmaczko c5c28cc568 chore(logging)_: switch to zap.Logger as central logger
Set zap.Logger as the  primary logger for status-go. All geth logs are
now proxied through zap.Logger.

closes: #6029
2024-11-22 10:03:22 +01:00
..
requests feat_: status-backend standard requests (#5868) 2024-10-02 12:28:23 +01:00
server fix_: delete endpoints file (#5922) 2024-10-07 13:07:33 +01:00
README.md docs_: better usage docs for status-backend (#5955) 2024-10-18 21:59:09 +03:00
main.go chore(logging)_: switch to zap.Logger as central logger 2024-11-22 10:03:22 +01:00

README.md

📝Description

Welcome to status-backend. This is a tool for debugging and testing status-go. In contrast to existing statusd and status-cli, the status-backend exposes full status-go API through HTTP.

This allows to communicate with status-go through HTTP the same way as status-desktop and status-mobile do, including:

  • create account
  • restore account
  • login
  • logout
  • start messenger
  • start wallet
  • subscribe to status-go signals
  • etc.

📍status-go API

[!NOTE]
Unfortunately, for now there is no convenient docs like OpenAPI

1 Public methods in ./mobile/status.go

Description

Any public functions in ./mobile/status.go with the one of the following signatures:

  • func(string) string - 1 argument, 1 return
  • func() string - 0 argument, 1 return

Address

Endpoints address: http://<address>/statusgo/<function-name>.

Here, statusgo is the name of the package in /mobile/status.go. We might create more APIs next to it in the future.

Response

Responses have JSON body with a single error field.
If error is empty, no error occurred.

The structure of response is defined in APIResponse:

// APIResponse generic response from API.
type APIResponse struct {
    Error string `json:"error"`
}

Parameters

Parameters are passed as request JSON body.
Specific parameters for each endpoint should be checked in the source code.

How to read the source code?

As example, let's look at CreateAccountAndLogin.

func CreateAccountAndLogin(requestJSON string) string {
	return callWithResponse(createAccountAndLogin, requestJSON)
}
  1. The function has 1 JSON string argument and 1 JSON string return value.
  2. The function wraps a private createAccountAndLogin with callWithResponse.
    callWithResponse does some internal magic like logging the request and response.
    So we should check the private function for real arguments and return values:
    func createAccountAndLogin(requestJSON string) string {
        var request requests.CreateAccount
        err := json.Unmarshal([]byte(requestJSON), &request)
        // ...
    }
    
  3. We can see that the parameters are unmarshalled to requests.CreateAccount:
    type CreateAccount struct {
        RootDataDir   string `json:"rootDataDir"`
        KdfIterations int    `json:"kdfIterations"`
        // ...
    
  4. We can also see that each the response is formed as makeJSONResponse(err):
    if err != nil {
        return makeJSONResponse(err)
    }
    
    ... which wraps the error to APIResponse:
    // APIResponse generic response from API.
    type APIResponse struct {
        Error string `json:"error"`
    }
    

Unsupported methods

Attempt to call any functions with unsupported signatures will return 501: Not Implemented HTTP code.
For example, VerifyAccountPassword has 3 arguments:

func VerifyAccountPassword(keyStoreDir, address, password string) string {
    return logAndCallString(verifyAccountPassword, keyStoreDir, address, password)
}

Later, as needed, a V2 of these functions will be introduced. V2 will have a single JSON argument composing all args in 1.
For example, https://github.com/status-im/status-go/pull/5865 fixes some of these.

Deprecated methods

Deprecated methods will have Deprecation: true HTTP header.

2 Signals in ./signal

[!NOTE]
Unfortunately, there is no description of when any expected signals will appear.
For now, you have to check the source code.

Address

Signals are available at ws://<address>/signals.

Connect to it as the first thing when running status-backend.

Available signals

List of possible events can be found in ./signal/event_*.go files.

For example, node.login event is defined here:

const (
    // EventLoggedIn is once node was injected with user account and ready to be used.
    EventLoggedIn = "node.login"
)

Signals structure

Each signal has this structure:

// Envelope is a general signal sent upward from node to RN app
type Envelope struct {
    Type  string      `json:"type"`
    Event interface{} `json:"event"`
}

Here, type is the name of the event, e.g. node.login.
And the structure of this event is defined in the same file:

// NodeLoginEvent returns the result of the login event
type NodeLoginEvent struct {
	Error        string                 `json:"error,omitempty"`
	Settings     *settings.Settings     `json:"settings,omitempty"`
	Account      *multiaccounts.Account `json:"account,omitempty"`
	EnsUsernames json.RawMessage        `json:"ensUsernames,omitempty"`
}

So the signal for node.login event will look like this (with corresponding data):

{
  "type": "node.login",
  "event": {
    "error": "",
    "settings": {},
    "account": {},
    "endUsernames": {} 
  }
}

3 Services in ./services/**/api.go

Services are registered in the go-ethereum JSON-RPC server.
All ./services/**/api.go are registered as services in geth.

Each method name has form <namespace>_<method>. In most cases namespace is the directory name, but it can be ensured in the APIs method of each service. For example, for wallet service:

// APIs returns list of available RPC APIs.
func (s *Service) APIs() []gethrpc.API {
    return []gethrpc.API{
        {
            Namespace: "wallet",
            Version:   "0.1.0",
            Service:   NewAPI(s),
            Public:    true,
        },
    }
}

Address

These methods are available through /statusgo/CallRPC endpoint defined here.

This is the way desktop and mobile clients call these methods. You don't have to run a separate geth HTTP server for this.

Arguments

For example:

### Send Contact Request
POST http://localhost:12345/statusgo/CallRPC

{
    "jsonrpc": "2.0",
    "method": "wakuext_sendContactRequest",
    "params": [
        {
            "id": "0x048f0b885010783429c2298b916e24b3c01f165e55fe8f98fce63df0a55ade80089f512943d4fde5f8c7211f1a87b267a85cbcb3932eb2e4f88aa4ca3918f97541",
            "message": "Hi, Alice!"
        }
    ]
}

Please reference to the source code for the list of methods and its arguments.

Notes

  1. In this case, there's no limitation to the number of arguments, comparing to mobile/status.go, so ll method are supported.
  2. Deprecated methods won't have a corresponding Deprecated: true

🏃‍♂️Usage

Start the app with the address to listen to:

status-backend --address localhost:12345

Or just use the root repo Makefile command:

make run-status-backend PORT=12345

Access the exposed API with any HTTP client you prefer:

👌 Simple flows

In most cases to start testing you'll need some boilerplate. Below are the simple call flows for common cases.

Create account and login

  1. Subscribe to /signals
  2. Call /statusgo/InitializeApplication
  3. Create an account
    1. Call /statusgo/CreateAccountAndLogin
    2. Wait for node.login signal
      • If error is empty, no error occurred
      • If error is not empty, stop the operation
  4. Start required services
    1. Call /statusgo/CallRPC with { "method": "wakuext_startMessenger", "params": [] }
    2. Call /statusgo/CallRPC with { "method": "wallet_startWallet", "params": [] }
  5. Apply temporary workarounds:
    1. Call /statusgo/CallRPC with { "method": "settings_getSettings", "params": [] }
      (otherwise settings don't get saved into DB)

Login into account

  1. Subscribe to /signals
  2. Call /statusgo/InitializeApplication
  3. Create an account
    1. Call /statusgo/LoginAccount
    2. Wait for node.login signal
      • If error is empty, no error occurred
      • If error is not empty, stop the operation
  4. Start required services
    1. Call /statusgo/CallRPC with { "method": "wakuext_startMessenger", "params": [] }
    2. Call /statusgo/CallRPC with { "method": "wallet_startWallet", "params": [] }