Skip to content
This repository has been archived by the owner on Sep 5, 2024. It is now read-only.

Commit

Permalink
merge major-upgrade to main
Browse files Browse the repository at this point in the history
  • Loading branch information
okedeji committed Jun 6, 2024
2 parents 6e7b0d6 + 9131d28 commit e0ca785
Show file tree
Hide file tree
Showing 10 changed files with 372 additions and 171 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
- name: Build package
run: |
python setup.py sdist bdist_wheel
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@v1.4.2
with:
Expand Down
44 changes: 32 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,39 @@
![Python!](https://img.shields.io/badge/Python-FFD43B?style=for-the-badge&logo=python&logoColor=blue)
![Apache License](https://img.shields.io/badge/Apache%20License-D22128?style=for-the-badge&logo=Apache&logoColor=white)

The `allocmd` is a CLI tool that handles worker nodes' seamless creation and deployment. With this tool, you do not need to write the worker node from scratch, the CLI tool will help you bootstrap all the needed components to get worker nodes working. All you have to do is to update the `config.yaml` file with your custom parameters, update the provided`main.py`to communicate with your inference server, run the deploy command, and your worker should be up and running.
The `allocmd` is a CLI tool that handles seamless creation of Allora external resources built to integrate with Allora chain. With this tool, you do not need to write the worker or reputer node or even a validator from scratch, the CLI tool will help you bootstrap all the needed components to get resource working.

To build a worker node with `allocmd`, you will need to follow the following steps:
The following are the list of stuff that can don with this tool at the moment
1. Generating worker node files
2. Genrating reputer node files
3. Generating validator node files
4. Funding of testnet account addresses

### 1. Install `allocmd` CLI
for all the files generation commands, the tool will help in generation of the needed files and their respective docker files and you can spin them up as usual docker containers with docker-compose

## Install `allocmd` CLI

You will begin with installing the tool on your machine.

```shell
pip install allocmd
```

> you can run `allocmd --help` to get general help or `allocmd [command] --help` to get help relating to a particular command.
> you should use version 1.0.0 for Allora Chain v1 and version 2.0.0 for Allora Chain v2. Run `allocmd --help` to get general help or `allocmd [command] --help` to get help relating to a particular command.
### 2. Initialize the worker for development
## Initializing resources
### Initialize the worker/reputer for development
> Note that all commands here will pass for both worker or reputer node
The next step is initializing the CLI to bootstrap all the needed components to get your worker running. The following command will handle the initialization process. It will create all the files in the appropriate directories and generate identities for your node to be used for local development.
The next step is initializing the CLI to bootstrap all the needed components to get your worker or reputer running. The following command will handle the initialization process. It will create all the files in the appropriate directories and generate identities for your node to be used for local development.

```shell
allocmd init --name <preffered name> --topic <topic id> --env dev
allocmd generate worker --name <preffered name> --topic <topic id> --env dev
```

Before running this command you will have to [pick the topic Id ](https://docs.allora.network/docs/existing-allora-appchain-topics)you wish to generate inference for after which you can run this command with the topic Id. The command will auto-create some files, the most important of which is the `dev-docker-compose.yaml`file which is an already complete docker-compose that you can run immediately to see your worker and head nodes running perfectly on your local machine. You can edit the files as you wish. for instance the `main.py` is meant for you to call your inference server, hence you will have to edit the sample code with actual URLs and logic as you prefer.
Before running this command you will have to [pick the topic Id ](https://docs.allora.network/docs/existing-allora-appchain-topics)you wish to generate inference for after which you can run this command with the topic Id. The command will auto-create some files, the most important of which is the `dev-docker-compose.yaml`file which is an already complete docker-compose that you can run immediately to see your worker/reputer and head nodes running perfectly on your local machine. You can edit the files as you wish. for instance the `main.py` is meant for you to call your inference server, hence you will have to edit the sample code with actual URLs and logic as you prefer.

When you run the docker-compose (`docker-compose -f dev-docker-compose.yaml up --build`), maybe after you have written and tested your logic in `main.py`, you then should be seeing the logs from the nodes, and you should be able to make a request to your head node and see it get a response from the worker node. Note that in production, you won't be the one to make the inference request, as the Allora chain will do this at the cadence provided by the topic creator.
When you run the docker-compose (`docker-compose -f dev-docker-compose.yaml up --build`), maybe after you have written and tested your logic in `main.py`, you then should be seeing the logs from the nodes, and you should be able to make a request to your head node and see it get a response from the worker/reputer node. Note that in production, you won't be the one to make the inference request, as the Allora chain will do this at the cadence provided by the topic creator.

You can test your node by running the following curl command:

Expand Down Expand Up @@ -56,12 +64,24 @@ curl --location 'http://localhost:6000/api/v1/functions/execute' --header 'Accep

The `<TOPIC_ID>` needs to be [an existing topic on the chain](https://docs.allora.network/docs/existing-allora-appchain-topics). The `<argument>` is what the topic is expecting to receive to perform the inference (as an indication to test, you can use the `DefaultArg` value from the topic on-chain, e.g. for ETH prediction topic, it should be `"ETH"`).

### 3. Initialize the worker for production
### Initialize the worker/reputer for production

Your worker node is now ready to be deployed, the `main.py` has been modified, all env variables passed, and the worker node is running locally and you are now ready to deploy your worker to run in the production environment. The following command will handle the generation of the `prod-docker-compose.yaml` file which contains all the keys and parameters needed for your worker to function perfectly in production.
Your worker/reputer node is now ready to be deployed, the `main.py` has been modified, all env variables passed, and the worker/reputer node is running locally and you are now ready to deploy your worker/reputer to run in the production environment. The following command will handle the generation of the `prod-docker-compose.yaml` file which contains all the keys and parameters needed for your worker/reputer to function perfectly in production.

```shell
allocmd init --env prod
allocmd generate worker --env prod
```

By running this command, `prod-docker-compose.yaml` will be generated with appropriate keys and parameters. You can now run the docker-compose file or deploy the whole codebase in your preferred cloud instance. At this stage, your worker should be responding to inference request from the Allora Chain.

### Initialize validator production
```shell
allocmd generate validator --name <validator-name> --network <testnet or mainnet>
```
The above command can generate validator files and you can then use docker-compose to deploy

### Fund account address
```shell
allocmd fund <address>
```
The above command takes address and fund the account with Allora Faucet
147 changes: 75 additions & 72 deletions allocmd/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
from jinja2 import Environment, FileSystemLoader
from importlib.resources import files
from termcolor import colored, cprint
from .utilities.utils import generate_all_files, print_allora_banner, run_key_generate_command, deployWorker, deployValidator, generateWorkerAccount, generateProdCompose
from .utilities.typings import Command
from .utilities.utils import generate_all_files, print_allora_banner, run_key_generate_command, deployWorker, deployValidator, generateWorkerAccount, generateProdCompose, check_docker_running, blocklessNode, fundAddress
from .utilities.typings import Command, BlocklessNodeType
from .utilities.constants import cliVersion

template_path = files('allocmd').joinpath('templates')
Expand All @@ -17,82 +17,85 @@ def cli():
"""A CLI Tool that handles creation of an Allora Worker Node"""
pass

@click.command()
@cli.group()
def generate():
"""generate scaffolded files and directories depending on the command type passed."""
pass

@generate.command()
@click.option('--env', 'environment', required=True, type=click.Choice(['dev', 'prod']), help='Environment to generate for')
@click.option('--name', required=False, help='Name of the worker.')
@click.option('--topic', required=False, type=int, help='The topic ID the worker is registered with.')
def init(environment, name=None, topic=None):
def worker(environment, name=None, topic=None):
"""Initialize your Allora Worker Node with necessary boilerplates"""

if environment == 'dev':
if topic is None:
cprint("You must provide topic id when running development init.", 'red')
return
elif name is None:
cprint("You must provide name when running development init.", 'red')
return
blocklessNode(environment, env, BlocklessNodeType.worker.name, name, topic)

@generate.command()
@click.option('--env', 'environment', required=True, type=click.Choice(['dev', 'prod']), help='Environment to generate for')
@click.option('--name', required=False, help='Name of the reputer.')
@click.option('--topic', required=False, type=int, help='The topic ID the reputer is registered with.')
def reputer(environment, name=None, topic=None):
"""Initialize your Allora Reputer Node with necessary boilerplates"""

blocklessNode(environment, env, BlocklessNodeType.reputer.name, name, topic)


@generate.command()
@click.option('--name',required=True, help='Name of the validator.')
@click.option('--network', required=True, type=click.Choice(['testnet', 'edgenet']), help='Your preffered chain network to run the validator on.')
def validator(name=None, network=None):
"""Initialize your Allora Worker Node with necessary boilerplates"""

if not check_docker_running():
cprint("Docker is not running, please start docker before running this command", 'red')
return

print_allora_banner()
cprint("Welcome to the Allora CLI!", 'green', attrs=['bold'])
print(colored("Allora CLI assists in the seamless creation and deployment of Allora validator nodes", 'yellow'))
cprint(f"\nThis command will generate some files in the directory named '{name}'.", 'cyan')

if click.confirm(colored("\nWould you like to proceed?", 'white', attrs=['bold']), default=True):
cprint("\nProceeding with the creation of validator node directory...", 'green')

os.makedirs(f"{name}/validator/scripts", exist_ok=True)

file_configs = [
{
"template_name": "validator-docker-compose.yaml.j2",
"file_name": "validator-docker-compose.yaml",
"context": {"val_name": name, "network": network}
},
{
"template_name": "start-validator.sh.j2",
"file_name": "scripts/start-validator.sh",
"context": {"val_name": name, "network": network}
},
]

generate_all_files(env, file_configs, Command.INIT, "validator", name)

subprocess.run(['chmod', '+x', f'{name}/validator/scripts/start-validator.sh'], check=True)
else:
cprint("\nOperation cancelled.", 'red')

@click.command()
@click.option('--address',required=True, help='the account address to be funded.')
@click.option('--network', required=True, type=click.Choice(['testnet', 'edgenet']), help='Your preffered chain network to fund address from.')
def fund(address=None, network=None):
"""fund allora account address"""

cprint(f"\nfunding allora address: {address}", 'green')
cprint(f"Funding account with {network} tokens", 'green')
faucet_url = f'https://faucet.{network}.allora.network/'
fundAddress(faucet_url, address, network)

cli.add_command(fund)




print_allora_banner()
cprint("Welcome to the Allora CLI!", 'green', attrs=['bold'])
print(colored("Allora CLI assists in the seamless creation and deployment of Allora worker nodes", 'yellow'))
print(colored("to provide model inference to the Allora Chain.", 'yellow'))
cprint(f"\nThis command will generate some files in the directory named '{name}'.", 'cyan')

if click.confirm(colored("\nWould you like to proceed?", 'white', attrs=['bold']), default=True):
cprint("\nProceeding with the creation of worker node directory...", 'green')

head_peer_id = run_key_generate_command(name)

file_configs = [
{
"template_name": "Dockerfile.j2",
"file_name": "Dockerfile",
"context": {}
},
{
"template_name": "main.py.j2",
"file_name": "main.py",
"context": {}
},
{
"template_name": "dev-docker-compose.yaml.j2",
"file_name": "dev-docker-compose.yaml",
"context": {"head_peer_id": head_peer_id, "topic_id": topic}
},
{
"template_name": "requirements.txt.j2",
"file_name": "requirements.txt",
"context": {}
},
{
"template_name": "gitignore.j2",
"file_name": ".gitignore",
"context": {}
},
{
"template_name": "env.j2",
"file_name": ".env",
"context": {}
},
{
"template_name": "config.yaml.j2",
"file_name": "config.yaml",
"context": {"name": name, "topic_id": topic}
}
]

generate_all_files(env, file_configs, Command.INIT, name)

generateWorkerAccount(name)
else:
cprint("\nOperation cancelled.", 'red')
elif environment == 'prod':
devComposePath = os.path.join(os.getcwd(), 'dev-docker-compose.yaml')
if not os.path.exists(devComposePath):
cprint("You must initialize the worker on dev please run allocmd init --env dev --name <worker name> --topic <topic id> and then run the prod init in the directory created", 'red')
else:
generateProdCompose(env)

# @click.command()
# @click.option('--logs', is_flag=True, help="Follow logs immediately after starting services.")
Expand Down Expand Up @@ -165,7 +168,7 @@ def deploy(type_):



cli.add_command(init)
# cli.add_command(init)
# cli.add_command(run)
# cli.add_command(terminate)
# cli.add_command(deploy)
Expand Down
10 changes: 2 additions & 8 deletions allocmd/templates/config.yaml.j2
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
# place all worker configuration variables here
# place all {{ b7s_type }} configuration variables here

name: {{ name }}
faucet_url: https://faucet.testnet.allora.network/
worker:
{{ b7s_type }}:
boot_nodes: /dns4/head-0.testnet.allora.network/tcp/9010/p2p/12D3KooWGAA1C2UNyj51QaGX5aXnVzRqeaqhyhUFFJixdqshwWC3
chain_rpc_address: https://allora-rpc.testnet.allora.network/
chain_topic_id: {{ topic_id }}
mnemonic: ""
hex_coded_pk: ""
address: ""
validator:
image_uri: alloranetwork/allora-chain
image_tag: latest
mnemonic: ""
hex_coded_pk: ""
address: ""
14 changes: 7 additions & 7 deletions allocmd/templates/dev-docker-compose.yaml.j2
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
version: "3.8"
services:
worker:
container_name: worker
{{ b7s_type }}:
container_name: {{ b7s_type }}
build: .
command:
- allora-node
- --role=worker
- --peer-db=/data/worker/peer-database
- --function-db=/data/worker/function-database
- --peer-db=/data/{{ b7s_type }}/peer-database
- --function-db=/data/{{ b7s_type }}/function-database
- --runtime-path=/app/runtime
- --runtime-cli=bls-runtime
- --workspace=/data/worker/workspace
- --private-key=/data/worker/key/priv.bin
- --workspace=/data/{{ b7s_type }}/workspace
- --private-key=/data/{{ b7s_type }}/key/priv.bin
- --log-level=debug
- --port=9011
- --topic={{ topic_id }}
Expand All @@ -27,7 +27,7 @@ services:
networks:
b7s-local:
aliases:
- worker
- {{ b7s_type }}
ipv4_address: 172.19.0.5

head:
Expand Down
51 changes: 51 additions & 0 deletions allocmd/templates/start-validator.sh.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/bin/bash
set -exu

NETWORK="{{ network }}"
GENESIS_URL="https://raw.githubusercontent.com/allora-network/networks/main/${NETWORK}/genesis.json"
PEERS_URL="https://raw.githubusercontent.com/allora-network/networks/main/${NETWORK}/peers.txt"
BLOCKLESS_API_URL="https://heads.${NETWORK}.allora.network"

APP_HOME="/data"
INIT_FLAG="${APP_HOME}/.initialized"
MONIKER="{{ val_name }}"
KEYRING_BACKEND=test #! Use test for simplicity, you should decide which backend to use !!!
GENESIS_FILE="${APP_HOME}/config/genesis.json"
DENOM="uallo"

echo "To re-initiate the node, remove the file: ${INIT_FLAG}"
if [ ! -f $INIT_FLAG ]; then
rm -rf ${APP_HOME}/config

#* Init node
allorad --home=${APP_HOME} init ${MONIKER} --chain-id=${NETWORK} --default-denom $DENOM

#* Download genesis
rm -f $GENESIS_FILE
curl -Lo $GENESIS_FILE $GENESIS_URL

#* Import allora account, priv_validator_key.json and node_key.json from the vault here
#* Here create a new allorad account
allorad --home $APP_HOME keys add ${MONIKER} --keyring-backend $KEYRING_BACKEND > $APP_HOME/${MONIKER}.account_info 2>&1

#* Setup allorad client
allorad --home=${APP_HOME} config set client chain-id ${NETWORK}
allorad --home=${APP_HOME} config set client keyring-backend $KEYRING_BACKEND

#* Create symlink for allorad config
ln -sf . ${APP_HOME}/.allorad

touch $INIT_FLAG
fi
echo "Node is initialized"

PEERS=$(curl -s ${PEERS_URL})

echo "Starting validator node"
allorad \
--home=${APP_HOME} \
start \
--moniker=${MONIKER} \
--minimum-gas-prices=0${DENOM} \
--rpc.laddr=tcp://0.0.0.0:26657 \
--p2p.persistent_peers=${PEERS}
17 changes: 17 additions & 0 deletions allocmd/templates/validator-docker-compose.yaml.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
version: "3"

services:
validator0:
container_name: {{ val_name }}
image: "alloranetwork/allora-chain:v0.0.10"
environment:
- NETWORK={{ network }}
- MONIKER={{ val_name }}
- APP_HOME=/data
volumes:
- ./data:/data
- ./scripts/:/scripts
ports:
- "26656-26657:26656-26657"
user: "0:0"
entrypoint: /scripts/start-validator.sh
3 changes: 1 addition & 2 deletions allocmd/utilities/constants.py
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
cliVersion = "1.0.4"

cliVersion = "2.0.0"
5 changes: 5 additions & 0 deletions allocmd/utilities/typings.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,8 @@
class Command(Enum):
DEPLOY = auto()
INIT = auto()


class BlocklessNodeType(Enum):
worker = auto()
reputer = auto()
Loading

0 comments on commit e0ca785

Please sign in to comment.