Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ testnet/*/binary
contracts-tmp/
*node_modules
push-chain-interop-contracts/
node-upgrade-testing/



Expand Down
31 changes: 31 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -480,3 +480,34 @@ e2e-run-test:
cp $(E2E_DIR)/push-chain-interop-contracts/contracts/svm-gateway/target/idl/pushsolanalocker.json $(CONTRACTS_DIR)/push-chain-sdk/packages/core/src/lib/constants/abi/feeLocker.json
cp $(E2E_DIR)/.env $(CONTRACTS_DIR)/push-chain-sdk/packages/core/.env
cd $(CONTRACTS_DIR)/push-chain-sdk && npx jest core/__e2e__/pushchain.spec.ts --runInBand --detectOpenHandles

###############################################################################
### e2e-upgrade... ###
###############################################################################

CHAIN_DIR := ./node-upgrade-testing
REPO := https://github.com/push-protocol/push-chain-node.git
UTILS_DIR := ./testutils/upgrade-testing-utils
CSV := $(UTILS_DIR)/upgrade_list.csv

chain-upgrade-test:
@echo ">>> Cloning push-chain-node repo..."
rm -rf $(CHAIN_DIR)
mkdir -p $(CHAIN_DIR)
cd $(CHAIN_DIR) && git clone $(REPO)

@echo ">>> Checking out first upgrade commit from CSV..."
FIRST_COMMIT=$$(awk -F',' 'NR==2 {print $$2}' $(CSV)); \
cd $(CHAIN_DIR)/push-chain-node && git checkout $$FIRST_COMMIT

@echo ">>> Copying utility files..."
mkdir -p $(CHAIN_DIR)/push-chain-node/scripts
cp $(UTILS_DIR)/test_node_e2e_upgrade.sh $(CHAIN_DIR)/push-chain-node/scripts/test_node_e2e_upgrade.sh
cp $(UTILS_DIR)/Dockerfile.node_upgrade_test $(CHAIN_DIR)/push-chain-node/Dockerfile.node_upgrade_test
cp $(UTILS_DIR)/docker-compose.yml $(CHAIN_DIR)/push-chain-node/docker-compose.yml

@echo ">>> Launching docker container for first upgrade..."
cd $(CHAIN_DIR)/push-chain-node && docker compose up -d --build



1 change: 1 addition & 0 deletions testutils/upgrade-testing-utils/.upgrade_index
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2
32 changes: 32 additions & 0 deletions testutils/upgrade-testing-utils/Dockerfile.node_upgrade_test
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
FROM golang:1.23 AS build
WORKDIR /app

COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN make install # regular build (dynamic link)

FROM ubuntu:22.04
RUN apt-get update && apt-get install -y bash curl jq git make gcc-x86-64-linux-gnu g++ build-essential libc6-dev pkg-config g++-x86-64-linux-gnu libc6-dev-amd64-cross && rm -rf /var/lib/apt/lists/*

RUN curl -OL https://go.dev/dl/go1.23.0.linux-amd64.tar.gz
RUN tar -C /usr/local -xzf go1.23.0.linux-amd64.tar.gz

ENV PATH=$PATH:/usr/local/go/bin

# Install wasmvm v1.5.4 shared library
RUN curl -L https://github.com/CosmWasm/wasmvm/releases/download/v1.5.4/libwasmvm.aarch64.so \
-o /usr/lib/libwasmvm.aarch64.so && chmod 755 /usr/lib/libwasmvm.aarch64.so

RUN curl -L https://github.com/CosmWasm/wasmvm/releases/download/v1.5.4/libwasmvm.x86_64.so \
-o /usr/lib/libwasmvm.x86_64.so && chmod 755 /usr/lib/libwasmvm.x86_64.so

COPY --from=build /go/bin/pchaind /usr/local/bin/pchaind
COPY scripts /app/scripts
WORKDIR /app

ENV CHAIN_ID=localchain_9000-1 \
BLOCK_TIME=1000ms \
CLEAN=true

CMD ["bash", "scripts/test_node_e2e_upgrade.sh"]
21 changes: 21 additions & 0 deletions testutils/upgrade-testing-utils/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
version: '3.8'

services:
push-chain-node:
build:
context: .
dockerfile: Dockerfile.node_upgrade_test
container_name: push-chain-node
environment:
- CHAIN_ID=localchain_9000-1
- BLOCK_TIME=1000ms
- CLEAN=true
command: bash scripts/test_node_e2e_upgrade.sh
extra_hosts:
- "host.docker.internal:host-gateway"
ports:
- "26657:26657" # Tendermint RPC
- "1317:1317" # Cosmos REST API
- "8545:8545" # Cosmos REST API
volumes:
- ./scripts:/app/scripts
174 changes: 174 additions & 0 deletions testutils/upgrade-testing-utils/test_node_e2e_upgrade.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
#!/bin/bash
# Run this script to quickly install, setup, and run the current version of the network without docker.
#
# Examples:
# CHAIN_ID="localchain_9000-1" HOME_DIR="~/.pchain" BLOCK_TIME="1000ms" CLEAN=true sh scripts/test_node.sh
# CHAIN_ID="localchain_9000-2" HOME_DIR="~/.pchain" CLEAN=true RPC=36657 REST=2317 PROFF=6061 P2P=36656 GRPC=8090 GRPC_WEB=8091 ROSETTA=8081 BLOCK_TIME="500ms" sh scripts/test_node.sh
shopt -s expand_aliases
set -eu

export KEY1="acc1"
export KEY2="acc2"
export KEY3="acc3"

export CHAIN_ID=${CHAIN_ID:-"localchain_9000-1"}
export MONIKER=${MONIKER:-"localvalidator"}
export KEYALGO="eth_secp256k1"
export KEYRING=${KEYRING:-"test"}
export HOME_DIR=$(eval echo "${HOME_DIR:-"~/.pchain"}")
export BINARY=${BINARY:-pchaind}
export DENOM=${DENOM:-upc}

export CLEAN=${CLEAN:-"false"}
export RPC=${RPC:-"26657"}
export REST=${REST:-"1317"}
export PROFF=${PROFF:-"6060"}
export P2P=${P2P:-"26656"}
export GRPC=${GRPC:-"9090"}
export GRPC_WEB=${GRPC_WEB:-"9091"}
export ROSETTA=${ROSETTA:-"8080"}
export BLOCK_TIME=${BLOCK_TIME:-"1s"}

# if which binary does not exist, install it
# if [ -z `which $BINARY` ]; then
# make install

# if [ -z `which $BINARY` ]; then
# echo "Ensure $BINARY is installed and in your PATH"
# exit 1
# fi
# fi

alias BINARY="$BINARY --home=$HOME_DIR"

command -v $BINARY > /dev/null 2>&1 || { echo >&2 "$BINARY command not found. Ensure this is setup / properly installed in your GOPATH (make install)."; exit 1; }
command -v jq > /dev/null 2>&1 || { echo >&2 "jq not installed. More info: https://stedolan.github.io/jq/download/"; exit 1; }

set_config() {
$BINARY config set client chain-id $CHAIN_ID
$BINARY config set client keyring-backend $KEYRING
}
set_config


from_scratch () {
# Fresh install on current branch
# make install

# remove existing daemon files.
if [ ${#HOME_DIR} -le 2 ]; then
echo "HOME_DIR must be more than 2 characters long"
return
fi
rm -rf $HOME_DIR && echo "Removed $HOME_DIR"

# reset values if not set already after whipe
set_config

add_key() {
key=$1
mnemonic=$2
echo $mnemonic | BINARY keys add $key --keyring-backend $KEYRING --algo $KEYALGO --recover
}

# push1ss5j8c8j453uecnczt3ms23lze30kxt4pzfvh9
add_key $KEY1 "surface task term spring horse impact tortoise often session cable off catch harvest rain able jealous coral cargo portion surge spring genre mix avoid"
# push1j55s4vpvmncruakqhj2k2fywnc9mvsuhcap28q
add_key $KEY2 "season wing cost lunch leg absurd parent practice frost mistake choose leopard switch shrug wrist this pistol bright spike hurt fit meadow smart hazard"
# push1fgaewhyd9fkwtqaj9c233letwcuey6dgly9gv9
add_key $KEY3 "episode silver life middle tumble slogan genius loop divide alpha raven bridge drive calm club system school raccoon unfold marine oyster radio treat sphere"

BINARY init $MONIKER --chain-id $CHAIN_ID --default-denom $DENOM

update_test_genesis () {
cat $HOME_DIR/config/genesis.json | jq "$1" > $HOME_DIR/config/tmp_genesis.json && mv $HOME_DIR/config/tmp_genesis.json $HOME_DIR/config/genesis.json
}

# === CORE MODULES ===

# Block
update_test_genesis '.consensus_params["block"]["max_gas"]="100000000"'

# Gov
update_test_genesis `printf '.app_state["gov"]["params"]["min_deposit"]=[{"denom":"%s","amount":"1000000"}]' $DENOM`
update_test_genesis '.app_state["gov"]["params"]["voting_period"]="30s"'
update_test_genesis '.app_state["gov"]["params"]["expedited_voting_period"]="15s"'

update_test_genesis `printf '.app_state["evm"]["params"]["evm_denom"]="%s"' $DENOM`
update_test_genesis '.app_state["evm"]["params"]["active_static_precompiles"]=["0x00000000000000000000000000000000000000CB","0x00000000000000000000000000000000000000ca","0x0000000000000000000000000000000000000100","0x0000000000000000000000000000000000000400","0x0000000000000000000000000000000000000800","0x0000000000000000000000000000000000000801","0x0000000000000000000000000000000000000802","0x0000000000000000000000000000000000000803","0x0000000000000000000000000000000000000804","0x0000000000000000000000000000000000000805"]'
update_test_genesis '.app_state["erc20"]["params"]["native_precompiles"]=["0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"]' # https://eips.ethereum.org/EIPS/eip-7528
update_test_genesis `printf '.app_state["erc20"]["token_pairs"]=[{contract_owner:1,erc20_address:"0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",denom:"%s",enabled:true}]' $DENOM`
update_test_genesis '.app_state["feemarket"]["params"]["no_base_fee"]=false'
update_test_genesis '.app_state["feemarket"]["params"]["base_fee"]="1000000000.000000000000000000"'
update_test_genesis '.app_state["feemarket"]["params"]["min_gas_price"]="1000000000.000000000000000000"'

# staking
update_test_genesis `printf '.app_state["staking"]["params"]["bond_denom"]="%s"' $DENOM`
update_test_genesis '.app_state["staking"]["params"]["min_commission_rate"]="0.050000000000000000"'

# mint
update_test_genesis `printf '.app_state["mint"]["params"]["mint_denom"]="%s"' $DENOM`

# crisis
update_test_genesis `printf '.app_state["crisis"]["constant_fee"]={"denom":"%s","amount":"1000"}' $DENOM`

## abci
update_test_genesis '.consensus["params"]["abci"]["vote_extensions_enable_height"]="1"'

# === CUSTOM MODULES ===
# tokenfactory
update_test_genesis '.app_state["tokenfactory"]["params"]["denom_creation_fee"]=[]'
update_test_genesis '.app_state["tokenfactory"]["params"]["denom_creation_gas_consume"]=100000'

# Allocate genesis accounts
# Total: 10 000000000 . 000000000 000000000
BINARY genesis add-genesis-account $KEY1 5000000000000000000000000000$DENOM,100000000test --keyring-backend $KEYRING --append
BINARY genesis add-genesis-account $KEY2 3000000000000000000000000000$DENOM,90000000test --keyring-backend $KEYRING --append
BINARY genesis add-genesis-account $KEY3 2000000000000000000000000000$DENOM,90000000test --keyring-backend $KEYRING --append

# Sign genesis transaction
# 10 000 . 000000000 000000000
BINARY genesis gentx $KEY1 10000000000000000000000$DENOM --gas-prices 1000000000${DENOM} --keyring-backend $KEYRING --chain-id $CHAIN_ID

BINARY genesis collect-gentxs

BINARY genesis validate-genesis
err=$?
if [ $err -ne 0 ]; then
echo "Failed to validate genesis"
return
fi
}

# check if CLEAN is not set to false
if [ "$CLEAN" != "false" ]; then
echo "Starting from a clean state"
from_scratch
fi

echo "Starting node..."

# Opens the RPC endpoint to outside connections
sed -i -e 's/laddr = "tcp:\/\/127.0.0.1:26657"/c\laddr = "tcp:\/\/0.0.0.0:'$RPC'"/g' $HOME_DIR/config/config.toml
sed -i -e 's/cors_allowed_origins = \[\]/cors_allowed_origins = \["\*"\]/g' $HOME_DIR/config/config.toml

# REST endpoint
sed -i -e 's/address = "tcp:\/\/localhost:1317"/address = "tcp:\/\/0.0.0.0:'$REST'"/g' $HOME_DIR/config/app.toml
sed -i -e 's/enable = false/enable = true/g' $HOME_DIR/config/app.toml
sed -i -e 's/enabled-unsafe-cors = false/enabled-unsafe-cors = true/g' $HOME_DIR/config/app.toml

# peer exchange
sed -i -e 's/pprof_laddr = "localhost:6060"/pprof_laddr = "localhost:'$PROFF'"/g' $HOME_DIR/config/config.toml
sed -i -e 's/laddr = "tcp:\/\/0.0.0.0:26656"/laddr = "tcp:\/\/0.0.0.0:'$P2P'"/g' $HOME_DIR/config/config.toml

# GRPC
sed -i -e 's/address = "localhost:9090"/address = "0.0.0.0:'$GRPC'"/g' $HOME_DIR/config/app.toml
sed -i -e 's/address = "localhost:9091"/address = "0.0.0.0:'$GRPC_WEB'"/g' $HOME_DIR/config/app.toml

# Rosetta Api
sed -i -e 's/address = ":8080"/address = "0.0.0.0:'$ROSETTA'"/g' $HOME_DIR/config/app.toml

# Faster blocks
sed -i -e 's/timeout_commit = "5s"/timeout_commit = "'$BLOCK_TIME'"/g' $HOME_DIR/config/config.toml

BINARY start --pruning=nothing --minimum-gas-prices=1000000000$DENOM --rpc.laddr="tcp://0.0.0.0:$RPC" --json-rpc.api=eth,txpool,personal,net,debug,web3 --chain-id="$CHAIN_ID"
6 changes: 6 additions & 0 deletions testutils/upgrade-testing-utils/upgrade_list.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
upgrade_name,commit_hash
current,2536b88b0d836ef900802014fc3d003401387c44
uaid-refactor,47aa1d83641d960c198605648b813106f9bb7bab
uaid-refactor,47aa1d83641d960c198605648b813106f9bb7bab
uaid-refactor,47aa1d83641d960c198605648b813106f9bb7bab
uaid-refactor,47aa1d83641d960c198605648b813106f9bb7bab
105 changes: 105 additions & 0 deletions testutils/upgrade-testing-utils/upgrade_node.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#!/bin/bash
set -e

# Paths inside container
CSV_FILE="./testutils/upgrade-testing-utils/upgrade_list.csv"
STATE_FILE="./testutils/upgrade-testing-utils/.upgrade_index"

# If first run, set index = 2 (meaning line 2 = current commit done, upgrades start at line 3)
if [ ! -f "$STATE_FILE" ]; then
echo 2 > "$STATE_FILE"
fi

CURRENT_INDEX=$(cat "$STATE_FILE")
NEXT_INDEX=$((CURRENT_INDEX + 1))

# Count total upgrades (excluding header row)
TOTAL_UPGRADES=$(($(wc -l < "$CSV_FILE") - 1))

echo "TOTAL_UPGRADES=$TOTAL_UPGRADES"
echo "CURRENT_INDEX=$CURRENT_INDEX"
echo "NEXT_INDEX=$NEXT_INDEX"

# If we’ve reached past last upgrade line
if [ "$NEXT_INDEX" -gt $((TOTAL_UPGRADES + 1)) ]; then
echo "✅ All upgrades applied!"
exit 0
fi

# Get the upgrade details from CSV
NEXT_UPGRADE=$(awk -F',' -v idx=$NEXT_INDEX 'NR==idx {print $1","$2}' "$CSV_FILE")

if [ -z "$NEXT_UPGRADE" ]; then
echo "❌ No upgrade found at line $NEXT_INDEX."
exit 1
fi

UPGRADE_NAME=$(echo "$NEXT_UPGRADE" | cut -d',' -f1 | xargs)
COMMIT_HASH=$(echo "$NEXT_UPGRADE" | cut -d',' -f2 | xargs)

echo ">>> Applying upgrade (line $NEXT_INDEX): $UPGRADE_NAME ($COMMIT_HASH)"

# Clone, checkout commit, build inside container
docker exec push-chain-node bash -c "
set -e
cd /app || mkdir -p /app && cd /app
echo '>>> Removing existing repo (if any)'
rm -rf push-chain-node

echo '>>> Cloning push-chain-node repo'
git clone https://github.com/push-protocol/push-chain-node.git push-chain-node

cd push-chain-node

echo '>>> Checking out commit $COMMIT_HASH'
git checkout $COMMIT_HASH

echo '>>> Building binary'
CGO_ENABLED=0 make build

if [ ! -f build/pchaind ]; then
echo '❌ Build failed'
exit 1
fi

echo '>>> Copying binary to /usr/local/bin'
cp build/pchaind /usr/local/bin/pchaind
"

# Get current height inside container
HEIGHT=$(docker exec push-chain-node pchaind status | jq -r '.SyncInfo.latest_block_height')
TARGET_HEIGHT=$(( (HEIGHT/1000 + 1) * 1000 ))

echo ">>> Submitting governance proposal for $UPGRADE_NAME at height $TARGET_HEIGHT..."
docker exec push-chain-node pchaind tx upgrade software-upgrade "$UPGRADE_NAME" \
--upgrade-height "$TARGET_HEIGHT" \
--title "Test Upgrade $UPGRADE_NAME" \
--summary "Automated upgrade test for $UPGRADE_NAME" \
--from acc1 \
--chain-id localchain_9000-1 \
--deposit 1000000upc \
--node tcp://localhost:26657 \
--gas auto \
--gas-adjustment 1.3 \
--fees 310596000000000upc \
--yes

# Wait for halt
echo ">>> Waiting for chain halt at height $TARGET_HEIGHT..."
while true; do
CUR_HEIGHT=$(docker exec push-chain-node pchaind status | jq -r '.SyncInfo.latest_block_height')
if [ "$CUR_HEIGHT" -ge "$TARGET_HEIGHT" ]; then
echo "Chain halted at $CUR_HEIGHT."
break
fi
sleep 5
done

# Restart node with upgraded binary
echo ">>> Restarting chain with upgraded binary..."
docker exec push-chain-node pkill pchaind || true
docker exec -d push-chain-node pchaind start

# Update state → store the line number we just processed
echo "$NEXT_INDEX" > "$STATE_FILE"
echo "✅ Upgrade $UPGRADE_NAME applied successfully!"
Loading