diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..c8090fd --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,51 @@ +name: CI + +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] + +jobs: + shellcheck: + name: ShellCheck + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@v4 + - name: Install ShellCheck + run: sudo apt-get install -y shellcheck + - name: Lint shell scripts + run: find . -name "*.sh" -print0 | xargs -0 shellcheck --severity=warning + + validate-json: + name: Validate JSON + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@v4 + - name: Validate JSON files + run: | + find . -name "*.json" -print0 | while IFS= read -r -d '' f; do + echo "Validating $f" + python3 -m json.tool "$f" > /dev/null + done + + rpc-tests: + name: RPC Tests (pytest) + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + - name: Install dependencies + run: pip install -r rpc/requirements.txt + - name: Run RPC tests + run: pytest rpc/rpc_test.py -v + timeout-minutes: 5 diff --git a/.gitignore b/.gitignore index 0bbd89e..062f6ad 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ .subnet-cli.pk +__pycache__/ +*.pyc diff --git a/api/env.sh b/api/env.sh index bbb7296..66de73f 100644 --- a/api/env.sh +++ b/api/env.sh @@ -1 +1,3 @@ +#!/bin/bash +# shellcheck disable=SC2034 # URL is used by sourcing scripts URL="127.0.0.1:9650" diff --git a/chains/backup-validator.sh b/chains/backup-validator.sh index a690c58..a4f5981 100755 --- a/chains/backup-validator.sh +++ b/chains/backup-validator.sh @@ -5,7 +5,7 @@ TARGET_DIR=".avalanchego/staking/" BACKUP_FILE_NAME="$(hostname).tar.gz" -cd ~ -tar czvf ${BACKUP_FILE_NAME} ${TARGET_DIR} +cd ~ || exit +tar czvf "${BACKUP_FILE_NAME}" "${TARGET_DIR}" echo "Backup ${TARGET_DIR} to ${PWD}/${BACKUP_FILE_NAME}" diff --git a/chains/install-subnet-cli.sh b/chains/install-subnet-cli.sh index 9b01c83..b37a256 100755 --- a/chains/install-subnet-cli.sh +++ b/chains/install-subnet-cli.sh @@ -1,3 +1,4 @@ +#!/bin/bash VERSION=0.0.2 # Populate latest here GOARCH=$(go env GOARCH) diff --git a/chains/update-validator-mainnet.sh b/chains/update-validator-mainnet.sh index f740697..d7ee682 100755 --- a/chains/update-validator-mainnet.sh +++ b/chains/update-validator-mainnet.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -euo pipefail AVALANCHEGO_PREVIOUS_VERSION="1.10.7" AVALANCHEGO_VERSION="1.10.11" @@ -10,22 +11,28 @@ SUBNET_ID="2gHgAgyDHQv7jzFg6MxU2yyKq5NZBpwFLFeP8xX2E3gyK1SzSQ" download_avalanchego() { echo "Step: download_avalanchego" - wget https://github.com/ava-labs/avalanchego/releases/download/v${AVALANCHEGO_VERSION}/avalanchego-linux-amd64-v${AVALANCHEGO_VERSION}.tar.gz - tar xzf avalanchego-linux-amd64-v${AVALANCHEGO_VERSION}.tar.gz - cp avalanchego-v${AVALANCHEGO_PREVIOUS_VERSION}/run.sh avalanchego-v${AVALANCHEGO_VERSION}/ + local archive="avalanchego-linux-amd64-v${AVALANCHEGO_VERSION}.tar.gz" + wget "https://github.com/ava-labs/avalanchego/releases/download/v${AVALANCHEGO_VERSION}/${archive}" + wget "https://github.com/ava-labs/avalanchego/releases/download/v${AVALANCHEGO_VERSION}/${archive}.sha256" + sha256sum --check "${archive}.sha256" + tar xzf "${archive}" + cp "avalanchego-v${AVALANCHEGO_PREVIOUS_VERSION}/run.sh" "avalanchego-v${AVALANCHEGO_VERSION}/" } -download_sunbet_evm() { - echo "Step: download_sunbet_evm" - mkdir subnet-evm-${SUBNET_EVM_VERSION} - wget https://github.com/ava-labs/subnet-evm/releases/download/v${SUBNET_EVM_VERSION}/subnet-evm_${SUBNET_EVM_VERSION}_linux_amd64.tar.gz - tar xzf subnet-evm_${SUBNET_EVM_VERSION}_linux_amd64.tar.gz -C subnet-evm-${SUBNET_EVM_VERSION} +download_subnet_evm() { + echo "Step: download_subnet_evm" + local archive="subnet-evm_${SUBNET_EVM_VERSION}_linux_amd64.tar.gz" + mkdir "subnet-evm-${SUBNET_EVM_VERSION}" + wget "https://github.com/ava-labs/subnet-evm/releases/download/v${SUBNET_EVM_VERSION}/${archive}" + wget "https://github.com/ava-labs/subnet-evm/releases/download/v${SUBNET_EVM_VERSION}/${archive}.sha256" + sha256sum --check "${archive}.sha256" + tar xzf "${archive}" -C "subnet-evm-${SUBNET_EVM_VERSION}" } update_subnet_evm() { echo "Step: update_subnet_evm" - cp subnet-evm-${SUBNET_EVM_VERSION}/subnet-evm ~/.avalanchego/plugins/${VM_ID} - sha256sum subnet-evm-${SUBNET_EVM_VERSION}/subnet-evm ~/.avalanchego/plugins/${VM_ID} + cp "subnet-evm-${SUBNET_EVM_VERSION}/subnet-evm" ~/.avalanchego/plugins/"${VM_ID}" + sha256sum "subnet-evm-${SUBNET_EVM_VERSION}/subnet-evm" ~/.avalanchego/plugins/${VM_ID} } show_validator_files() { @@ -58,7 +65,7 @@ show_next_action_reminder() { main() { show_configs download_avalanchego - download_sunbet_evm + download_subnet_evm update_subnet_evm show_validator_files show_next_action_reminder diff --git a/chains/update-validator-testnet.sh b/chains/update-validator-testnet.sh index 6f2c22d..c2b3cac 100755 --- a/chains/update-validator-testnet.sh +++ b/chains/update-validator-testnet.sh @@ -1,4 +1,5 @@ #!/bin/bash +set -euo pipefail AVALANCHEGO_PREVIOUS_VERSION="1.10.7" AVALANCHEGO_VERSION="1.10.11" @@ -10,22 +11,28 @@ SUBNET_ID="81vK49Udih5qmEzU7opx3Zg9AnB33F2oqUTQKuaoWgCvFUWQe" download_avalanchego() { echo "Step: download_avalanchego" - wget https://github.com/ava-labs/avalanchego/releases/download/v${AVALANCHEGO_VERSION}/avalanchego-linux-amd64-v${AVALANCHEGO_VERSION}.tar.gz - tar xzf avalanchego-linux-amd64-v${AVALANCHEGO_VERSION}.tar.gz - cp avalanchego-v${AVALANCHEGO_PREVIOUS_VERSION}/run.sh avalanchego-v${AVALANCHEGO_VERSION}/ + local archive="avalanchego-linux-amd64-v${AVALANCHEGO_VERSION}.tar.gz" + wget "https://github.com/ava-labs/avalanchego/releases/download/v${AVALANCHEGO_VERSION}/${archive}" + wget "https://github.com/ava-labs/avalanchego/releases/download/v${AVALANCHEGO_VERSION}/${archive}.sha256" + sha256sum --check "${archive}.sha256" + tar xzf "${archive}" + cp "avalanchego-v${AVALANCHEGO_PREVIOUS_VERSION}/run.sh" "avalanchego-v${AVALANCHEGO_VERSION}/" } -download_sunbet_evm() { - echo "Step: download_sunbet_evm" - mkdir subnet-evm-${SUBNET_EVM_VERSION} - wget https://github.com/ava-labs/subnet-evm/releases/download/v${SUBNET_EVM_VERSION}/subnet-evm_${SUBNET_EVM_VERSION}_linux_amd64.tar.gz - tar xzf subnet-evm_${SUBNET_EVM_VERSION}_linux_amd64.tar.gz -C subnet-evm-${SUBNET_EVM_VERSION} +download_subnet_evm() { + echo "Step: download_subnet_evm" + local archive="subnet-evm_${SUBNET_EVM_VERSION}_linux_amd64.tar.gz" + mkdir "subnet-evm-${SUBNET_EVM_VERSION}" + wget "https://github.com/ava-labs/subnet-evm/releases/download/v${SUBNET_EVM_VERSION}/${archive}" + wget "https://github.com/ava-labs/subnet-evm/releases/download/v${SUBNET_EVM_VERSION}/${archive}.sha256" + sha256sum --check "${archive}.sha256" + tar xzf "${archive}" -C "subnet-evm-${SUBNET_EVM_VERSION}" } update_subnet_evm() { echo "Step: update_subnet_evm" - cp subnet-evm-${SUBNET_EVM_VERSION}/subnet-evm ~/.avalanchego/plugins/${VM_ID} - sha256sum subnet-evm-${SUBNET_EVM_VERSION}/subnet-evm ~/.avalanchego/plugins/${VM_ID} + cp "subnet-evm-${SUBNET_EVM_VERSION}/subnet-evm" ~/.avalanchego/plugins/"${VM_ID}" + sha256sum "subnet-evm-${SUBNET_EVM_VERSION}/subnet-evm" ~/.avalanchego/plugins/${VM_ID} } show_validator_files() { @@ -58,7 +65,7 @@ show_next_action_reminder() { main() { show_configs download_avalanchego - download_sunbet_evm + download_subnet_evm update_subnet_evm show_validator_files show_next_action_reminder diff --git a/rpc/requirements.txt b/rpc/requirements.txt new file mode 100644 index 0000000..039806f --- /dev/null +++ b/rpc/requirements.txt @@ -0,0 +1,3 @@ +pytest>=7.0.0 +requests>=2.28.0 +websockets>=11.0.0 diff --git a/rpc/rpc_test.py b/rpc/rpc_test.py index c3561e8..48a745e 100644 --- a/rpc/rpc_test.py +++ b/rpc/rpc_test.py @@ -1,7 +1,7 @@ #!/usr/bin/python3 +import pytest import requests -import json MAINNET_RPC_URL = "https://mainnetrpc.num.network" @@ -10,55 +10,58 @@ TESTNET_CHAIN_ID = 10508 -def test_connectivity(rpc_url): - try: - response = requests.get(rpc_url) - if response.status_code == 200: - print("Node is reachable, HTTP Status Code: 200") - else: - print(f"Node is not reachable, HTTP Status Code: {response.status_code}") - except Exception as e: - print(f"Connection failed, Error: {str(e)}") - - -def test_functionality(rpc_url): - payload = { - "jsonrpc": "2.0", - "method": "web3_clientVersion", - "params": [], - "id": 1 - } - - try: - response = requests.post(rpc_url, json=payload) - response_data = response.json() - print("RPC request successful, Response Data:", response_data) - except Exception as e: - print(f"RPC request failed, Error: {str(e)}") - - -def test_chain_id(rpc_url, chain_id): - payload = { - "jsonrpc": "2.0", - "method": "eth_chainId", - "id": chain_id, - } - - try: - response = requests.post(rpc_url, json=payload) - response_data = response.json() - print("RPC request successful, Response Data:", response_data) - except Exception as e: - print(f"RPC request failed, Error: {str(e)}") - - -if __name__ == '__main__': - print("Testing Mainnet RPC") - test_connectivity(MAINNET_RPC_URL) - test_functionality(MAINNET_RPC_URL) - test_chain_id(MAINNET_RPC_URL, MAINNET_CHAIN_ID) - - print("Testing Testnet RPC") - test_connectivity(TESTNET_RPC_URL) - test_functionality(TESTNET_RPC_URL) - test_chain_id(TESTNET_RPC_URL, TESTNET_CHAIN_ID) +def _rpc_post(rpc_url, payload): + response = requests.post(rpc_url, json=payload, timeout=10) + response.raise_for_status() + return response.json() + + +# --- Mainnet tests --- + +def test_mainnet_connectivity(): + response = requests.get(MAINNET_RPC_URL, timeout=10) + assert response.status_code == 200, ( + f"Mainnet node not reachable, HTTP status: {response.status_code}" + ) + + +def test_mainnet_client_version(): + payload = {"jsonrpc": "2.0", "method": "web3_clientVersion", "params": [], "id": 1} + data = _rpc_post(MAINNET_RPC_URL, payload) + assert "result" in data, f"Missing 'result' in response: {data}" + assert data["result"], "web3_clientVersion returned empty result" + + +def test_mainnet_chain_id(): + payload = {"jsonrpc": "2.0", "method": "eth_chainId", "params": [], "id": 1} + data = _rpc_post(MAINNET_RPC_URL, payload) + assert "result" in data, f"Missing 'result' in response: {data}" + assert int(data["result"], 16) == MAINNET_CHAIN_ID, ( + f"Expected chain ID {MAINNET_CHAIN_ID}, got {int(data['result'], 16)}" + ) + + +# --- Testnet tests --- + +def test_testnet_connectivity(): + response = requests.get(TESTNET_RPC_URL, timeout=10) + assert response.status_code == 200, ( + f"Testnet node not reachable, HTTP status: {response.status_code}" + ) + + +def test_testnet_client_version(): + payload = {"jsonrpc": "2.0", "method": "web3_clientVersion", "params": [], "id": 1} + data = _rpc_post(TESTNET_RPC_URL, payload) + assert "result" in data, f"Missing 'result' in response: {data}" + assert data["result"], "web3_clientVersion returned empty result" + + +def test_testnet_chain_id(): + payload = {"jsonrpc": "2.0", "method": "eth_chainId", "params": [], "id": 1} + data = _rpc_post(TESTNET_RPC_URL, payload) + assert "result" in data, f"Missing 'result' in response: {data}" + assert int(data["result"], 16) == TESTNET_CHAIN_ID, ( + f"Expected chain ID {TESTNET_CHAIN_ID}, got {int(data['result'], 16)}" + ) + diff --git a/subnet-cli/install-subnet-cli.sh b/subnet-cli/install-subnet-cli.sh index 9b01c83..b37a256 100755 --- a/subnet-cli/install-subnet-cli.sh +++ b/subnet-cli/install-subnet-cli.sh @@ -1,3 +1,4 @@ +#!/bin/bash VERSION=0.0.2 # Populate latest here GOARCH=$(go env GOARCH)