Service Example X: Erigolem – Managed Erigon

Ethereum client as a service, implemented using a self-contained runtime
The purpose of this example is to demonstrate building a Golem service using a dedicated, self-contained runtime. It features an Ethereum network client, Erigon (f.k.a. Turbo-geth) started on provider nodes on user's demand. Interaction with the service, once it's running and its location and credentials have been passed to the requestor, is not facilitated by Golem though.
This example illustrates the following Golem features & aspects:
All code for this example can be found in this repository: https://github.com/golemfactory/yagna-service-erigon/

Architecture overview

The application consists of three major components:
  1. 1.
    User interface
    A browser-based interface written in TypeScript using React. It allows the end user to interact with the application, i.e. start an Erigon node after authenticating with their Metamask wallet, and later to manage their node(s).
  2. 2.
    Requestor server
    A Python application developed using Quart and yapapi-service-manager. It acts both as an HTTP server (handling requests from the user interface), and as a requestor agent (submitting tasks to the Golem network).
  3. 3.
    Erigon runtime
    A dedicated, self-contained runtime created with ya-runtime-sdk. It is a Rust binary wrapping Erigon service itself so that it can be orchestrated by the yagna daemon. The runtime also controls the access to the service by managing Nginx configuration.
A typical interaction flow proceeds in the following manner:
  1. 1.
    User opens the web interface in their browser.
  2. 2.
    User authenticates with their metamask wallet.
  3. 3.
    User clicks to start an Erigon node.
  4. 4.
    Request to start an Erigon node is sent via the REST API to the requestor server.
  5. 5.
    Requestor starts a service activity (these steps are handled by yagna core and yapapi internally):
    1. 1.
      Requestor server makes a number of API calls to the yagna daemon running on the requestor's machine in order to find an available Erigon provider and sign an agreement.
    2. 2.
      Once the agreement is signed, requestor server sends a command to the provider to start an Erigon node.
    3. 3.
      Yagna daemon running on the provider's machine spawns an Erigon runtime process (a service wrapper/supervisor process).
  6. 6.
    Erigon runtime starts the actual Erigon service (backend & rpcdaemon processes).
  7. 7.
    Erigon runtime generates a new username and password and puts them in the NGINX configuration.
  8. 8.
    NGINX auto-reloads on configuration change, from now on granting access to the Erigon service only for user(s) authenticated with the newly-created credentials. (NGINX is continuously running on provider's machine as a daemon, so it doesn't need to be started.)
  9. 9.
    The credentials are passed backed to the requestor server along with provider's node's public IP address and the port on which NGINX is listening.
  10. 10.
    Requestor server passes all the received information to the web interface where it can be viewed by the user.
  11. 11.
    User interacts with the Erigon service (via NGINX proxy).
  12. 12.
    Once the user finishes using their Erigon instance, they click to stop it.
  13. 13.
    Web interface sends a request to stop the instance via the REST API to the requestor server.
  14. 14.
    Requestor server sends a stop command to the provider.
  15. 15.
    Erigon runtime stops the Erigon service and exits.
  16. 16.
    Requestor server orchestrates a payment for the service in the background without further user interaction.
The design presented above excludes making any payments for the service by its end user. This omission was made intentionally to make te service implementation simpler. It's not a problem as long as the service is used for demonstrative purpose only.
So in order to demonstrate building an Erigon service application we'll start from the Provider side and develop a custom service runtime:
Then we'll move on to implementing a REST API which will serve as a Requestor-side agent to control the service runtime instances hosted on Provider side:
Finally we'll wrap up with a utility to setup a Provider machine to host Erigon service instances:
Let's crack on!