Metadata-Version: 2.1
Name: pantos-service-node
Version: 2.1.0
Summary: Pantos Service Node (reference implementation)
License: GPL-3.0-only
Author: Pantos GmbH
Author-email: contact@pantos.io
Requires-Python: >=3.10,<4.0
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Requires-Dist: pantos-common (==4.0.1) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: alembic (==1.14.0) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: celery (==5.3.1) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: cerberus (==1.3.4) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: flask (==3.0.3) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: flask-cors (==5.0.0) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: flask-restful (==0.3.10) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: marshmallow (==3.23.1) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: psycopg (==3.2.3) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: pyyaml (==6.0.1) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: sqlalchemy (==2.0.36) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: web3 (==6.5.0) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: json-log-formatter (==0.5.2) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: pyaml-env (==1.2.1) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: python-dotenv (==1.0.1) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: hexbytes (==1.2.1) ; python_version >= "3.10" and python_version < "4"
Requires-Dist: semantic-version (==2.10.0) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: gunicorn (==23.0.0) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: requests (==2.32.3) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: flower (==2.0.1) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: aiohappyeyeballs (==2.4.3) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: aiohttp (==3.10.10) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: aiosignal (==1.3.1) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: amqp (==5.2.0) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: aniso8601 (==9.0.1) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: annotated-types (==0.7.0) ; python_version >= "3.10" and python_version < "4"
Requires-Dist: async-timeout (==4.0.3) ; python_version >= "3.10" and python_version < "3.11"
Requires-Dist: attrs (==24.2.0) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: billiard (==4.2.1) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: bitarray (==3.0.0) ; python_version >= "3.10" and python_version < "4"
Requires-Dist: blinker (==1.8.2) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: certifi (==2024.8.30) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: charset-normalizer (==3.4.0) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: ckzg (==2.0.1) ; python_version >= "3.10" and python_version < "4"
Requires-Dist: click (==8.1.7) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: click-didyoumean (==0.3.1) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: click-plugins (==1.1.1) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: click-repl (==0.3.0) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: colorama (==0.4.6) ; python_version >= "3.10" and python_version < "4.0" and platform_system == "Windows"
Requires-Dist: cytoolz (==1.0.0) ; python_version >= "3.10" and python_version < "4" and implementation_name == "cpython"
Requires-Dist: eth-abi (==5.1.0) ; python_version >= "3.10" and python_version < "4"
Requires-Dist: eth-account (==0.13.0) ; python_version >= "3.10" and python_version < "4"
Requires-Dist: eth-hash (==0.7.0) ; python_version >= "3.10" and python_version < "4"
Requires-Dist: eth-keyfile (==0.8.1) ; python_version >= "3.10" and python_version < "4"
Requires-Dist: eth-keys (==0.4.0) ; python_version >= "3.10" and python_version < "4"
Requires-Dist: eth-rlp (==2.1.0) ; python_version >= "3.10" and python_version < "4"
Requires-Dist: eth-typing (==3.5.2) ; python_version >= "3.10" and python_version < "4"
Requires-Dist: eth-utils (==2.3.1) ; python_version >= "3.10" and python_version < "4"
Requires-Dist: frozenlist (==1.5.0) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: idna (==3.10) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: itsdangerous (==2.2.0) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: jinja2 (==3.1.4) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: jsonschema (==4.23.0) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: jsonschema-specifications (==2024.10.1) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: kombu (==5.4.2) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: lru-dict (==1.3.0) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: markupsafe (==3.0.2) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: multidict (==6.1.0) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: parsimonious (==0.10.0) ; python_version >= "3.10" and python_version < "4"
Requires-Dist: prompt-toolkit (==3.0.48) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: propcache (==0.2.0) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: protobuf (==5.28.3) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: pycryptodome (==3.20.0) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: pydantic (==2.9.2) ; python_version >= "3.10" and python_version < "4"
Requires-Dist: pydantic-core (==2.23.4) ; python_version >= "3.10" and python_version < "4"
Requires-Dist: python-dateutil (==2.9.0.post0) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: pytz (==2024.2) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: pywin32 (==308) ; python_version >= "3.10" and python_version < "4.0" and platform_system == "Windows"
Requires-Dist: referencing (==0.35.1) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: regex (==2024.9.11) ; python_version >= "3.10" and python_version < "4"
Requires-Dist: rlp (==3.0.0) ; python_version >= "3.10" and python_version < "4"
Requires-Dist: rpds-py (==0.20.0) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: setuptools (==75.2.0) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: six (==1.16.0) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: toolz (==1.0.0) ; python_version >= "3.10" and python_version < "4" and (implementation_name == "pypy" or implementation_name == "cpython")
Requires-Dist: typing-extensions (==4.12.2) ; python_version >= "3.10" and python_version < "4"
Requires-Dist: tzdata (==2024.2) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: urllib3 (==2.2.3) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: vine (==5.1.0) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: wcwidth (==0.2.13) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: websockets (==13.1) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: werkzeug (==3.0.4) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: yarl (==1.16.0) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: mako (==1.3.6) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: packaging (==24.2) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: psycopg-binary (==3.2.3) ; implementation_name != "pypy" and python_version >= "3.10" and python_version < "4.0"
Requires-Dist: psycopg-pool (==3.2.3) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: greenlet (==3.1.1) ; python_version < "3.13" and (platform_machine == "aarch64" or platform_machine == "ppc64le" or platform_machine == "x86_64" or platform_machine == "amd64" or platform_machine == "AMD64" or platform_machine == "win32" or platform_machine == "WIN32") and python_version >= "3.10"
Requires-Dist: humanize (==4.11.0) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: prometheus-client (==0.21.0) ; python_version >= "3.10" and python_version < "4.0"
Requires-Dist: tornado (==6.4.1) ; python_version >= "3.10" and python_version < "4.0"
Description-Content-Type: text/markdown

<img src="https://raw.githubusercontent.com/pantos-io/servicenode/img/pantos-logo-full.svg" alt="Pantos logo" align="right" width="120" />

[![CI](https://github.com/pantos-io/servicenode/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/pantos-io/servicenode/actions/workflows/ci.yaml) 
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=pantos-io_servicenode&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=pantos-io_servicenode)



# Pantos Service Node (reference implementation)

## 1. Introduction

### 1.1 Overview

Welcome to the documentation for Pantos Service Node. 

The Pantos Service Node is responsible for initiating cross-chain transfers on behalf of the users. To initiate a cross-chain token transfer, a client has to send a signed request to a service node. To find an appropriate service node, the client can query the PantosHub contract of the source blockchain. To enable this, each service node registers itself at the PantosHub contract of each source blockchain supported by the service node.

### 1.2 Features

The Pantos Service Node is split into two applications:

#### Web server application

The web server application is responsible for the following:

1. Serving signed (by the service node) bids.
2. Accepting signed (by the user) transfer requests.

#### Celery application

The celery application is responsible for the following:

1. Updating the bids later served to the user through the web application.
2. Submitting the signed transfer requests to the source blockchain.

## 2. Installation

### IMPORTANT ###

We provide two ways to modify the app configuration, either through `service-node-config.env` or `service-node-config.yml`. We recommend using the `.env` file, as the `.yml` file is overwritten on every install.

While using the `.env` file you need to be aware that any fields containing certain special characters need to be wrapped around single quotes (e.g. `ETHEREUM_PRIVATE_KEY_PASSWORD='12$$#%R^'`).

The application will complain on startup should the configuration be incorrect.

### 2.1 Pre-built packages

There are two ways to install the apps using pre-built packages:

#### Debian package distribution

We provide Debian packages alongside every release, you can find them in the [releases tab](https://github.com/pantos-io/servicenode/releases). Further information on how to use the service node packages can be found [here](https://pantos.gitbook.io/technical-documentation/general/service-node).

We have a PPA hosted on GitHub, which can be accessed as follows:

```bash
curl -s --compressed "https://pantos-io.github.io/servicenode/KEY.gpg" | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/servicenode.gpg >/dev/null
sudo curl -s --compressed -o /etc/apt/sources.list.d/pantos-servicenode.list "https://pantos-io.github.io/servicenode/pantos-servicenode.list"
sudo apt update
sudo apt install pantos-service-node
```

#### Docker images

We also distribute docker images in DockerHub with each release. These are made available under the pantosio project as either [**app**](https://hub.docker.com/r/pantosio/service-node-app) or [**worker**](https://hub.docker.com/r/pantosio/service-node-worker).

##### Local Setup

You can run a local setup with docker by doing the following steps:

- Run `make docker` on the `ethereum-contracts` project
- The variables `DB_URL`, `CELERY_BACKEND` and `CELERY_BROKER` are already defined in the `docker-compose.yml`
- Modify the `docker.env` file to match your current setup
- Ensure you have a `signer_key` file located in the same directory. If you don't, you can create one with `make signer-key`
- Run `make docker`

##### Local development with Docker

You can do local development with Docker by enabling dev mode (Docker watch mode). To do so, set the environment variable `DEV_MODE` to true, like this:

`DEV_MODE=true make docker`

#### Multiple local deployments

We support multiple local deployments, for example for testing purposes, you can run the stacks like this:

`make docker INSTANCE_COUNT=<number of instances>`

To remove all the stacks, run the following:

`make docker-remove`

Please note that this mode uses an incremental amount of resources and that Docker Desktop doesn't fully support displaying it, but it should be good enough to test multiple service nodes locally.

##### Production Setup

The production setup is slightly different, for convenience we provide a separate `.env` file and `make` method.

- The variables `DB_URL`, `CELERY_BACKEND` and `CELERY_BROKER` are already defined in the `docker-compose.yml`
- Modify the `.env` file (**not** `docker.env`) to match your current setup
- Ensure you have a `signer_key` file located in the same directory. If you don't, you can create one with `make signer-key`
- Run `make docker-prod`

Please note that you may need to add a load balancer or another webserver in front of this setup should you want to host this setup under a specific domain.

If you're hosting this on a cloud provider (AWS, GCP, Azure or alike), these are normally provided. You just need to point the load balancer to the port exposed by the app, `8080`, and configure the rest accordingly.

#### Python package

We distribute the package in pypi under the following project and https://pypi.org/project/pantos-service-node/. You can install it to your project by using `pip install pantos-service-node`.

### 2.2 Prerequisites

Please make sure that your environment meets the following requirements:

#### Python Version

The Pantos Service Node supports **Python 3.10** or higher. Ensure that you have the correct Python version installed before the installation steps. You can download the latest version of Python from the official [Python website](https://www.python.org/downloads/).

#### Library Versions

The Pantos Service Node has been tested with the library versions specified in **poetry.lock**.

#### Poetry

Poetry is our tool of choice for dependency management and packaging.

Installing: 
https://python-poetry.org/docs/#installing-with-the-official-installer
or
https://python-poetry.org/docs/#installing-with-pipx

By default poetry creates the venv directory under ```{cache-dir}/virtualenvs```. If you opt for creating the virtualenv inside the project’s root directory, execute the following command:
```bash
poetry config virtualenvs.in-project true
```

#### Conda (Debian package building only)

Conda is only required to build the Debian package. To install conda, follow the instructions [here](https://docs.conda.io/projects/conda/en/latest/user-guide/install/linux.html). Check the installation was correct running `conda -version`.


### 2.2  Installation Steps

#### Libraries

Create the virtual environment and install the dependencies:

```bash
poetry install --no-root
```

#### Pre-commit

In order to run pre-commit before a commit is done, you have to install it:

```bash
pre-commit install --hook-type commit-msg -f && pre-commit install
```

Whenever you try to make a commit, the pre-commit steps are executed.

## 3. Usage

### 3.1 Format, lint and test

Run the following command from the repository's root directory:

```bash
make code
```

### 3.2 OpenAPI

If you want to generate the OpenAPI documentation, you can run the following command:

```bash
make openapi-docs
```
which will generate a `openapi.json` file in the `docs` directory.
If you want to specify a different path for the output file, you can do so by running:

```bash
make openapi-docs OUTPUT_FILE=<path>/<filename.json>
```

### 3.3 Local development environment

#### PostgreSQL

Launch the PostgreSQL interactive terminal:

```bash
sudo -u postgres psql
```

Create a Service Node user and three databases:

```
CREATE ROLE "pantos-service-node" WITH LOGIN PASSWORD '<PASSWORD>';
CREATE DATABASE "pantos-service-node" WITH OWNER "pantos-service-node";
CREATE DATABASE "pantos-service-node-celery" WITH OWNER "pantos-service-node";
CREATE DATABASE "pantos-service-node-test" WITH OWNER "pantos-service-node";
```

#### RabbitMQ

Create a Service Node user and virtual host:

```
sudo rabbitmqctl add_user pantos-service-node <PASSWORD>
sudo rabbitmqctl add_vhost pantos-service-node
sudo rabbitmqctl set_permissions -p pantos-service-node pantos-service-node ".*" ".*" ".*"
```

## 4. Contributing

For contributions take a look at our [code of conduct](CODE_OF_CONDUCT.md).
