Skip to content

Commit

Permalink
Anthitesis Node Builder v0.1
Browse files Browse the repository at this point in the history
  • Loading branch information
zi0Black committed Jan 30, 2025
1 parent 30012b3 commit 2fedd1b
Show file tree
Hide file tree
Showing 6 changed files with 329 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,6 @@ ecosystem/indexer-grpc/indexer-transaction-generator/*.yaml
*.dot
*.bytecode
!third_party/move/move-prover/tests/xsources/design/*.bytecode

# Anthitesis generated files
genesis_anthitesis/
23 changes: 23 additions & 0 deletions scripts/anthitesis-templates/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
FROM debian:stable

# Install common dependencies
RUN apt-get update && apt-get install -y libdw1 libssl-dev openssl && rm -rf /var/lib/apt/lists/*

# Copy both the aptos-node and aptos-faucet-service into the container
COPY ./aptos-node /usr/local/bin/aptos-node
COPY ./aptos-faucet-service /usr/local/bin/aptos-faucet-service
COPY ./genesis.blob /opt/aptos/genesis/genesis.blob
COPY ./waypoint.txt /opt/aptos/genesis/waypoint.txt

# Copy the entrypoint script
COPY ./entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh

# Set some defaults (overridden in docker-compose if needed)
ENV ROLE="validator"
ENV CONFIG_PATH="/opt/aptos/etc/validator.yaml"
ENV MINT_KEY="/opt/aptos/etc/mint.key"
ENV NODE_URL="http://127.0.0.1:8080"
ENV CHAIN_ID="TESTING"

ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
41 changes: 41 additions & 0 deletions scripts/anthitesis-templates/Dockerfile-build
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Stage 1: Build Aptos Node
FROM debian:stable

ARG APTOS_BRANCH
ARG NODE_COUNT
ARG NETWORK_IP
ARG FULLNODE_NODES
ARG ROOT_KEY
ARG CHAIN_ID
ARG GENESIS_DIR
ARG LAYOUT_FILE

# Install required tools and dependencies
RUN apt update && apt install -y \
git \
curl \
wget \
cmake \
clang \
build-essential \
libssl-dev \
lld \
openssl \
python3 \
python3-pip \
python-is-python3 \
python3-venv \
yq

# Clone Aptos repository
RUN git clone --single-branch -b $APTOS_BRANCH --depth=1 https://github.com/aptos-labs/aptos-core.git
WORKDIR /aptos-core
COPY ./scripts/anthitesis-templates/gen.sh gen.sh
RUN chmod +x gen.sh

# Build the aptos-node binary
RUN python3 -m venv .venv
RUN /bin/bash -c "source .venv/bin/activate && scripts/dev_setup.sh -b -t"

ENV PATH="/root/.cargo/bin:$PATH"
RUN ./gen.sh
19 changes: 19 additions & 0 deletions scripts/anthitesis-templates/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env bash
set -e

echo "Container starting with ROLE=${ROLE}"

if [ "$ROLE" = "faucet" ]; then
echo "Starting aptos-faucet-service with key: ${MINT_KEY}"
echo "Node URL: ${NODE_URL}, Chain ID: ${CHAIN_ID}"
exec /usr/local/bin/aptos-faucet-service run-simple \
--key "${MINT_KEY}" \
--node-url "${NODE_URL}" \
--chain-id "${CHAIN_ID}"
elif [ "$ROLE" = "validator" ]; then
echo "Starting aptos-node with config: ${CONFIG_PATH}"
exec /usr/local/bin/aptos-node -f "${CONFIG_PATH}"
else
echo "ERROR: Unknown ROLE: ${ROLE}"
exit 1
fi
120 changes: 120 additions & 0 deletions scripts/anthitesis-templates/gen.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
#!/bin/bash
# Build all binaries and framework
cargo build --release -p aptos-node
cargo run --package aptos-framework release --target mainnet
cargo build --release -p aptos-faucet-service

mkdir "$GENESIS_DIR"
cp mainnet.mrb "$GENESIS_DIR/framework.mrb"
cp target/release/aptos-node "$GENESIS_DIR/aptos-node"
cp target/release/aptos-faucet-service "$GENESIS_DIR/aptos-faucet-service"

# Generate layout file using yq
yq eval -n "
.root_key = \"0x$ROOT_KEY\" |
.users = [$(for i in $(seq 1 "$NODE_COUNT"); do echo -n "\"validator_$i\","; done | sed 's/,$//')] |
.chain_id = $CHAIN_ID |
.allow_new_validators = false |
.epoch_duration_secs = 7200 |
.is_test = false |
.min_price_per_gas_unit = 1 |
.min_stake = 100000000000000 |
.min_voting_threshold = 100000000000000 |
.max_stake = 1000000000000000 |
.recurring_lockup_duration_secs = 86400 |
.required_proposer_stake = 1000000 |
.rewards_apy_percentage = 10 |
.voting_duration_secs = 43200 |
.voting_power_increase_limit = 20
" > "$GENESIS_DIR/$LAYOUT_FILE"

# Generate keys and configs for each node
for i in $(seq 1 "$NODE_COUNT"); do
VALIDATOR_DIR="${GENESIS_DIR}/validator_$i/keys"
mkdir -p "$VALIDATOR_DIR"

# Generate validator keys
cargo run --package aptos -- genesis generate-keys --output-dir "$VALIDATOR_DIR"

# Assign IP addresses
# 10 is the offset for validator and 20 for fullnode, this is to avoid conflicts with the default docker bridge network and between the validator and fullnode
VALIDATOR_IP="$(echo "$NETWORK_IP" | awk -F '.' '{print $1"."$2"."$3"."($4+10+'"$i"')}')"
FULLNODE_IP="$(echo "$NETWORK_IP" | awk -F '.' '{print $1"."$2"."$3"."($4+20+'"$i"')}')"

VALIDATOR_HOST="$VALIDATOR_IP:6180"
FULLNODE_HOST="$FULLNODE_IP:6182"

# Check if this node requires a full-node configuration
if [[ " ${FULLNODE_NODES[@]} " =~ " $i " ]]; then
FULLNODE_ARG="--full-node-host $FULLNODE_HOST"
else
FULLNODE_ARG=""
fi

# Generate ValidatorConfiguration (join during genesis already setup the stacking ammount for the validator so we don't need to faucet them)
cargo run --package aptos -- genesis set-validator-configuration \
--owner-public-identity-file "$VALIDATOR_DIR"/public-keys.yaml \
--username "validator_$i" \
--stake-amount 100000000000000 \
--validator-host "$VALIDATOR_HOST" \
$FULLNODE_ARG \
--local-repository-dir . \
--join-during-genesis

cp -r "validator_$i"/* "$GENESIS_DIR/validator_$i/"
rm -rf "validator_$i"

# Check if this node requires a full-node configuration
if [[ " ${FULLNODE_NODES[@]} " =~ " $i " ]]; then

# Read the required values from the generated files
FULL_NODE_NETWORK_KEY=$(yq eval '.full_node_network_public_key' "$VALIDATOR_DIR/public-keys.yaml")
ACCOUNT_ADDRESS=$(yq eval '.account_address' "$VALIDATOR_DIR/validator-full-node-identity.yaml")

yq eval -n "
.base.role = \"full_node\" |
.base.data_dir = \"/opt/aptos/data\" |
.base.waypoint.from_file = \"/opt/aptos/genesis/waypoint.txt\" |
.execution.genesis_file_location = \"/opt/aptos/genesis/genesis.blob\" |
.full_node_networks[0].network_id = \"public\" |
.full_node_networks[0].discovery_method = \"onchain\" |
.full_node_networks[0].identity.type = \"from_config\" |
.full_node_networks[0].identity.key = \"$FULL_NODE_NETWORK_KEY\" |
.full_node_networks[0].identity.peer_id = \"$ACCOUNT_ADDRESS\" |
.full_node_networks[0].listen_address = \"/ip4/0.0.0.0/tcp/6182\" |
.full_node_networks[0].max_inbound_connections = 100 |
.full_node_networks[0].mutual_authentication = false |
.full_node_networks[0].seed_addrs = {} |
.storage.rocksdb_configs.enable_storage_sharding = true |
.api.enabled = true |
.api.address = \"0.0.0.0:8080\"
" > "$GENESIS_DIR/validator_$i/validator.yaml"

else
yq eval -n "
.base.role = \"validator\" |
.base.data_dir = \"/opt/aptos/data\" |
.base.waypoint.from_file = \"/opt/aptos/genesis/waypoint.txt\" |
.consensus.safety_rules.service.type = \"local\" |
.consensus.safety_rules.backend.type = \"on_disk_storage\" |
.consensus.safety_rules.backend.path = \"secure-data.json\" |
.consensus.safety_rules.backend.namespace = null |
.consensus.safety_rules.initial_safety_rules_config.from_file.waypoint.from_file = \"/opt/aptos/genesis/waypoint.txt\" |
.consensus.safety_rules.initial_safety_rules_config.from_file.identity_blob_path = \"/opt/aptos/genesis/validator-identity.yaml\" |
.execution.genesis_file_location = \"/opt/aptos/genesis/genesis.blob\" |
.storage.rocksdb_configs.enable_storage_sharding = true |
.validator_network.discovery_method = \"onchain\" |
.validator_network.mutual_authentication = true |
.validator_network.identity.type = \"from_file\" |
.validator_network.identity.path = \"/opt/aptos/genesis/validator-identity.yaml\" |
.api.enabled = true |
.api.address = \"0.0.0.0:8080\"
" > "$GENESIS_DIR/validator_$i/validator.yaml"
fi

done

cat "$GENESIS_DIR/layout.yaml"

# Generate genesis.blob and waypoint
cargo run --package aptos -- genesis generate-genesis --local-repository-dir "$GENESIS_DIR" --output-dir "$GENESIS_DIR"
123 changes: 123 additions & 0 deletions scripts/anthitesis_docker_gen.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#!/bin/bash

# Exit immediately if a command exits with a non-zero status
set -e

# Default variables
LAYOUT_FILE="layout.yaml"
GENESIS_DIR="genesis_anthitesis"
CHAIN_ID=8
ROOT_KEY=""
NETWORK_IP="127.0.0.0"
NODE_COUNT=1
FULLNODE_NODES=()
APTOS_BRANCH="main"
# Print usage
function usage() {
echo "Usage: $0 [options]"
echo "Options:"
echo " -b APTOS_BRANCH Aptos branch (default: $APTOS_BRANCH)"
echo " -f FRAMEWORK_DIR Directory for Aptos framework release (default: $FRAMEWORK_DIR)"
echo " -r ROOT_KEY Root key for genesis (default: $ROOT_KEY)"
echo " -c CHAIN_ID Chain ID (default: $CHAIN_ID)"
echo " -i NETWORK_IP Network IP address for nodes (default: $NETWORK_IP)"
echo " -n NODE_COUNT Number of nodes (default: $NODE_COUNT)"
echo " -x FULLNODE_NODES Comma-separated list of node indices to include full-node config (e.g., 1,3)"
echo " -h Show this help message"
}

# Parse command-line arguments
while getopts "b:f:r:c:i:n:x:h" opt; do
case $opt in
b) APTOS_BRANCH="$OPTARG" ;;
f) FRAMEWORK_DIR="$OPTARG" ;;
r) ROOT_KEY="$OPTARG" ;;
c) CHAIN_ID="$OPTARG" ;;
i) NETWORK_IP="$OPTARG" ;;
n) NODE_COUNT="$OPTARG" ;;
x) IFS=',' read -ra FULLNODE_NODES <<< "$OPTARG" ;;
h) usage; exit 0 ;;
*) usage; exit 1 ;;
esac
done

if ! command -v yq &> /dev/null; then
echo "yq is required to be installed. Please install yq and try again."
exit 1
fi

if [ -d "$GENESIS_DIR" ]; then
echo "Removing existing $GENESIS_DIR directory..."
rm -rf $GENESIS_DIR
echo "Cleaning up docker images and containers..."
# for type in validator_ fullnode_ build faucet; do
# docker container ls -a | grep "genesis_anthitesis-${type}" | awk '{print $1}' | xargs -r docker rm
# docker image ls | grep "genesis_anthitesis-${type}" | awk '{print $3}' | xargs -r docker rmi
# done
fi
mkdir -p $GENESIS_DIR

if [ -z "$ROOT_KEY" ]; then
echo "Generating root key..."
aptos key generate --key-type ed25519 --output-file $GENESIS_DIR/mint.key --encoding hex
ROOT_KEY=$(cat $GENESIS_DIR/mint.key.pub)
echo "Root key generated: $ROOT_KEY"
else
echo "Using provided public root key: $ROOT_KEY"
echo "Please provide the private root key in the $GENESIS_DIR/mint.key file before running docker-compose up"
fi

# Build the Aptos framework, node, genesis and all the configs and identities
echo "Building Aptos framework, node and genesis, it may take a while..."
docker build \
-t genesis_anthitesis-build \
-f scripts/anthitesis-templates/Dockerfile-build \
--build-arg APTOS_BRANCH="$APTOS_BRANCH" \
--build-arg NODE_COUNT="$NODE_COUNT" \
--build-arg NETWORK_IP="$NETWORK_IP" \
--build-arg FULLNODE_NODES="$FULLNODE_NODES" \
--build-arg ROOT_KEY="$ROOT_KEY" \
--build-arg CHAIN_ID="$CHAIN_ID" \
--build-arg GENESIS_DIR=$GENESIS_DIR \
--build-arg LAYOUT_FILE=$LAYOUT_FILE \
.
docker create --name genesis_anthitesis-builder genesis_anthitesis-build
docker cp genesis_anthitesis-builder:/aptos-core/$GENESIS_DIR/. $GENESIS_DIR/
docker rm -f genesis_anthitesis-builder
echo "Genesis blob and waypoint generated in $GENESIS_DIR."

# Entrypoint and Dockerfile copy
cp scripts/anthitesis-templates/entrypoint.sh $GENESIS_DIR/entrypoint.sh
cp scripts/anthitesis-templates/Dockerfile $GENESIS_DIR/Dockerfile

# Generate docker-compose.yaml using yq
SERVICES=""
for i in $(seq 1 "$NODE_COUNT"); do
if [ "$i" -gt 1 ]; then
SERVICES="$SERVICES |"
fi
SERVICES="$SERVICES .services.validator_$i.image = \"aptos/node:latest\" |"
SERVICES="$SERVICES .services.validator_$i.environment.ROLE = \"validator\" |"
SERVICES="$SERVICES .services.validator_$i.environment.CONFIG_PATH = \"/opt/aptos/etc/validator.yaml\" |"
SERVICES="$SERVICES .services.validator_$i.volumes = [\"./validator_$i/validator.yaml:/opt/aptos/etc/validator.yaml\", \"./validator_$i/keys/validator-identity.yaml:/opt/aptos/genesis/validator-identity.yaml\"] |"
SERVICES="$SERVICES .services.validator_$i.networks.custom_network.ipv4_address = \"$(echo "$NETWORK_IP" | awk -F '.' '{print $1"."$2"."$3"."($4+10+'"$i"')}')\" |"
SERVICES="$SERVICES .services.validator_$i.restart = \"unless-stopped\" |"
SERVICES="$SERVICES .services.validator_$i.expose = [6180, 6181, 9101, 8080]"
done

yq eval -n "
.services.faucet.image = \"aptos/node:latest\" |
.services.faucet.environment.ROLE = \"faucet\" |
.services.faucet.environment.MINT_KEY = \"/opt/aptos/etc/mint.key\" |
.services.faucet.environment.NODE_URL = \"http://valiadator_1:8080\" |
.services.faucet.environment.CHAIN_ID = \"$CHAIN_ID\" |
.services.faucet.volumes = [\"./mint.key:/opt/aptos/etc/mint.key\"] |
.services.faucet.networks.custom_network.ipv4_address = \"$(echo "$NETWORK_IP" | awk -F '.' '{print $1"."$2"."$3"."($4+30)}')\" |
.services.faucet.restart = \"unless-stopped\" |
.services.faucet.expose = [8080] |
.networks.custom_network.driver = \"bridge\" |
.networks.custom_network.ipam.config[0].subnet = \"$NETWORK_IP/24\" |
$SERVICES
" > "$GENESIS_DIR/docker-compose.yaml"

echo "Enter $GENESIS_DIR and run docker build -t aptos/node:latest . && docker compose up --force-recreate"

0 comments on commit 2fedd1b

Please sign in to comment.