Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

initial impl of chain-upgrade #51

Merged
merged 12 commits into from
Jan 4, 2022
Merged
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
31 changes: 31 additions & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,34 @@ zombienet-tests-integration:
retry: 2
tags:
- zombienet-polkadot-integration-test

zombienet-dummy-chain-upgrade:
stage: deploy
<<: *kubernetes-env
image: "paritypr/zombienet:${CI_COMMIT_SHORT_SHA}"
rules:
- if: $CI_PIPELINE_SOURCE == "schedule"
- if: $CI_COMMIT_REF_NAME == "master"
- if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ # PRs
- if: $CI_COMMIT_REF_NAME =~ /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1
# needs:
# - job: publish-docker-pr

variables:
GH_DIR: 'https://github.com/paritytech/zombienet/tree/feat-chain-upgrade-command/tests'

before_script:
- echo "Zombie-net Tests Config"
- echo "paritypr/zombienet:${CI_COMMIT_SHORT_SHA}"
- echo "${GH_DIR}"
- export DEBUG=zombie*
- export ZOMBIENET_INTEGRATION_TEST_IMAGE="docker.io/paritypr/synth-wave:master"
- export COL_IMAGE="docker.io/paritypr/colander:master"

script:
- /home/nonroot/zombie-net/scripts/run-test-local-env-manager.sh
--test="0003-parachains-upgrade-smoke-test.feature"
allow_failure: true
retry: 2
tags:
- zombienet-polkadot-integration-test
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"debug": "^4.3.2",
"execa": "^5.1.1",
"mocha": "^9.1.2",
"napi-maybe-compressed-blob": "0.0.2",
"tmp-promise": "^3.0.2",
"toml": "^3.0.0",
"yaml": "^2.0.0-9"
Expand Down
1 change: 1 addition & 0 deletions scripts/docker/zombienet_injected.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ WORKDIR /home/nonroot/zombie-net
COPY ./artifacts/dist ./dist
COPY static-configs ./static-configs
COPY scripts ./scripts
COPY tests ./tests
COPY artifacts/package* ./
RUN npm install --production
RUN chown -R nonroot. /home/nonroot
Expand Down
5 changes: 5 additions & 0 deletions scripts/run-test-env-manager.sh
Original file line number Diff line number Diff line change
Expand Up @@ -214,10 +214,15 @@ function run_test {
set -x
set +e
if [[ ! -z $TEST_TO_RUN ]]; then
TEST_FOUND=0
for i in $(find ${OUTPUT_DIR} -name "${TEST_TO_RUN}"| head -1); do
TEST_FOUND=1
zombie test $i
EXIT_STATUS=$?
done;
if [[ $TEST_FOUND -lt 1 ]]; then
EXIT_STATUS=1
fi;
else
for i in $(find ${OUTPUT_DIR} -name *.feature | sort); do
echo "running test: ${i}"
Expand Down
161 changes: 161 additions & 0 deletions scripts/run-test-local-env-manager.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
#!/bin/bash

# Based on https://gitlab.parity.io/parity/simnet/-/blob/master/scripts/run-test-environment-manager-v2.sh

set -eou pipefail


function usage {
cat << EOF
DEPENDENCY 1: gcloud
https://cloud.google.com/sdk/docs/install

DEPENDENCY 2: kubectl
gcloud components install kubectl


Usage: ${SCRIPT_NAME} OPTION

OPTION
-t, --test OPTIONAL Test file to run
If omitted "all" test in the tests directory will be used.
-h, --help OPTIONAL Print this help message
-o, --output-dir OPTIONAL
Path to dir where to save contens of --github-remote-dir
Defaults to ${SCRIPT_PATH}
specified, it will be ifered from there.

EXAMPLES
Run tests
${SCRIPT_NAME} -g https://github.com/paritytech/polkadot/tree/master/zombienet_tests

EOF
}

function main {
# Main entry point for the script
set_defaults_for_globals
parse_args "$@"
create_isolated_dir
copy_to_isolated
run_test
log INFO "Exit status is ${EXIT_STATUS}"
exit "${EXIT_STATUS}"
}

function create_isolated_dir {
TS=$(date +%s)
ISOLATED=${OUTPUT_DIR}/${TS}
mkdir -p ${ISOLATED}
OUTPUT_DIR="${ISOLATED}"
}

function set_defaults_for_globals {
# DEFAULT VALUES for variables used for testing different projects
SCRIPT_NAME="$0"
SCRIPT_PATH=$(dirname "$0") # relative
SCRIPT_PATH=$(cd "${SCRIPT_PATH}" && pwd) # absolutized and normalized

export GOOGLE_CREDENTIALS="/etc/zombie-net/sa-zombie.json"

cd "${SCRIPT_PATH}"

EXIT_STATUS=0
GH_REMOTE_DIR=""
TEST_TO_RUN=""


LAUNCH_ARGUMENTS=""
USE_LOCAL_TESTS=false
OUTPUT_DIR="${SCRIPT_PATH}"
}

function parse_args {
function needs_arg {
if [ -z "${OPTARG}" ]; then
log DIE "No arg for --${OPT} option"
fi
}

function check_args {
if [[ -n "${GH_REMOTE_DIR}" &&
! "${GH_REMOTE_DIR}" =~ https:\/\/github.com\/ ]] ; then
log DIE "Not a github URL"
fi
}

# shellcheck disable=SC2214
while getopts i:t:g:h:uo:-: OPT; do
# support long options: https://stackoverflow.com/a/28466267/519360
if [ "$OPT" = "-" ]; then # long option: reformulate OPT and OPTARG
OPT="${OPTARG%%=*}" # extract long option name
OPTARG="${OPTARG#$OPT}" # extract long option argument (may be empty)
OPTARG="${OPTARG#=}" # if long option argument, remove assigning `=`
fi
case "$OPT" in
t | test) needs_arg ; TEST_TO_RUN="${OPTARG}" ;;
g | github-remote-dir) needs_arg ; GH_REMOTE_DIR="${OPTARG}" ;;
h | help ) usage ; exit 0 ;;
o | output-dir) needs_arg ; OUTPUT_DIR="${OPTARG}" ;;
??* ) log DIE "Illegal option --${OPT}" ;;
? ) exit 2 ;;
esac
done
shift $((OPTIND-1)) # remove parsed options and args from $@ list
check_args
}

function copy_to_isolated {
cd "${SCRIPT_PATH}"
echo $(pwd)
echo $(ls)
echo $(ls ..)
cp -r ../tests/* "${OUTPUT_DIR}"
}
function run_test {
# RUN_IN_CONTAINER is env var that is set in the dockerfile
if [[ -v RUN_IN_CONTAINER ]]; then
gcloud auth activate-service-account --key-file "${GOOGLE_CREDENTIALS}"
gcloud container clusters get-credentials parity-zombienet --zone europe-west3-b --project parity-zombienet
fi
cd "${OUTPUT_DIR}"
set -x
set +e
if [[ ! -z $TEST_TO_RUN ]]; then
TEST_FOUND=0
for i in $(find ${OUTPUT_DIR} -name "${TEST_TO_RUN}"| head -1); do
TEST_FOUND=1
zombie test $i
EXIT_STATUS=$?
done;
if [[ $TEST_FOUND -lt 1 ]]; then
EXIT_STATUS=1
fi;
else
for i in $(find ${OUTPUT_DIR} -name *.feature | sort); do
echo "running test: ${i}"
zombie test $i
TEST_EXIT_STATUS=$?
EXIT_STATUS=$((EXIT_STATUS+TEST_EXIT_STATUS))
done;
fi

set +x
set -e
}

function log {
local lvl msg fmt
lvl=$1 msg=$2
fmt='+%Y-%m-%d %H:%M:%S'
lg_date=$(date "${fmt}")
if [[ "${lvl}" = "DIE" ]] ; then
lvl="ERROR"
echo -e "\n${lg_date} - ${lvl} - ${msg}"
exit 1
else
echo -e "\n${lg_date} - ${lvl} - ${msg}"
fi
}

main "$@"
71 changes: 71 additions & 0 deletions src/jsapi-helpers/chain-upgrade.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { ApiPromise, Keyring } from "@polkadot/api";
import { withTypeString } from "@polkadot/types";
import { cryptoWaitReady } from "@polkadot/util-crypto";
import { readFileSync, promises as fsPromises } from "fs";

import { compress, decompress } from "napi-maybe-compressed-blob";

export async function chainUpgrade(api: ApiPromise, wasmFilePath: string): Promise<void> {
// The filename of the runtime/PVF we want to upgrade to. Usually a file
// with `.compact.compressed.wasm` extension.
console.log(`upgrading chain with file: ${wasmFilePath}`);

let code = readFileSync(wasmFilePath).toString("hex");
await performChainUpgrade(api, code);
}

export async function chainDummyUpgrade(api: ApiPromise): Promise<void> {
const code: any = await api.rpc.state.getStorage(":code");
const codeHex = code.toString().slice(2)
const codeBuf = Buffer.from(hexToBytes(codeHex));
const decompressed = decompress(codeBuf);

// add dummy
// echo -n -e "\x00\x07\x05\x64\x75\x6D\x6D\x79\x0A"
const dummyBuf = [0x00, 0x07, 0x05, 0x64, 0x75, 0x6D, 0x6D, 0x79, 0x0A];
const withDummyCode = Buffer.concat([decompressed, Buffer.from(dummyBuf)]);

// compress again
const compressed = compress(withDummyCode);

// perform upgrade
await performChainUpgrade(api, compressed.toString("hex"));
}


async function performChainUpgrade(api: ApiPromise, code: string) {
await cryptoWaitReady()

const keyring = new Keyring({ type: "sr25519" });
const alice = keyring.addFromUri("//Alice");

await new Promise<void>(async (resolve, reject) => {
const unsub = await api.tx.sudo
.sudoUncheckedWeight(api.tx.system.setCodeWithoutChecks(`0x${code}`), 1)
.signAndSend(alice, (result) => {
console.log(`Current status is ${result.status}`);
if (result.status.isInBlock) {
console.log(
`Transaction included at blockHash ${result.status.asInBlock}`
);
} else if (result.status.isFinalized) {
console.log(
`Transaction finalized at blockHash ${result.status.asFinalized}`
);
unsub();
return resolve();
} else if (result.isError) {
console.log(`Transaction Error`);
unsub();
return reject();
}
});
});
}

/// Internal
function hexToBytes(hex: any) {
for (var bytes = [], c = 0; c < hex.length; c += 2)
bytes.push(parseInt(hex.substr(c, 2), 16));
return bytes;
}
15 changes: 15 additions & 0 deletions src/jsapi-helpers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { ApiPromise, WsProvider } from "@polkadot/api";
import { chainUpgrade, chainDummyUpgrade } from "./chain-upgrade";

async function connect(apiUrl: string, types: any): Promise<ApiPromise> {
const provider = new WsProvider(apiUrl)
const api = new ApiPromise({ provider, types })
await api.isReady
return api
}

export {
connect,
chainUpgrade,
chainDummyUpgrade
}
Loading