πŸ“‹ Complete Manual Walkthrough

This section provides a step-by-step guide to manually run the Zenith network, deploy a Virtual Blockchain, and execute transactions. This replicates the automated end-to-end test but with detailed explanations for each step.

Phase 1: Building Components

First, we need to build all the necessary components:

# Build all Rust components (VB runtimes, guest programs, tools)
# This compiles the Substrate VB example, genesis generator, input generator, and libraries
cargo build --release
# Build the Zenith daemon (the main node software)
go build -o target/release/zenithd ./cmd/zenithd

What's happening:

  • Cargo builds the Substrate VB runtime compiled for RISC-V target
  • Genesis generator creates initial blockchain state
  • Input generator creates transaction data for testing
  • Zenith daemon (zenithd) is the main node software for the network

Verify the build was successful:

ls target/release/genesis-gen # Genesis state generator
ls target/release/input-gen # Transaction input generator
ls target/release/zenithd # Zenith daemon
ls target/release/libzenith.so # Zenith library with RISC0 bindings
ls target/riscv-guest/substrate-vb/method/riscv32im-risc0-zkvm-elf/release/method.bin # VB image

Before deploying to the network, let's test everything works locally:

# Create a temporary directory for our test
TEMP_DIR=$(mktemp -d)
cd $TEMP_DIR
# Generate genesis state for the Substrate VB
$PROJECT_DIR/target/release/genesis-gen

What's happening: The genesis generator creates the initial state for our Substrate VB, including the merkle tree root that represents the blockchain's starting state.

You should see files created:

ls -la
# genesis.bin - Binary genesis state
# genesis.json - Human-readable genesis info (after next step)

Apply the genesis configuration:

bash $PROJECT_DIR/examples/substrate-vb/genesis-gen/apply_genesis.sh

What's happening: This script creates a local merkle tree from the genesis state:

  • Initializes a merkle tree in ./tree directory using zenithd merkletree init
  • Extracts key-value pairs from genesis.json and populates the tree
  • Calculates the merkle root (state root) from the populated tree
  • Adds the state_root and genesis_hash back to genesis.json

Extract the important values:

GENESIS_STATE_ROOT=$(cat genesis.json | jq -r '.state_root')
GENESIS_HASH=$(cat genesis.json | jq -r '.genesis_hash')
echo "Genesis State Root: $GENESIS_STATE_ROOT"
echo "Genesis Hash: $GENESIS_HASH"

Generate test transaction input:

$PROJECT_DIR/target/release/input-gen --state-root $GENESIS_STATE_ROOT --genesis-hash $GENESIS_HASH

What's happening: This creates sample transactions (extrinsics) that our VB will process in the first block.

Test local execution (this proves everything works):

VB_IMAGE="$PROJECT_DIR/target/riscv-guest/substrate-vb/method/riscv32im-risc0-zkvm-elf/release/method.bin"
$PROJECT_DIR/target/release/zenithd prove -e $VB_IMAGE -i input.bin -o output.bin -r receipt.bin -t ./tree

What's happening: This executes the VB locally in the RISC0 environment and generates a zero-knowledge proof. If successful, you'll see messages like "Extrinsic 0 executed successfully".

Phase 3: Network Deployment

Now let's start the actual Zenith network:

3.1 Start Zenith Validator Node

In a new terminal, start the validator:

# Create validator configuration directory
mkdir -p ./tests/assets/validator-root
# Start the containerized Zenith validator with IPFS
podman run -d --name validator \
-p 26657:26657 -p 1317:1317 -p 5001:5001 -p 9090:9090 \
-v ./tests/assets/validator-root:/root/.zenith \
-e WORKER_MNEMONIC="horn edge entire mask sock belt modify fever fit people throw cargo tone accident bike below goat sudden cup sure opinion room flavor style" \
-e RISC0_DEV_MODE=1 \
-e RISC0_INFO=1 \
-e RUST_LOG=info \
-e RUST_BACKTRACE=1 \
-e LOG_LEVEL=info \
quay.io/zenith/node:latest start --log_level info

What's happening: This starts a Zenith validator node with:

  • Port 26657: Tendermint RPC (for blockchain operations)
  • Port 1317: REST API (for queries)
  • Port 5001: IPFS API (for data storage)
  • Port 9090: gRPC API (for advanced queries)
  • Volume mount: Persists validator state and keys
  • Environment variables: Configure RISC0 development mode and logging

Wait for the node to start and create the first block:

# Wait for the node to be ready
sleep 10
# Check if the first block has been created
curl -s http://localhost:26657/block?height=1 | jq '.result.block.header.height'

Copy validator keys to local machine (needed for transactions):

podman cp validator:/root/.zenith/keyring-test ~/.zenith/

3.2 Verify IPFS is Running

Test IPFS connectivity:

echo "test" | ipfs add -q --api http://localhost:5001
# Should return: QmTest...

3.3 Register a Worker Node

Workers are the compute nodes that execute VB transactions:

zenithd tx workers create-worker worker-1 "First worker" \
--from alice \
--yes \
--fees 500stake \
--keyring-backend test \
--chain-id zenith-1 \
--node http://localhost:26657

What's happening: This registers a worker node on the Zenith network. Workers announce their capability to execute VB operations and generate zero-knowledge proofs.

Verify the transaction was successful:

# Get the transaction hash from the output above and check it
zenithd query tx <TX_HASH> --node http://localhost:26657

Phase 4: Virtual Blockchain Deployment

Now we'll deploy our Substrate VB to the network:

4.1 Compute VB Image ID

VB_IMAGE="target/riscv-guest/substrate-vb/method/riscv32im-risc0-zkvm-elf/release/method.bin"
IMAGE_ID=$(zenithd compute-image-id -e $VB_IMAGE)
echo "VB Image ID: $IMAGE_ID"

What's happening: This computes a deterministic identifier for our VB image that workers will use to verify they're executing the correct program.

4.2 Upload Artifacts to IPFS

Upload the VB program and genesis state:

# Upload VB image (the compiled RISC-V program)
IMAGE_CID=$(ipfs add -q --api http://localhost:5001 $VB_IMAGE)
echo "VB Image CID: $IMAGE_CID"
# Upload genesis state
GENESIS_CID=$(ipfs add -q --api http://localhost:5001 $TEMP_DIR/genesis.bin)
echo "Genesis CID: $GENESIS_CID"

What's happening: IPFS provides content-addressed storage. The CIDs (Content Identifiers) uniquely identify our data and ensure integrity - if the data changes, the CID changes.

4.3 Create VB On-Chain

Register the VB with the Zenith network:

VB_ID="substrate-vb-1"
zenithd tx vbs create-vb \
$VB_ID $IMAGE_CID $IMAGE_ID $GENESIS_CID $GENESIS_STATE_ROOT \
--from alice \
--yes \
--fees 500stake \
--keyring-backend test \
--chain-id zenith-1 \
--node http://localhost:26657

What's happening: This creates a Virtual Blockchain entry on the Zenith network with references to:

  • VB ID: Human-readable identifier
  • Image CID: Where to find the VB program on IPFS
  • Image ID: Deterministic program identifier
  • Genesis CID: Where to find the initial state on IPFS
  • Genesis State Root: The merkle root of the initial state

4.4 Verify Worker Synchronization

Check that workers have fetched and initialized the VB:

# Look for log messages indicating the worker has initialized the VB
podman logs validator 2>&1 | grep "Updated merkle tree"

What's happening: Workers automatically detect new VB deployments, download the artifacts from IPFS, and initialize their local merkle trees with the genesis state.

Phase 5: Execute VB Transactions

Now let's build the first block of our VB:

5.1 Upload Transaction Input

# Upload the transaction data we generated earlier
INPUT_CID=$(ipfs add -q --api http://localhost:5001 $TEMP_DIR/input.bin)
echo "Input CID: $INPUT_CID"

What's happening: This uploads our sample transactions to IPFS so workers can fetch them for execution.

5.2 Create VB Call

Submit a request to execute transactions on our VB:

zenithd tx vbs create-call $VB_ID $INPUT_CID \
--from alice \
--yes \
--fees 500stake \
--keyring-backend test \
--chain-id zenith-1 \
--node http://localhost:26657

What's happening: This creates a "call" - a request for workers to execute the transactions in input.bin on VB substrate-vb-1.

5.3 Monitor Execution

Watch the logs to see the execution process:

# Worker picks up the task
podman logs validator 2>&1 | grep "Task ACK sent successfully"
# Worker completes execution and submits proof
podman logs validator 2>&1 | grep "Task FINISH sent successfully"
# Zenith verifies proof and updates VB state
podman logs validator 2>&1 | grep "VB updated"

What's happening:

  1. ACK: Worker acknowledges the task and begins execution
  2. Execution: Worker downloads input from IPFS, executes transactions in RISC0 zkVM, generates proof
  3. FINISH: Worker uploads results to IPFS and submits proof to Zenith
  4. Verification: Zenith verifies the zero-knowledge proof and updates the VB state root

5.4 Verify Results

Query the VB state to see the updated state root:

# Using CLI
zenithd query vbs show-vb $VB_ID --node http://localhost:26657
# Using REST API (alternative method)
curl -s http://localhost:1317/zenith/vbs/v1/vb/$VB_ID | jq .

What's happening: The VB state root should now reflect the new state after processing the transactions. This proves that the block was successfully built and verified. You can use either the CLI or REST API to query the VB state.