From e79f5370d04957d4d0b37c401809d548fd3cdb9c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Feb 2026 12:17:47 +0000 Subject: [PATCH 1/3] Initial plan From e32c01cc00b8e314abd09025c88163575fb7a30a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Feb 2026 12:23:51 +0000 Subject: [PATCH 2/3] Add CI/CD pipeline, fix error handling in validator scripts, rewrite RPC tests with pytest Co-authored-by: numbers-official <181934381+numbers-official@users.noreply.github.com> --- .github/workflows/ci.yml | 51 +++++++++++ .gitignore | 2 + api/env.sh | 2 + chains/backup-validator.sh | 4 +- chains/install-subnet-cli.sh | 1 + chains/update-validator-mainnet.sh | 29 +++--- chains/update-validator-testnet.sh | 29 +++--- rpc/__pycache__/rpc_test.cpython-312.pyc | Bin 0 -> 2863 bytes rpc/requirements.txt | 3 + rpc/rpc_test.py | 109 ++++++++++++----------- subnet-cli/install-subnet-cli.sh | 1 + 11 files changed, 154 insertions(+), 77 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 rpc/__pycache__/rpc_test.cpython-312.pyc create mode 100644 rpc/requirements.txt 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/__pycache__/rpc_test.cpython-312.pyc b/rpc/__pycache__/rpc_test.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e9144769379dd88be317ca5fdbab8387203c2116 GIT binary patch literal 2863 zcmds2OH3O_7@oD)Yr}#~fxLLG`hd74CIUht8bD2;H8i*g2$6hQIi3ZU*lTxoOlo8* ztLmY&hvZNxk&>!WPeJJ+hpM8t9+7%k2PCX5J@v#5;Txy^v-TPsP?JLsQAe6@=kd=! z|9<~t{-~|3M$poK{iOcrLFjKbv74hbD1>0}0AYl=7z!7^T$sa-umd~8PRxgS>Fg~KmZ;f%E8BKi$|qOYZ;W`F!!_L89M=O z#gTC=G7T@aXQerZVBh`HCQR-9Y2oh`BI$>A)ou(V8kW(aPx|5M~ED^!%sGdw{x}q~nB}qE)flN1( zW5fiKX|qaZGp3k$L8cbE-UX0FJH9jbR#sOY-g zy}{MN?}iP&sQ~gQ%D`?s3@kdcFnAT6lu297BDF;L3d*2G!QMIiV&|RnU}NJr!u-h+ zkI)Yeo7%6qH1fMbgk7=55vFf<17O)dQ4AkROJD_nN<@i77b3S~N|zLznVFP|-MihHbGuC$>N7eIa)2A%{!ISNA>-ZubRq zeLYxMbvNv~kJ7y62^~0l0vFBtyUOieIT}+Hjm|1WR}guE_6z&2Hq(~@_@8_{Ru(=eXsv1;1JMpduAp_;H5%L}gR5;u; zi=hj!VAk2O@T!b06K7}a6*zvCv2~FF3j%*FycB6BXuZW&T_9GRSod9Ka#q1$kE&I) zt+XdBoQTCf)45huX1&Y@o~!W$PQ{cV@*!-o_emcI*mH6mR}c{A{S*221k})*_2nDR l8mKN`-(wH=1t-_MU&nL8zL)1d=Jve;3{RKm1va+~{|5(XYcv1= literal 0 HcmV?d00001 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) From 124c9e12d56d731c8835ab98ce984e2cd1de07ba Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Feb 2026 12:24:02 +0000 Subject: [PATCH 3/3] Remove pycache from git tracking Co-authored-by: numbers-official <181934381+numbers-official@users.noreply.github.com> --- rpc/__pycache__/rpc_test.cpython-312.pyc | Bin 2863 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 rpc/__pycache__/rpc_test.cpython-312.pyc diff --git a/rpc/__pycache__/rpc_test.cpython-312.pyc b/rpc/__pycache__/rpc_test.cpython-312.pyc deleted file mode 100644 index e9144769379dd88be317ca5fdbab8387203c2116..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2863 zcmds2OH3O_7@oD)Yr}#~fxLLG`hd74CIUht8bD2;H8i*g2$6hQIi3ZU*lTxoOlo8* ztLmY&hvZNxk&>!WPeJJ+hpM8t9+7%k2PCX5J@v#5;Txy^v-TPsP?JLsQAe6@=kd=! z|9<~t{-~|3M$poK{iOcrLFjKbv74hbD1>0}0AYl=7z!7^T$sa-umd~8PRxgS>Fg~KmZ;f%E8BKi$|qOYZ;W`F!!_L89M=O z#gTC=G7T@aXQerZVBh`HCQR-9Y2oh`BI$>A)ou(V8kW(aPx|5M~ED^!%sGdw{x}q~nB}qE)flN1( zW5fiKX|qaZGp3k$L8cbE-UX0FJH9jbR#sOY-g zy}{MN?}iP&sQ~gQ%D`?s3@kdcFnAT6lu297BDF;L3d*2G!QMIiV&|RnU}NJr!u-h+ zkI)Yeo7%6qH1fMbgk7=55vFf<17O)dQ4AkROJD_nN<@i77b3S~N|zLznVFP|-MihHbGuC$>N7eIa)2A%{!ISNA>-ZubRq zeLYxMbvNv~kJ7y62^~0l0vFBtyUOieIT}+Hjm|1WR}guE_6z&2Hq(~@_@8_{Ru(=eXsv1;1JMpduAp_;H5%L}gR5;u; zi=hj!VAk2O@T!b06K7}a6*zvCv2~FF3j%*FycB6BXuZW&T_9GRSod9Ka#q1$kE&I) zt+XdBoQTCf)45huX1&Y@o~!W$PQ{cV@*!-o_emcI*mH6mR}c{A{S*221k})*_2nDR l8mKN`-(wH=1t-_MU&nL8zL)1d=Jve;3{RKm1va+~{|5(XYcv1=