Infernet
Node
Onchain

On-chain subscriptions

We move on to the on-chain workflow of the Infernet Node. To test it, we will need to scaffold some additional components. Specifically, we will

  1. Deploy an Infernet Node ready to service our requests
  2. Deploy an Anvil (opens in a new tab) local testnet node with the Infernet SDK contracts already setup (via our infernet-anvil (opens in a new tab) image)
  3. Deploy an simple Infernet Consumer contract for our on-chain demo

Then, we will:

  1. Create an on-chain subscription with our newly deployed consumer contract
  2. Monitor the subscription's lifecycle, from the node listening for and processing it, to the results being sent back to the consumer contract

Deploy Infernet Node with Anvil

We first deploy an Infernet Node with the hello-world container and a local Anvil chain instance with the Infernet SDK contracts pre-deployed. This step is nearly identical to the off-chain requests setup, with the only difference being the choice of the anvil chain.

ℹ️

We highly recommend you go through the off-chain requests walkthrough first. This will give you a better understanding of the Infernet Node and now to deploy and interact with it.

Configure the Infernet Node with the anvil chain:

infernet-cli config anvil --skip

The output should look something like this:

No version specified. Using latest: v1.3.0
Using configurations:
   Chain = 'anvil'
   Version = '1.3.0'
   GPU support = disabled
   Output dir = 'deploy'

Stored base configurations to '/root/deploy'.
To configure services:
  - Use `infernet-cli add-service`
  - Or edit config.json directly

Add the hello-world container to the node configuration:

infernet-cli add-service hello-world --skip

The output should look something like this:

Version not provided. Using latest version 'latest'.
Successfully added service 'hello-world' to config.json.

Now you can start all services:

infernet-cli start

Verify the setup

In another terminal, you can run docker container ls to see a list of the now running containers:

docker container ls

The output should look something like this:

CONTAINER ID   IMAGE                                        COMMAND                  CREATED          STATUS          PORTS                  NAMES
ed7ec08617aa   ritualnetwork/hello-world-infernet:latest    "gunicorn app:create…"   3 minutes ago   Up 3 minutes   0.0.0.0:3000->3000/tcp   hello-world
22be7ee424f1   ritualnetwork/infernet-node:1.3.1            "/app/entrypoint.sh"     3 minutes ago   Up 3 minutes   0.0.0.0:4000->4000/tcp   infernet-node
f2a0b22a8220   fluent/fluent-bit:3.1.4                      "/fluent-bit/bin/flu…"   3 minutes ago   Up 3 minutes   2020/tcp, 24224/tcp      infernet-fluentbit
d802f062ab93   ritualnetwork/infernet-anvil:1.0.0           "anvil --host 0.0.0.…"   3 minutes ago   Up 3 minutes   0.0.0.0:8545->3000/tcp   infernet-anvil
b9d42788dd24   redis:7.4.0                                  "docker-entrypoint.s…"   3 minutes ago   Up 3 minutes   0.0.0.0:6379->6379/tcp   infernet-redis

Notice that you now have an Anvil node running on port 8545 and an Infernet Node on port 4000.

By default, the infernet-anvil (opens in a new tab) has the Infernet SDK contracts pre-deployed for you:

  • Coordinator: 0x2E983A1Ba5e8b38AAAeC4B440B9dDcFBf72E15d1
  • Primary node: 0x70997970C51812dc3A010C7d01b50e0d17dc79C8
  • Registry: 0x663F3ad617193148711d28f5334eE4Ed07016602
  • Inbox: 0x8438Ad1C834623CfF278AB6829a248E37C2D7E3f
  • Reader: 0xBC9129Dc0487fc2E169941C75aABC539f208fb01
  • Fee: 0x6e989C01a3e3A94C973A62280a72EC335598490e
  • Wallet Factory: 0xF6168876932289D073567f347121A267095f3DD6
  • Wallet (owned by the primary node): 0x60985ee8192B322c3CAbA97A9A9f7298bdc4335C

Note, that the primary node address assigned is the default second account in Anvil. This account is prefunded with 10,000 ETH with private key: 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d.

Deploy a consumer

Next, we must deploy a consumer contract that implements the simple Infernet SDK interface.

Our SaysGM (opens in a new tab) contract is a simple, example consumer. All this contract does is request a compute output from an Infernet Node, and upon receiving the result, use the forge console to print the result. We can deploy this contract via the associated forge project (opens in a new tab).

Anvil logs

During this process, it is useful to look at the logs of the Anvil node to see what's going on. To follow the logs, in a new terminal, run:

docker logs -f infernet-anvil

Get source code

ℹ️

If you deployed the Infernet Node "From Source" in Step 1, you can skip this step.

Clone the starter repository locally:

# Clone locally
git clone --recurse-submodules https://github.com/ritual-net/infernet-container-starter

Installing dependencies

Before we can deploy the contract, it is important to install relevant dependencies:

# Navigate to the repository
cd infernet-container-starter
 
# cd in, install, and cd out
cd projects/hello-world/contracts && forge install --no-commit foundry-rs/forge-std && forge install --no-commit ritual-net/infernet-sdk && cd ../../../

Deploying the contract

To deploy the SaysGM consumer contract, in another terminal, run:

make deploy-contracts project=hello-world

You should expect to see similar Anvil logs:

eth_sendRawTransaction
eth_getTransactionReceipt

Transaction: 0x23ca6b1d1823ad5af175c207c2505112f60038fc000e1e22509816fa29a3afd6
Contract created: 0x13D69Cf7d6CE4218F646B759Dcf334D82c023d8e
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

From our logs, we can see that the SaysGM contract has been deployed to address 0x13D69Cf7d6CE4218F646B759Dcf334D82c023d8e.

For reference, the SaysGM contract is a simple Consumer that looks like this:

function sayGM() public {
  _requestCompute(
      "hello-world",
      bytes("Good morning!"),
      1, // redundancy
      address(0), // paymentToken
      0, // paymentAmount
      address(0), // wallet
      address(0) // prover
  );
}

Call the contract

Now, let's call the contract initiating a request to the Infernet Node. In the same terminal, run:

make call-contract project=hello-world

For reference, the CallContract script looks like this:

contract CallContract is Script {
    function run() public {
        // Setup wallet
        uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
        vm.startBroadcast(deployerPrivateKey);
 
        SaysGM saysGm = SaysGM(0x13D69Cf7d6CE4218F646B759Dcf334D82c023d8e);
 
        saysGm.sayGM();
 
        vm.stopBroadcast();
    }
}

You should first expect to see an initiation transaction sent to the SaysGm contract:

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

Shortly after that you should see another transaction submitted from the Infernet Node which is the result of your on-chain subscription and its associated job request:

eth_chainId
eth_sendRawTransaction


_____  _____ _______ _    _         _
|  __ \|_   _|__   __| |  | |  /\   | |
| |__) | | |    | |  | |  | | /  \  | |
|  _  /  | |    | |  | |  | |/ /\ \ | |
| | \ \ _| |_   | |  | |__| / ____ \| |____
|_|  \_\_____|  |_|   \____/_/    \_\______|


subscription Id 1
interval 1
redundancy 1
node 0x70997970C51812dc3A010C7d01b50e0d17dc79C8
input:
0x
output:
0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000607b276f7574707574273a202268656c6c6f2c20776f726c64212c20796f757220696e707574207761733a207b27736f75726365273a20302c202764617461273a20273437366636663634323036643666373236653639366536373231277d227d
decoded output:  {'output': "hello, world!, your input was: {'source': 0, 'destination': 0, 'data': '476f6f64206d6f726e696e6721', 'requires_proof': False}"}
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 now confirm that the address of the Infernet Node (see the logged node parameter in the Anvil logs above) matches the address of the node we setup by default for our Infernet Node.

Also notice that the decoded output above should exactly match the off-chain job results.

Congratulations! 🎉 You have successfully created an on-chain subscription request!

Additional Resources

From here, you can:

  1. Continue to the payments quickstart
  2. See the source code (opens in a new tab) for our SaysGM consumer contract
  3. Read more about the Infernet SDK architecture
  4. Find out more about the Infernet Node architecture