Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cmd)_: status-backend #5847

Merged
merged 10 commits into from
Sep 27, 2024
7 changes: 7 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,13 @@ spiff-workflow: build/bin/spiff-workflow
status-cli: ##@build Build status-cli to send messages
status-cli: build/bin/status-cli

status-backend: ##@build Build status-backend to run status-go as HTTP server
status-backend: build/bin/status-backend

run-status-backend: PORT ?= 0
run-status-backend: ##@run Start status-backend server listening to localhost:PORT
go run ./cmd/status-backend --address localhost:${PORT}

statusd-prune-docker-image: SHELL := /bin/sh
statusd-prune-docker-image: ##@statusd-prune Build statusd-prune docker image
@echo "Building docker image for ststusd-prune..."
Expand Down
155 changes: 155 additions & 0 deletions cmd/status-backend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# 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

## Public methods in `./mobile/status.go`

Only specific function signatures are currently supported:
- `func(string) string` - 1 argument, 1 return
- `func() string` - 0 argument, 1 return

### Unsupported methods

Attempt to call any other functions will return `501: Not Implemented` HTTP code.
For example, [`VerifyAccountPassword`](https://github.com/status-im/status-go/blob/669256095e16d953ca1af4954b90ca2ae65caa2f/mobile/status.go#L275-L277) has 3 arguments:
```go
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.

## Signals in `./signal`

Each signal has [this structure](https://github.com/status-im/status-go/blob/c9b777a2186364b8f394ad65bdb18b128ceffa70/signal/signals.go#L30-L33):
```go
// Envelope is a general signal sent upward from node to RN app
type Envelope struct {
Type string `json:"type"`
Event interface{} `json:"event"`
}
```

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

For example, `node.login` event is defined [here](https://github.com/status-im/status-go/blob/6bcf5f1289f9160168574290cbd6f90dede3f8f6/signal/events_node.go#L27-L28):
```go
const (
// EventLoggedIn is once node was injected with user account and ready to be used.
EventLoggedIn = "node.login"
)
```

And the structure of this event is [defined in the same file](https://github.com/status-im/status-go/blob/6bcf5f1289f9160168574290cbd6f90dede3f8f6/signal/events_node.go#L36-L42):
```go
// 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):
```json
{
"type": "node.login",
"event": {
"error": "",
"settings": {},
"account": {},
"endUsernames": {}
}
}
```

## Services in `./services/**/api.go`

Services are registered in go-ethereum JSON-RPC server. To call such method, send request to `statusgo/CallRPC` endpoint.

For example:
```http request
### Send Contact Request
POST http://localhost:12345/statusgo/CallRPC
{
"jsonrpc": "2.0",
"method": "wakuext_sendContactRequest",
"params": [
{
"id": "0x048f0b885010783429c2298b916e24b3c01f165e55fe8f98fce63df0a55ade80089f512943d4fde5f8c7211f1a87b267a85cbcb3932eb2e4f88aa4ca3918f97541",
"message": "Hi, Alice!"
}
]
}
```

### 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:
```shell
status-backend --address localhost:12345
```

Or just use the root repo Makefile command:
```shell
make run-status-backend PORT=12345
```

Access the exposed API with any HTTP client you prefer:
- From your IDE:
- [JetBrains](https://www.jetbrains.com/help/idea/http-client-in-product-code-editor.html)
- [VS Code](https://marketplace.visualstudio.com/items?itemName=humao.rest-client)
- From UI client:
- [Postman](https://learning.postman.com/docs/getting-started/first-steps/sending-the-first-request/)
- [Insomnia](https://docs.insomnia.rest/insomnia/send-your-first-request)
- From command line:
- [Curl](https://curl.se/docs/httpscripting.html)
- From your script:
- [Python](https://pypi.org/project/requests/)
- [Go](https://pkg.go.dev/net/http)

# 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. `InitializeApplication`
2. `CreateAccountAndLogin`
3. `wakuext_startMessenger`
4. `wallet_startWallet`
5. `settings_getSettings` (temporary workaround, otherwise settings don't get saved into DB)

## Login into account

1. `InitializeApplication`
2. `LoginAccount`
3. `wakuext_startMessenger`
4. `wallet_startWallet`
48 changes: 48 additions & 0 deletions cmd/status-backend/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package main

import (
"flag"
stdlog "log"
"os"

"golang.org/x/crypto/ssh/terminal"

"github.com/ethereum/go-ethereum/log"

"github.com/status-im/status-go/cmd/statusd/server"
"github.com/status-im/status-go/logutils"
)

var (
address = flag.String("address", "", "host:port to listen")
logger = log.New("package", "status-go/cmd/status-backend")
)

func init() {
logSettings := logutils.LogSettings{
Enabled: true,
MobileSystem: false,
Level: "INFO",
}
colors := terminal.IsTerminal(int(os.Stdin.Fd()))
if err := logutils.OverrideRootLogWithConfig(logSettings, colors); err != nil {
stdlog.Fatalf("failed to initialize log: %v", err)
}
}

func main() {
flag.Parse()

srv := server.NewServer()
srv.Setup()

err := srv.Listen(*address)
if err != nil {
logger.Error("failed to start server", "error", err)
return
}

log.Info("server started", "address", srv.Address())
srv.RegisterMobileAPI()
srv.Serve()
}
1 change: 1 addition & 0 deletions cmd/statusd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ func main() {
logger.Error("failed to start server", "error", err)
return
}
go srv.Serve()
log.Info("server started", "address", srv.Address())
defer func() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
Expand Down
120 changes: 120 additions & 0 deletions cmd/statusd/server/endpoints.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 30 additions & 0 deletions cmd/statusd/server/parse-api/endpoints_template.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Code generated by parse-api/main.go. DO NOT EDIT.
// source: parse-api/main.go

package server

import statusgo "github.com/status-im/status-go/mobile"

var EndpointsWithRequest = map[string]func(string) string{
{{- range .FunctionsWithResp }}
"/{{ $.PackageName }}/{{ . }}": {{ $.PackageName }}.{{ . }},
{{- end }}
}

var EndpointsWithoutRequest = map[string]func() string{
{{- range .FunctionsNoArgs }}
"/{{ $.PackageName }}/{{ . }}": {{ $.PackageName }}.{{ . }},
{{- end }}
}

var EndpointsUnsupported = []string{
{{- range .UnsupportedEndpoints }}
"/{{ $.PackageName }}/{{ . }}",
{{- end }}
}

var EndpointsDeprecated = map[string]struct{}{
{{- range .DeprecatedEndpoints }}
"/{{ $.PackageName }}/{{ . }}": {},
{{- end }}
}
Loading