feat: publishing infernet-container-starter v0.2.0
This commit is contained in:
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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={
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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.
|
||||
|
231
projects/hello-world/hello-world.md
Normal file
231
projects/hello-world/hello-world.md
Normal 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)
|
Reference in New Issue
Block a user