Nimbus on mobile

Daily progress update:

  • Helped @deme investigate a bug on the Nimbus side, which he fixed, meaning we now receive the correct public key in messages, and we no longer have to hardcode it.
  • Cleaned up the code on the Go side.

The console app is now fully functional on Nimbus as well as Geth, based solely on a -nimbus command line argument. There are a few remaining improvements to be done on the Nimbus side, and now the remaining major issue is managing the libnimbus.so dependency. Right now it’s being copied manually, but ideally we’d have some form of dependency management for it. One way to do it is with Nix, by building our branch of the Nim compiler, and then using that to build the Nimbus repository. That’s what I’ll be working on next.

2 Likes

Daily progress report:

  • Finished the integration of the Nimbus Nix build with the console client project.

If you want to take the Nimbus console client for a spin, just run the following commands:

git checkout https://github.com/status-im/status-console-client.git -b feature/status-nim && \
cd status-console-client && \
make build-nimbus && \
./bin/status-term-client -nimbus -fleet eth.staging

then in the bottom line (use TAB key to cycle through panes), type /chat add status to open the #status chat.

Note: There’s a UI glitch in that you must first select a chat in the top-left panel by pressing ENTER before being able to post messages to it.

  • Current state
    • Nimbus node is fully integrated in console app. PRs are in review. Nimbus API library is built by console client project using Nix expression from Nimbus repo.
    • Some unused APIs are not yet implemented (e.g. NewMessageFilter, SubscribeEnvelopeEvents, GetFilter, SendMessagesRequest, RequestHistoricMessagesWithTimeout, SyncMessages), however this PoC should be enough of a foundation to clarify how those will need to be implemented.

I’m considering the console PoC closed for now, since it demonstrates that the idea works and can be extended.

Next steps would be to integrate Nimbus in status-react (without envelope monitor or mailserver functionality):

  • generalize StatusNode to use facades over the actual node implementation (tricky since there are many supporting types from geth):
    • plug in call to nimbus_start in StatusNode.StartWithOptions (in this case, we’d not register mailserversService)
    • add missing configuration options to nimbus_start (e.g. bootstrap and static nodes, etc.)
    • implement RPC functionality between Nimbus and status-go and abstract it behind a facade
    • implement facade over service management API (gethNode.Service(serviceInstance))
      • implement support for service management in Nimbus bridge
      • bridge with respective services in Nimbus (wallet, ShhExt, accounts manager)
  • Add an argument to params.NodeConfig to determine the type of node to use (this would be passed from the UI in status-react).
    • Add UI in status-react to determine the ETH node type.
1 Like

Daily progress update:

  • Starting to look at using Nimbus in status-react. This is a huge undertaking considering how closely tied status-go is with geth types.
  • To start with, I’m exposing whispertypes.Whisper as a geth service, to make it easier to replace the SHH node type in a single place in status-go.
3 Likes

Met today with @adam and @andre to discuss the course of action for integrating status-go/status-react with Nimbus and the outcome was:

  • We will define a minimal use case in status-react that we want to support and work towards. In this case it’ll be:
    1. Creating an in-device account
    2. Adding a public chat
    3. Sending and receiving a message in the public chat
    4. Logging out
  • In order to start this effort, we’ll determine the methods in status-go/mobile/status.go called by status-react during a test run by logging them out.
  • Implement adapters for this high-level interface for Nimbus and possibly Geth (to validate approach). This could potentially be done in a simplistic new repo (acting as a drop-in status-go replacement) to avoid the huge surface area that would need to be taken into account in status-go.

Created issue in status-go repo for work mentioned in previous post.

1 Like

I don’t think we can get away from creating an isolated project that will provide the abstraction over node implementations (Geth or Nimbus), as well as supporting types (e.g. Hash, HexBytes, Address, etc). This project would be imported as a module by status-protocol-go and status-go (so they wouldn’t need to e.g. include libnimbus.h).
This would probably mean a separate repo, like we have already for e.g. status-im/whisper.

The dependency tree would look something like this:

  • status-react
    • status-go
      • status-eth-node (NEW)
      • status-protocol-go
        • whisper
        • status-eth-node (NEW)
          • go-ethereum
          • whisper
          • nimbus

Some of the benefits:

  • Currently we have a build-nimbus.sh script in status-console-client. We’ll need to replicate this script into each Nimbus-consuming app. If we create status-eth-node, the script will live there instead of being replicated.
  • Only status-eth-node will know about Nimbus’ C interface (libnimbus.h and libnimbus.so), and will expose a Go interface to the consuming projects, making it easier to consume Nimbus. This will also allow us to implement some additional logic on top of the node that is guaranteed to be shared.
  • Currently some supporting types (e.g. Hash, HexBytes) live in status-protocol-go, even though they are not strictly protocol-related. status-eth-node would provide a better home for these types.

NOTE: @adam is currently investigating the feasibility of a status-go mono repo.

1 Like

I think that’s correct. These structs from go-ethereum and various other helpers are shared throughout Status Go codebase and probably shouldn’t be decoupled at all. And I definitely like the idea of putting the Nim-C-Go interface in one package.

TLDR; we should be safe with using monorepo and submodules. In Go 1.13, this combination works correctly and there are techniques which will nicely solve a few identified edge cases.

The main point of using monorepo is providing a developer-friendly environment. Currently, we suffer from a long chain of dependencies. For example, a change in whisper may require a change in status-protocol-go and then finally a change in status-go. All these changes needs to be committed in order and pushed to remote repositories in order to be functionally tested.

Submodules solves a problem of versioning and providing an interface for clients. For example, status-protocol-go provides a programatic interface to interact with the Status community through messages.

Here is a branch with a working example: https://github.com/status-im/status-go/tree/experiment/submodules

  • status-go is the main module
  • status-eth-node is a submodule without dependencies
  • status-protocol is a submodule depending on status-eth-node

As you can use, I vendored dependencies of status-protocol to see what is there and there are only relevant files.

However, to download a submodule, the whole repo is cloned and additionally a submodule tag with a version needs to be created like status-eth-node/v1.0.0. Then, such a submodule can be required like this go get github.com/status-im/status-go/[email protected] where the repo is github.com/status-im/status-go.

There are two problems with that:

  1. The submodule path is long.
  2. Development is still difficult.

These two problems are solvable, though.

  1. We should be able to use vanity imports so go.status.im/geth-node or go.status.im/protocol should work fine regardless whether they are submodules or not. Relevant topic is canonical import paths.
  2. It’s possible to use relative imports in go.mods using replace directive. Here is an example of a test which require a submodule with relative redirect: https://github.com/status-im/status-go/blob/experiment/submodules/t/test-status-protocol/messenger_test.go (it requires github.com/status-im/status-go/status-protocol which has go.mod entry replace github.com/status-im/status-go/status-eth-node => ../status-eth-node) and the test passes fine.
2 Likes

Okay, agreed.

That doesn’t follow. How does using Git submodules improve this situation? The repos still remain separate repos, they still need to be committed to, and the changes still have to be orchestrated together to take effect, all you changed is how they are imported, not how their changes are synced. Unless I’m missing something.

I see, so when you say “submodules” you don’t mean Git submodules, just regular folders checked into the repo. That makes much more sense.

Daily progress update:

  • Archived status-protocol-go and moved the contents to status-go monorepo

Daily progress update:

  • Working on giving Geth/Nimbus wrappers and types a new home, in a new submodule called eth-node which will be consumed by protocol and status-go.
1 Like

Daily progress update:

  • Fix some more issues in status-go
  • Create PR for status-console-client to adapt it to the new changes in status-go

Daily progress update:

  • Continue creating Nimbus backend on top of prep PR branch

Daily progress update:

  • Merged status-go Nimbus prep PR
  • Created and merged a PR in status-console-client to bring it up to date with status-go

Will be investigating how we can consume Nimbus in status-react/status-go. The PoCs done so far consume libnimbus.so (dynamic module) instead of libnimbus.a (static module), and status-react is based on consuming status-go statically (libstatus.a), so it would be very helpful to be able to build status-go agains libnimbus.a. Right now the issue is with linker symbol clashes in the secp256k1 library which is imported both by Nimbus and status-go.

1 Like

Daily progress update:

  • Managed to consume libnimbus.a in status-console-client by passing -extldflags=-Wl,--allow-multiple-definition" to go build. This sidesteps the issue for the time being, but we should ensure that the Nimbus version of secp256k1 is the same as the Go version, since there are no guarantees on which one gets picked by the linker.
  • Will now start adapting the status-go Nix recipes in status-react to build a Nimbus version. Among other things, this requires cross-compiling Nimbus for Android.

Managed to cross-compile Nimbus for Android (32 and 64-bit), but still running into a linker error due to architecture mismatch when linking libnimbus.a with status-go through gomobile:

/nix/store/30ph8xgdbskprmhymyc7gfrw7hy4436n-androidsdk/libexec/android-sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/bin/../lib/gcc/aarch64-linux-android/4.9.x/../../../../aarch64-linux-android/bin/ld: skipping incompatible go/src/github.com/status-im/status-go/vendor/github.com/status-im/status-go/eth-node/bridge/nimbus/libnimbus.a when searching for -lnimbus

Nimbus:

> objdump -f secp256k1.c.o
secp256k1.c.o:     file format elf64-little
architecture: UNKNOWN!, flags 0x00000011:

status-go:

> objdump -f jni/arm64-v8a/libgojni.so
jni/arm64-v8a/libgojni.so:     file format elf64-littleaarch64
architecture: aarch64, flags 0x00000150:

Need to figure out why the Nimbus build isn’t being assigned the right architecture.

Linking with the armv7 (32-bit) version of libnimbus.a when building with gomobile bind -target=android/arm yields a slightly different message:

# github.com/status-im/status-go/vendor/github.com/status-im/status-go/eth-node/bridge/nimbus
/nix/store/30ph8xgdbskprmhymyc7gfrw7hy4436n-androidsdk/libexec/android-sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.9.x/../../../../arm-linux-androideabi/bin/ld: fatal error: go/src/github.com/status-im/status-go/vendor/github.com/status-im/status-go/eth-node/bridge/nimbus/libnimbus.a(secp256k1.c.o): unsupported ELF machine number 0
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Indeed, if I run objdump -f on it, I can see that secp256k1.c.o is the only object file which has unknown architecture and wrong file format:

In archive /nix/store/pn94h52cl42cn54mi6jj67cmdrbsj0vw-nimbus-0.0.1-android-armv7/lib/libnimbus.a:

secp256k1.c.o:     file format elf32-little
architecture: UNKNOWN!, flags 0x00000011:
HAS_RELOC, HAS_SYMS
start address 0x00000000


stdlib_assertions.nim.c.o:     file format elf32-littlearm
architecture: arm, flags 0x00000011:
HAS_RELOC, HAS_SYMS
start address 0x00000000

Also, it looks like Nix is using the wrong strip binary (from host binutils, instead of the one from the cross-compiler toolchain), still need to adapt that:

post-installation fixup
shrinking RPATHs of ELF executables and libraries in /nix/store/pn94h52cl42cn54mi6jj67cmdrbsj0vw-nimbus-0.0.1-android-armv7
strip is /nix/store/a9ms1pkj00sgssxqr2cvryv3h5fki4an-binutils-2.31.1/bin/strip
stripping (with command strip and flags -S) in /nix/store/pn94h52cl42cn54mi6jj67cmdrbsj0vw-nimbus-0.0.1-android-armv7/lib 

Fixed the issues above by setting CC env var to the cross-compiler and setting TARGET_STRIP and donStripHost Nix variables.

Now coming across missing pcre lib when linking:

# github.com/status-im/status-go/vendor/github.com/status-im/status-go/eth-node/bridge/nimbus
/nix/store/30ph8xgdbskprmhymyc7gfrw7hy4436n-androidsdk/libexec/android-sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/bin/../lib/gcc/aarch64-linux-android/4.9.x/../../../../aarch64-linux-android/bin/ld: cannot find -lpcre
clang: error: linker command failed with exit code 1 (use -v to see invocation)