feat: publishing infernet-container-starter v0.2.0

This commit is contained in:
ritual-all
2024-03-29 10:50:13 -04:00
parent 41aaa152e6
commit 4545223364
155 changed files with 6086 additions and 257 deletions

View File

@ -4,15 +4,20 @@ WORKDIR /app
ENV PYTHONUNBUFFERED 1
ENV PYTHONDONTWRITEBYTECODE 1
ENV PIP_NO_CACHE_DIR 1
ENV RUNTIME docker
ENV PYTHONPATH src
WORKDIR /app
RUN apt-get update
RUN apt-get install -y git curl
# install uv
ADD --chmod=755 https://astral.sh/uv/install.sh /install.sh
RUN /install.sh && rm /install.sh
COPY src/requirements.txt .
RUN pip install --upgrade pip && pip install -r requirements.txt
RUN /root/.cargo/bin/uv pip install --system --no-cache -r requirements.txt
COPY src src

View File

@ -3,8 +3,8 @@
In this tutorial, we'll create a simple hello-world container that can be used
with infernet.
> [!NOTE]
> This directory `containers/hello-world` already includes the final result
> [!NOTE]
> This directory `containers/hello-world` already includes the final result
> of this tutorial. Run the following tutorial in a new directory.
Let's get started! 🎉
@ -88,7 +88,7 @@ This is a simple Dockerfile that:
3. Copies the source code
4. Runs the app on port `3000`
> [!IMPORTANT]
> [!IMPORTANT]
> App must be exposed on port `3000`. Infernet's orchestrator
> will always assume that the container apps are exposed on that port within the container.
> Users can then remap this port to any port that they want on the host machine
@ -127,7 +127,7 @@ docker run --rm -p 3000:3000 --name hello hello-world
In another terminal, run:
```
curl localhost:3000
curl "localhost:3000"
```
It should return something like:
@ -159,5 +159,5 @@ The output should be something like:
Your users will never call this endpoint directly. Instead, they will:
1. Either [create an off-chain job request](../../../README.md#L36) through the node API
1. Either [create an off-chain job request](../hello-world#L36) through the node API
2. Or they will make a subscription on their contracts

View File

@ -1,9 +1,10 @@
from time import sleep
from typing import Any
import requests
def hit_server_directly():
def hit_server_directly() -> None:
print("hello")
r = requests.get("http://localhost:3000/")
print(r.status_code)
@ -11,7 +12,7 @@ def hit_server_directly():
print("server response", r.text)
def poll_until_complete(id: str):
def poll_until_complete(id: str) -> Any:
status = "running"
r = None
while status == "running":
@ -24,11 +25,12 @@ def poll_until_complete(id: str):
status = r.get("status")
print("status", status)
if status != "running":
return r
break
sleep(1)
return r
def create_job_through_node():
def create_job_through_node() -> None:
r = requests.post(
"http://localhost:4000/api/jobs",
json={

View File

@ -1,2 +1,2 @@
Flask>=3.0.0,<4.0.0
gunicorn>=21.2.0,<22.0.0
gunicorn>=21.2.0,<22.0.0

View File

@ -11,4 +11,4 @@ deploy:
# calling sayGM()
call-contract:
@PRIVATE_KEY=$(sender) forge script script/CallContract.s.sol:CallContract --broadcast --rpc-url $(RPC_URL)
@PRIVATE_KEY=$(sender) forge script script/CallContract.s.sol:CallContract --broadcast --rpc-url $(RPC_URL)

View File

@ -1,15 +1,15 @@
# `Hello-World` Consumer Contracts
This is a [foundry](https://book.getfoundry.sh/) project that implements a simple Consumer
contract, [`SaysGm`](./src/SaysGM.sol).
contract, [`SaysGm`](./src/SaysGM.sol).
This readme explains how to compile and deploy the contract to the Infernet Anvil Testnet network.
This readme explains how to compile and deploy the contract to the Infernet Anvil Testnet network.
For a detailed tutorial on how to write a consumer contract, refer to the [tutorial doc](./Tutorial.md).
> [!IMPORTANT]
> Ensure that you are running the following scripts with the Infernet Anvil Testnet network.
> The [tutorial](./../../../README.md) at the root of this repository explains how to
> [!IMPORTANT]
> Ensure that you are running the following scripts with the Infernet Anvil Testnet network.
> The [tutorial](../hello-world) at the root of this repository explains how to
> bring up an infernet node.
### Installing the libraries
@ -27,7 +27,7 @@ forge compile
### Deploying the contracts
The deploy script at `script/Deploy.s.sol` deploys the `SaysGM` contract to the Infernet Anvil Testnet network.
We have the [following make target](./Makefile#L9) to deploy the contract. Refer to the Makefile
We have the [following make target](./Makefile#L9) to deploy the contract. Refer to the Makefile
for more understanding around the deploy scripts.
```bash
make deploy
@ -35,10 +35,9 @@ make deploy
### Requesting a job
We also have a script called `CallContract.s.sol` that requests a job to the `SaysGM` contract.
Refer to the [script](./script/CallContract.s.sol) for more details. Similar to deployment,
Refer to the [script](./script/CallContract.s.sol) for more details. Similar to deployment,
you can run that script using the following convenience make target.
```bash
make call-contract
```
Refer to the [Makefile](./Makefile#L14) for more details.

View File

@ -218,7 +218,7 @@ PRIVATE_KEY=0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a \
```
### Using a `Makefile`
To make running these commands easier, we can add them to a `Makefile`. This allows
To make running these commands easier, we can add them to a `Makefile`. This allows
us to run `make deploy` and `make call` instead of typing out the full command every time.
Refer to [this project's Makefile](./Makefile) for an example.
@ -226,4 +226,4 @@ Refer to [this project's Makefile](./Makefile) for an example.
### 🎉 Done!
Congratulations! You've successfully created a contract that requests compute from
our `hello-world` container.
our `hello-world` container.

View File

@ -0,0 +1,231 @@
# Hello, World!
Welcome to the first tutorial of Infernet! In this tutorial we will guide you through the process of setting up and
running an Infernet Node, and then demonstrate how to create and monitor off-chain compute jobs and on-chain subscriptions.
To interact with infernet, one could either create a job by accessing an infernet node directly through it's API (we'll
refer to this as an off-chain job), or by creating a subscription on-chain (we'll refer to this as an on-chain job).
## Requesting an off-chain job: Hello World!
This project is a simple [flask-app](container/src/app.py) that is compatible with `infernet`, and simply
[echoes what you send to it](container/src/app.py#L16).
### Install Docker & Verify Installation
To run this, you'll need to have docker installed. You can find instructions for installing docker [here](https://docs.docker.com/install/).
After installing & running docker, you can verify that the docker daemon is running by running the following command:
```bash copy
docker --version
# Docker version 25.0.2, build 29cf629
```
### Clone the starter repository
```bash copy
# Clone locally
git clone --recurse-submodules https://github.com/ritual-net/infernet-container-starter
# Navigate to the repository
cd infernet-container-starter
```
### Build the `hello-world` container
Once inside the repository directory, you can run a simple command to build the `hello-world` container:
```bash copy
make build-container project=hello-world
```
### Running Locally
Then, from the top-level project directory, Run the following make command:
```
make deploy-container project=hello-world
```
This will deploy an infernet node along with the `hello-world` image.
### Creating an off-chain job through the API
You can create an off-chain job by posting to the `node` directly.
```bash
curl -X POST "http://127.0.0.1:4000/api/jobs" \
-H "Content-Type: application/json" \
-d '{"containers":["hello-world"], "data": {"some": "input"}}'
# returns
{"id":"d5281dd5-c4f4-4523-a9c2-266398e06007"}
```
This will return the id of that job.
### Getting the status/result/errors of a job
You can check the status of a job like so:
```bash
curl -X GET "http://127.0.0.1:4000/api/jobs?id=d5281dd5-c4f4-4523-a9c2-266398e06007"
# returns
[{"id":"d5281dd5-c4f4-4523-a9c2-266398e06007", "result":{"container":"hello-world","output": {"output":"hello, world!, your input was: {'source': 1, 'data': {'some': 'input'}}"}} ,"status":"success"}]
```
### Configuration
This project already comes with a pre-filled config file. The config file for the hello-world project is located
[here](container/config.json):
```bash
projects/hello-world/config.json
```
## Requesting an on-chain job
In this section we'll go over how to request an on-chain job in a local anvil node.
### Infernet's Anvil Testnet
To request an on-chain job, you'll need to deploy contracts using the infernet sdk.
We already have a public [anvil node](https://hub.docker.com/r/ritualnetwork/infernet-anvil) docker image which has the
corresponding infernet sdk contracts deployed, along with a node that has
registered itself to listen to on-chain subscription events.
* Coordinator Address: `0x5FbDB2315678afecb367f032d93F642f64180aa3`
* Node Address: `0x70997970C51812dc3A010C7d01b50e0d17dc79C8` (This is the second account in the anvil's accounts.)
### Deploying Infernet Node & Infernet's Anvil Testnet
This step is similar to the section above:
```bash
project=hello-world make deploy-container
```
In another terminal, run `docker container ls`, you should see something like this
```bash
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c2ca0ffe7817 ritualnetwork/infernet-anvil:0.0.0 "anvil --host 0.0.0.…" 9 seconds ago Up 8 seconds 0.0.0.0:8545->3000/tcp anvil-node
0b686a6a0e5f ritualnetwork/hello-world-infernet:0.0.2 "gunicorn app:create…" 9 seconds ago Up 8 seconds 0.0.0.0:3000->3000/tcp hello-world
28b2e5608655 ritualnetwork/infernet-node:0.1.1 "/app/entrypoint.sh" 10 seconds ago Up 10 seconds 0.0.0.0:4000->4000/tcp deploy-node-1
03ba51ff48b8 fluent/fluent-bit:latest "/fluent-bit/bin/flu…" 10 seconds ago Up 10 seconds 2020/tcp, 0.0.0.0:24224->24224/tcp deploy-fluentbit-1
a0d96f29a238 redis:latest "docker-entrypoint.s…" 10 seconds ago Up 10 seconds 0.0.0.0:6379->6379/tcp deploy-redis-1
```
You can see that the anvil node is running on port `8545`, and the infernet
node is running on port `4000`. Same as before.
### Deploying Consumer Contracts
We have a [sample forge project](./contracts) which contains
a simple consumer contract, [`SaysGM`](contracts/src/SaysGM.sol).
All this contract does is to request a job from the infernet node, and upon receiving
the result, it will use the `forge` console to print the result.
**Anvil Logs**: First, it's useful to look at the logs of the anvil node to see what's going on. In
a new terminal, run `docker logs -f anvil-node`.
**Deploying the contracts**: In another terminal, run the following command:
```bash
project=hello-world make deploy-contracts
```
You should be able to see the following logs in the anvil logs:
```bash
eth_sendRawTransaction
eth_getTransactionReceipt
Transaction: 0x23ca6b1d1823ad5af175c207c2505112f60038fc000e1e22509816fa29a3afd6
Contract created: 0x663f3ad617193148711d28f5334ee4ed07016602
Gas used: 476669
Block Number: 1
Block Hash: 0x6b026b70fbe97b4a733d4812ccd6e8e25899a1f6c622430c3fb07a2e5c5c96b7
Block Time: "Wed, 17 Jan 2024 22:17:31 +0000"
eth_getTransactionByHash
eth_getTransactionReceipt
eth_blockNumber
```
We can see that a new contract has been created at `0x663f3ad617193148711d28f5334ee4ed07016602`.
That's the address of the `SaysGM` contract.
### Calling the contract
Now, let's call the contract. In the same terminal, run the following command:
```bash
project=hello-world make call-contract
```
You should first see that a transaction was sent to the `SaysGm` contract:
```bash
eth_getTransactionReceipt
Transaction: 0xe56b5b6ac713a978a1631a44d6a0c9eb6941dce929e1b66b4a2f7a61b0349d65
Gas used: 123323
Block Number: 2
Block Hash: 0x3d6678424adcdecfa0a8edd51e014290e5f54ee4707d4779e710a2a4d9867c08
Block Time: "Wed, 17 Jan 2024 22:18:39 +0000"
eth_getTransactionByHash
```
Then, right after that you should see another transaction submitted by the `node`,
which is the result of the job request:
```bash
eth_chainId
eth_sendRawTransaction
_____ _____ _______ _ _ _
| __ \|_ _|__ __| | | | /\ | |
| |__) | | | | | | | | | / \ | |
| _ / | | | | | | | |/ /\ \ | |
| | \ \ _| |_ | | | |__| / ____ \| |____
|_| \_\_____| |_| \____/_/ \_\______|
subscription Id 1
interval 1
redundancy 1
node 0x70997970C51812dc3A010C7d01b50e0d17dc79C8
input:
0x
output:
0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000607b276f7574707574273a202268656c6c6f2c20776f726c64212c20796f757220696e707574207761733a207b27736f75726365273a20302c202764617461273a20273437366636663634323036643666373236653639366536373231277d227d
proof:
0x
Transaction: 0x949351d02e2c7f50ced2be06d14ca4311bd470ec80b135a2ce78a43f43e60d3d
Gas used: 94275
Block Number: 3
Block Hash: 0x57ed0cf39e3fb3a91a0d8baa5f9cb5d2bdc1875f2ad5d6baf4a9466f522df354
Block Time: "Wed, 17 Jan 2024 22:18:40 +0000"
eth_blockNumber
eth_newFilter
```
We can see that the address of the `node` matches the address of the node in
our ritual anvil node.
### Next Steps
To learn more about on-chain requests, check out the following resources:
1. [Tutorial](contracts/Tutorial.md) on this project's consumer smart contracts.
2. [Infernet Callback Consumer Tutorial](https://docs.ritual.net/infernet/sdk/consumers/Callback)
3. [Infernet Nodes Docoumentation](https://docs.ritual.net/infernet/node/introduction)
4. [Infernet-Compatible Containers](https://docs.ritual.net/infernet/node/containers)