diff --git a/.vitepress/config.ts b/.vitepress/config.ts index 4beadff90c1..572f706cdd0 100644 --- a/.vitepress/config.ts +++ b/.vitepress/config.ts @@ -388,6 +388,10 @@ function sidebarHome() { text: "Install celestia-app", link: "/how-to-guides/celestia-app", }, + { + text: "Set up a local testnet", + link: "/how-to-guides/local-testnet", + }, { text: "Docker images", link: "/how-to-guides/docker-images" }, ], }, diff --git a/how-to-guides/local-testnet.md b/how-to-guides/local-testnet.md new file mode 100644 index 00000000000..91ca7f2ab0a --- /dev/null +++ b/how-to-guides/local-testnet.md @@ -0,0 +1,156 @@ +# Setting up a Celestia local testnet + +This guide walks through setting up a local Celestia testnet with a validator node, bridge node, and light node. + +## Prerequisites + +- celestia-app installed (based on [compatible versions](./participate.md)) +- celestia node binary installed +- jq installed +- nc (netcat) installed + +## Bash script + +This method will start up the testnet with a bash script. It still assumes you have the prerequisites installed. + +```bash +bash -c "$(curl -sL https://docs.celestia.org/start-local.sh)" +``` + +To interact with the nodes, you can use the `celestia` and `celestia-appd` CLIs. Use the [funding and testing](#funding-and-testing) section below as a guide. + +## Manual setup + +### Starting the validator node + +First, navigate to the celestia-app scripts directory and run the single node script: + +```bash +cd celestia-app/scripts +bash single-node.sh +``` + +### Setting up the bridge node + +Once your validator node is running, get the genesis block hash: + +```bash +curl -X GET "localhost:26657/block?height=1" | jq -r '.result .block_id.hash' +``` + +This will return a hash like: +``` +1D53B32ACB02563E425BA1F8B5178B06A748E0F7B9748A8B1D07C34B454AF595 +``` + +Set this as an environment variable: + +```bash +export CELESTIA_CUSTOM=test:1D53B32ACB02563E425BA1F8B5178B06A748E0F7B9748A8B1D07C34B454AF595 +``` + +Initialize the bridge node: + +```bash +celestia bridge init \ + --node.store $HOME/.celestia-custom-bridge \ + --core.ip localhost \ + --core.grpc.port 9090 \ + --core.rpc.port 26657 \ + --p2p.network test +``` + +Start the bridge node: + +```bash +celestia bridge start \ + --node.store $HOME/.celestia-custom-bridge \ + --core.ip localhost \ + --core.grpc.port 9090 \ + --core.rpc.port 26657 \ + --p2p.network test +``` + +### Setting up the light node + +In a new terminal, set the same environment variable: + +```bash +export CELESTIA_CUSTOM=test:1D53B32ACB02563E425BA1F8B5178B06A748E0F7B9748A8B1D07C34B454AF595 +``` + +Initialize the light node: + +```bash +celestia light init \ + --p2p.network test \ + --core.ip localhost:26657 \ + --node.store $HOME/.celestia-custom-light/ +``` + +Get the bridge node's peer info: + +```bash +celestia p2p info --node.store $HOME/.celestia-custom-bridge +``` + +From the output, locate the relevant IP4 address and peer ID. The output will look similar to this example output: + +```json +{ + "result": { + "id": "12D3KooWAVsZ36CdczaEXNNnDpsPcVyAnbeBe5EPG7AjttRGfux7", + "peer_addr": [ + "/ip4/10.0.0.125/tcp/2121", + "/ip4/10.0.0.125/udp/2121/webrtc-direct/certhash/uEiBXDYwH1McBsLM2aEc3SdvAuhq2ZQ0RUgbjgTvXMQm7LA", + "/ip4/192.0.2.0/tcp/16279", + "/ip4/192.0.2.0/udp/16279/webrtc-direct/certhash/uEiBXDYwH1McBsLM2aEc3SdvAuhq2ZQ0RUgbjgTvXMQm7LA", + "/ip4/127.0.0.1/udp/2121/webrtc-direct/certhash/uEiBXDYwH1McBsLM2aEc3SdvAuhq2ZQ0RUgbjgTvXMQm7LA", + "/ip6/::1/tcp/2121", + "/ip6/::1/udp/2121/webrtc-direct/certhash/uEiBXDYwH1McBsLM2aEc3SdvAuhq2ZQ0RUgbjgTvXMQm7LA" + ] + } +} +``` + +Start the light node with custom RPC port and trusted peer: + +```bash +celestia light start \ + --p2p.network test \ + --core.ip localhost:26657 \ + --node.store $HOME/.celestia-custom-light/ \ + --headers.trusted-peers /ip4/10.0.0.125/udp/2121/webrtc-direct/certhash/uEiBXDYwH1McBsLM2aEc3SdvAuhq2ZQ0RUgbjgTvXMQm7LA/p2p/12D3KooWAVsZ36CdczaEXNNnDpsPcVyAnbeBe5EPG7AjttRGfux7 \ + --rpc.port 42069 +``` + +### Funding and testing + +Get the bridge node's account address: + +```bash +celestia state account-address --node.store $HOME/.celestia-custom-bridge +``` + +Get the light node's account address: + +```bash +celestia state account-address --node.store $HOME/.celestia-custom-light --url http://localhost:42069 +``` + +Send funds to the light node: + +```bash +celestia-appd tx bank send validator celestia1p8yx4yveuu6ushyccepsuknaqpqn9ppq07m4n3 10000000utia \ + --chain-id test \ + --keyring-backend test \ + --fees 500utia +``` + +Test by submitting a blob from the light node: + +```bash +celestia blob submit 0x4772756763686174 '"Simplicity is the ultimate sophistication." -Leonardo da Vinci' \ + --node.store $HOME/.celestia-custom-light \ + --url http://localhost:42069 +``` diff --git a/public/start-local.sh b/public/start-local.sh new file mode 100644 index 00000000000..0e2a5364327 --- /dev/null +++ b/public/start-local.sh @@ -0,0 +1,139 @@ +#!/bin/bash + +# Warning for default .celestia-app directory deletion +echo "WARNING: This script will DELETE your existing .celestia-app directory and start a new local testnet." +echo "Make sure you have backed up any important data from .celestia-app" +read -p "Do you wish to continue? [y/n] " answer +if [[ ! "$answer" =~ ^[Yy]$ ]]; then + echo "Aborting..." + exit 1 +fi + +# Function to check if a command exists +check_command() { + if ! command -v "$1" &> /dev/null; then + echo "Error: $1 is required but not installed." + exit 1 + fi +} + +# Function to wait for a service to be ready +wait_for_service() { + local port=$1 + local service=$2 + echo "Waiting for $service to be ready..." + while ! nc -z localhost "$port"; do + sleep 1 + done + echo "$service is ready!" + sleep 5 # Give it a little extra time to stabilize +} + +# Check for required commands +check_command "celestia-appd" +check_command "celestia" +check_command "jq" +check_command "nc" + +# Clean up existing directories +rm -rf "$HOME/.celestia-custom-bridge" +rm -rf "$HOME/.celestia-custom-light" + +# Create necessary directories +mkdir -p "$HOME/.celestia-custom-bridge" +mkdir -p "$HOME/.celestia-custom-light" + +# Kill any existing celestia processes +pkill celestia-appd +pkill celestia +sleep 2 + +# Start the validator node +echo "Starting validator node..." +cd "$HOME/celestia-app/scripts" || exit 1 + +# Automatically answer "y" to the prompt +echo "y" | bash single-node.sh & +wait_for_service 26657 "validator node" + +# Get the genesis block hash +echo "Getting genesis block hash..." +BLOCK_HASH=$(curl -s "localhost:26657/block?height=1" | jq -r '.result.block_id.hash') +if [ -z "$BLOCK_HASH" ]; then + echo "Error: Could not get block hash" + exit 1 +fi + +# Export the custom network variable +export CELESTIA_CUSTOM="test:$BLOCK_HASH" +echo "Set CELESTIA_CUSTOM=$CELESTIA_CUSTOM" + +# Initialize and start bridge node +echo "Initializing bridge node..." +celestia bridge init \ + --node.store "$HOME/.celestia-custom-bridge" \ + --core.ip localhost \ + --core.grpc.port 9090 \ + --core.rpc.port 26657 \ + --p2p.network test +echo "Starting bridge node..." +celestia bridge start \ + --node.store "$HOME/.celestia-custom-bridge" \ + --core.ip localhost \ + --core.grpc.port 9090 \ + --core.rpc.port 26657 \ + --p2p.network test & +wait_for_service 2121 "bridge node" + +# Get bridge node info +echo "Getting bridge node peer info..." +BRIDGE_INFO=$(celestia p2p info --node.store "$HOME/.celestia-custom-bridge") +PEER_ID=$(echo "$BRIDGE_INFO" | jq -r '.result.id') +PEER_ADDR=$(echo "$BRIDGE_INFO" | jq -r '.result.peer_addr[] | select(contains("/ip4/10.0.0.125") and contains("/udp/") and contains("/webrtc-direct/"))' | head -n 1) +if [ -z "$PEER_ID" ] || [ -z "$PEER_ADDR" ]; then + echo "Error: Could not get peer information" + exit 1 +fi + +# Initialize and start light node +echo "Initializing light node..." +celestia light init \ + --p2p.network test \ + --core.ip localhost:26657 \ + --node.store "$HOME/.celestia-custom-light/" + +# Construct the light node start command +LIGHT_NODE_CMD="celestia light start \ + --p2p.network test \ + --core.ip localhost:26657 \ + --node.store \"$HOME/.celestia-custom-light/\" \ + --headers.trusted-peers \"$PEER_ADDR/p2p/$PEER_ID\" \ + --rpc.port 42069" + +# Log the command +echo "Starting light node with command:" +echo "$LIGHT_NODE_CMD" + +# Execute the command +eval "$LIGHT_NODE_CMD" & +wait_for_service 42069 "light node" + +# Get addresses and fund light node +echo "Getting node addresses..." +BRIDGE_ADDRESS=$(celestia state account-address --node.store "$HOME/.celestia-custom-bridge" | jq -r '.result') +LIGHT_ADDRESS=$(celestia state account-address --node.store "$HOME/.celestia-custom-light" --url http://localhost:42069 | jq -r '.result') +echo "Funding light node..." +celestia-appd tx bank send validator "$LIGHT_ADDRESS" 10000000utia \ + --chain-id test \ + --keyring-backend test \ + --fees 500utia -y +echo "Waiting for funding transaction to be included in a block..." +sleep 5 +echo "Testing blob submission..." +celestia blob submit 0x4772756763686174 '"Simplicity is the ultimate sophistication." -Leonardo da Vinci' \ + --node.store "$HOME/.celestia-custom-light" \ + --url http://localhost:42069 +echo "Setup complete!" +echo "Bridge node address: $BRIDGE_ADDRESS" +echo "Light node address: $LIGHT_ADDRESS" +echo "Custom network: $CELESTIA_CUSTOM"