Migrate commonware-avs-node into this repo
#15
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Local Integration Test | |
| on: | |
| pull_request: | |
| branches: | |
| - main | |
| - dev | |
| - staging | |
| jobs: | |
| local-integration-test: | |
| name: Local Integration Test (No Docker Build) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Cache cargo builds | |
| uses: Swatinem/rust-cache@v2 | |
| - name: Create .env file from example | |
| run: | | |
| cp example.env .env | |
| # Set to LOCAL mode | |
| sed -i 's|^ENVIRONMENT=.*|ENVIRONMENT=LOCAL|' .env | |
| # Comment out TESTNET RPC URLs | |
| sed -i 's|^HTTP_RPC=https://|# HTTP_RPC=https://|' .env | |
| sed -i 's|^WS_RPC=wss://|# WS_RPC=wss://|' .env | |
| sed -i 's|^RPC_URL=https://|# RPC_URL=https://|' .env | |
| # Uncomment local RPC URLs | |
| sed -i '/^# HTTP_RPC=http:\/\/localhost:8545/s/^# //' .env | |
| sed -i '/^# WS_RPC=ws:\/\/localhost:8545/s/^# //' .env | |
| sed -i '/^# RPC_URL=http:\/\/ethereum:8545/s/^# //' .env | |
| # Set FORK_URL for local forking from repository secret or default to Holesky | |
| if [ -n "${{ secrets.RPC_URL }}" ]; then | |
| sed -i "s|^# FORK_URL=.*|FORK_URL=${{ secrets.RPC_URL }}|" .env | |
| else | |
| sed -i 's|^# FORK_URL=.*|FORK_URL=https://ethereum-holesky.publicnode.com|' .env | |
| fi | |
| # Uncomment Holesky testnet contract addresses (needed for LOCAL mode with fork) | |
| sed -i 's/^#DELEGATION_MANAGER_ADDRESS=/DELEGATION_MANAGER_ADDRESS=/' .env | |
| sed -i 's/^#STRATEGY_MANAGER_ADDRESS=/STRATEGY_MANAGER_ADDRESS=/' .env | |
| sed -i 's/^#LST_CONTRACT_ADDRESS=/LST_CONTRACT_ADDRESS=/' .env | |
| sed -i 's/^#LST_STRATEGY_ADDRESS=/LST_STRATEGY_ADDRESS=/' .env | |
| sed -i 's/^#BLS_SIGNATURE_CHECKER_ADDRESS=/BLS_SIGNATURE_CHECKER_ADDRESS=/' .env | |
| sed -i 's/^#OPERATOR_STATE_RETRIEVER_ADDRESS=/OPERATOR_STATE_RETRIEVER_ADDRESS=/' .env | |
| sed -i 's/^#ALLOCATION_MANAGER_ADDRESS=/ALLOCATION_MANAGER_ADDRESS=/' .env | |
| # Use default Anvil private key for testing | |
| DEFAULT_ANVIL_KEY="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" | |
| sed -i "s|^PRIVATE_KEY=.*|PRIVATE_KEY=$DEFAULT_ANVIL_KEY|" .env | |
| sed -i "s|^FUNDED_KEY=.*|FUNDED_KEY=$DEFAULT_ANVIL_KEY|" .env | |
| - name: Update config for host-based router | |
| run: | | |
| # Update node addresses in config.example.json to use localhost | |
| # instead of Docker DNS names, since the router runs on the host. | |
| # EigenLayer will use this file to register operators on-chain. | |
| sed -i 's|"node-1:3001"|"localhost:4001"|g' config/config.example.json | |
| sed -i 's|"node-2:3002"|"localhost:4002"|g' config/config.example.json | |
| sed -i 's|"node-3:3003"|"localhost:4003"|g' config/config.example.json | |
| # Also create config.json for consistency | |
| cp config/config.example.json config/config.json | |
| - name: Pull required Docker images | |
| run: | | |
| docker compose pull ethereum eigenlayer signer node-1 node-2 node-3 | |
| - name: Start services with docker-compose (no router build) | |
| run: | | |
| # Start all services except router | |
| docker compose up -d ethereum eigenlayer signer node-1 node-2 node-3 | |
| - name: Wait for EigenLayer setup to complete | |
| run: | | |
| echo "Waiting for EigenLayer to complete setup..." | |
| timeout=300 | |
| elapsed=0 | |
| while [ $elapsed -lt $timeout ]; do | |
| # Check if the keys have been generated by EigenLayer | |
| if [ -f "config/.nodes/operator_keys/testacc1.private.bls.key.json" ] && | |
| [ -f "config/.nodes/operator_keys/testacc2.private.bls.key.json" ] && | |
| [ -f "config/.nodes/operator_keys/testacc3.private.bls.key.json" ]; then | |
| echo "✓ BLS keys generated by EigenLayer" | |
| # Check if AVS deployment file exists | |
| if [ -f "config/.nodes/avs_deploy.json" ]; then | |
| echo "✓ AVS deployed" | |
| echo "=== EigenLayer setup complete ===" | |
| ls -la config/.nodes/ | |
| ls -la config/.nodes/operator_keys/ | |
| break | |
| fi | |
| fi | |
| # Check if container exited with error | |
| container_id=$(docker compose ps -q eigenlayer) | |
| if [ -n "$container_id" ] && [ "$(docker inspect -f '{{.State.Status}}' $container_id 2>/dev/null)" = "exited" ]; then | |
| exit_code=$(docker inspect -f '{{.State.ExitCode}}' $container_id) | |
| if [ "$exit_code" != "0" ]; then | |
| echo "EigenLayer container exited with error code: $exit_code" | |
| docker compose logs eigenlayer --tail=50 | |
| exit 1 | |
| fi | |
| fi | |
| echo "Waiting for EigenLayer setup... ($elapsed/$timeout seconds)" | |
| sleep 10 | |
| elapsed=$((elapsed + 10)) | |
| done | |
| if [ $elapsed -ge $timeout ]; then | |
| echo "Timeout waiting for EigenLayer setup" | |
| docker compose logs eigenlayer --tail=50 | |
| exit 1 | |
| fi | |
| - name: Build and run router with cargo | |
| run: | | |
| echo "Building router with cargo..." | |
| cargo build -p commonware-avs-router --release | |
| echo "Starting router in background..." | |
| # Source environment and run router in background | |
| nohup cargo run -p commonware-avs-router --release -- --key-file config/router_orchestrator.json --port 3000 > router.log 2>&1 & | |
| echo $! > router.pid | |
| # Wait for router to start | |
| sleep 10 | |
| # Check if router is running | |
| if kill -0 $(cat router.pid) 2>/dev/null; then | |
| echo "✓ Router started successfully (PID: $(cat router.pid))" | |
| else | |
| echo "✗ Router failed to start" | |
| cat router.log | |
| exit 1 | |
| fi | |
| - name: Test counter increment functionality | |
| run: | | |
| echo "Testing counter increment..." | |
| # Wait for services to be fully ready | |
| sleep 20 | |
| # Get the counter contract address from the deployment file | |
| if [ ! -f "config/.nodes/avs_deploy.json" ]; then | |
| echo "AVS deployment file not found" | |
| exit 1 | |
| fi | |
| COUNTER_ADDRESS=$(cat config/.nodes/avs_deploy.json | jq -r '.addresses.counter' || echo "") | |
| if [ -z "$COUNTER_ADDRESS" ]; then | |
| echo "Counter contract address not found in deployment file" | |
| exit 1 | |
| fi | |
| echo "Counter contract address: $COUNTER_ADDRESS" | |
| # Read the initial counter value from the smart contract | |
| # The function signature for "number()" is 0x8381f58a | |
| INITIAL_RESPONSE=$(curl -s -X POST http://localhost:8545 \ | |
| -H "Content-Type: application/json" \ | |
| -d '{ | |
| "jsonrpc":"2.0", | |
| "method":"eth_call", | |
| "params":[{ | |
| "to":"'$COUNTER_ADDRESS'", | |
| "data":"0x8381f58a" | |
| }, "latest"], | |
| "id":1 | |
| }') | |
| INITIAL_HEX=$(echo "$INITIAL_RESPONSE" | jq -r '.result') | |
| # Handle empty or invalid response | |
| if [ -z "$INITIAL_HEX" ] || [ "$INITIAL_HEX" = "null" ] || [ "$INITIAL_HEX" = "0x" ]; then | |
| echo "Warning: Invalid initial counter response, defaulting to 0" | |
| INITIAL_COUNT=0 | |
| else | |
| INITIAL_COUNT=$(printf "%d\n" "$INITIAL_HEX" 2>/dev/null || echo "0") | |
| fi | |
| echo "Initial counter value: $INITIAL_COUNT" | |
| # Wait for increments (router aggregates periodically) | |
| echo "Waiting 150 seconds for counter increments..." | |
| sleep 150 | |
| # Read the final counter value from the smart contract | |
| FINAL_RESPONSE=$(curl -s -X POST http://localhost:8545 \ | |
| -H "Content-Type: application/json" \ | |
| -d '{ | |
| "jsonrpc":"2.0", | |
| "method":"eth_call", | |
| "params":[{ | |
| "to":"'$COUNTER_ADDRESS'", | |
| "data":"0x8381f58a" | |
| }, "latest"], | |
| "id":1 | |
| }') | |
| FINAL_HEX=$(echo "$FINAL_RESPONSE" | jq -r '.result') | |
| # Handle empty or invalid response | |
| if [ -z "$FINAL_HEX" ] || [ "$FINAL_HEX" = "null" ] || [ "$FINAL_HEX" = "0x" ]; then | |
| echo "Warning: Invalid final counter response, defaulting to 0" | |
| FINAL_COUNT=0 | |
| else | |
| FINAL_COUNT=$(printf "%d\n" "$FINAL_HEX" 2>/dev/null || echo "0") | |
| fi | |
| echo "Final counter value: $FINAL_COUNT" | |
| # Verify increment | |
| if [ "$FINAL_COUNT" -gt "$INITIAL_COUNT" ]; then | |
| echo "✓ Counter successfully incremented from $INITIAL_COUNT to $FINAL_COUNT" | |
| else | |
| echo "✗ Counter did not increment (still at $FINAL_COUNT)" | |
| echo "=== Router logs ===" | |
| cat router.log | |
| exit 1 | |
| fi | |
| - name: Collect logs on failure | |
| if: failure() | |
| run: | | |
| echo "=== Docker Compose Status ===" | |
| docker compose ps -a | |
| echo "=== Ethereum Logs ===" | |
| docker compose logs ethereum --tail 50 | |
| echo "=== EigenLayer Logs ===" | |
| docker compose logs eigenlayer --tail 100 | |
| echo "=== Router Logs ===" | |
| if [ -f router.log ]; then | |
| cat router.log | |
| else | |
| echo "Router log file not found" | |
| fi | |
| echo "=== Node-1 Logs ===" | |
| docker compose logs node-1 --tail 50 | |
| echo "=== Node-2 Logs ===" | |
| docker compose logs node-2 --tail 50 | |
| echo "=== Node-3 Logs ===" | |
| docker compose logs node-3 --tail 50 | |
| - name: Cleanup | |
| if: always() | |
| run: | | |
| # Kill router if still running | |
| if [ -f router.pid ] && kill -0 $(cat router.pid) 2>/dev/null; then | |
| kill $(cat router.pid) | |
| fi | |
| # Stop docker compose services | |
| docker compose down --volumes --remove-orphans |