diff --git a/install.md b/install.md index e081a95..ccaf178 100644 --- a/install.md +++ b/install.md @@ -26,7 +26,7 @@ sudo apt update sudo apt install build-essential jq wget git -y wget https://dl.google.com/go/go1.18.1.linux-amd64.tar.gz -tar -xvf go1.18.1.linux-amd64.tar.gz +tar -xvf go1.19.5.linux-amd64.tar.gz sudo mv go /usr/local ``` @@ -53,10 +53,10 @@ git clone https://github.com/cosmic-horizon/QWOYN.git # Install `qwoynd` cd QWOYN -git checkout v1.0.0-beta +git checkout v1.0.0 make install -# check version (1.0.0-beta) +# check version (1.0.0) qwoynd version ``` diff --git a/scripts/completions.sh b/scripts/completions.sh new file mode 100755 index 0000000..c8c2f75 --- /dev/null +++ b/scripts/completions.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +set -e +rm -rf completions +mkdir completions + +for sh in bash zsh fish; do + go run cmd/regen/main.go completion "$sh" >"completions/regen.$sh" +done diff --git a/scripts/generate_cli_docs.go b/scripts/generate_cli_docs.go new file mode 100644 index 0000000..b183e9f --- /dev/null +++ b/scripts/generate_cli_docs.go @@ -0,0 +1,18 @@ +package main + +import ( + "log" + + "github.com/spf13/cobra/doc" + + "github.com/regen-network/regen-ledger/v5/app/client/cli" +) + +// generate documentation for all regen app commands +func main() { + rootCmd, _ := cli.NewRootCmd() + err := doc.GenMarkdownTree(rootCmd, "commands") + if err != nil { + log.Fatal(err) + } +} diff --git a/scripts/generate_feature_docs.sh b/scripts/generate_feature_docs.sh new file mode 100755 index 0000000..e68bfbb --- /dev/null +++ b/scripts/generate_feature_docs.sh @@ -0,0 +1,96 @@ +#!/usr/bin/env bash + +set -eo pipefail + +# NOTE: fork of github.com/raviqqe/gherkin2markdown with Rule support +go install github.com/ryanchristo/gherkin2markdown@latest + +echo "Generating data feature docs..." + +data_dir="docs/modules/data/features" +data_server_dir="$data_dir/server" +data_types_dir="$data_dir/types" + +mkdir -p $data_server_dir +mkdir -p $data_types_dir + +data_readme="# Features\n\n" +data_readme+="## Types\n\n" + +for file in $(find ./x/data/features -path -prune -o -name '*.feature' | sort -t '\0' -n); do + name=$(basename "${file%.*}") + data_readme+="- [$name](./types/$name.html)\n" + "$GOBIN/gherkin2markdown" "$file" > "$data_types_dir/$name.md" +done + +data_readme+="\n## Server\n\n" + +for file in $(find ./x/data/server -path -prune -o -name '*.feature' | sort -t '\0' -n); do + name=$(basename "${file%.*}") + data_readme+="- [$name](./server/$name.html)\n" + "$GOBIN/gherkin2markdown" "$file" > "$data_server_dir/$name.md" +done + +echo -e "$data_readme" > "$data_dir/README.md" + +echo "Generating ecocredit feature docs..." + +eco_dir="docs/modules/ecocredit/features" +eco_server_dir="$eco_dir/server" +eco_types_dir="$eco_dir/types" + +mkdir -p $eco_server_dir +mkdir -p $eco_types_dir + +eco_readme="# Features\n\n" +eco_readme+="## Types\n\n" +eco_readme+="### Base\n\n" + +for file in $(find ./x/ecocredit/base/types/v1/features -path -prune -o -name '*.feature' | sort -t '\0' -n); do + name=$(basename "${file%.*}") + eco_readme+="- [$name](./types/$name.html)\n" + "$GOBIN/gherkin2markdown" "$file" > "$eco_types_dir/$name.md" +done + +eco_readme+="### Basket\n\n" + +for file in $(find ./x/ecocredit/basket/types/v1/features -path -prune -o -name '*.feature' | sort -t '\0' -n); do + name=$(basename "${file%.*}") + eco_readme+="- [$name](./types/$name.html)\n" + "$GOBIN/gherkin2markdown" "$file" > "$eco_types_dir/$name.md" +done + +eco_readme+="### Marketplace\n\n" + +for file in $(find ./x/ecocredit/marketplace/types/v1/features -path -prune -o -name '*.feature' | sort -t '\0' -n); do + name=$(basename "${file%.*}") + eco_readme+="- [$name](./types/$name.html)\n" + "$GOBIN/gherkin2markdown" "$file" > "$eco_types_dir/$name.md" +done + +eco_readme+="## Server\n\n" +eco_readme+="### Base\n\n" + +for file in $(find ./x/ecocredit/base/keeper/features -path -prune -o -name '*.feature' | sort -t '\0' -n); do + name=$(basename "${file%.*}") + eco_readme+="- [$name](./server/$name.html)\n" + "$GOBIN/gherkin2markdown" "$file" > "$eco_server_dir/$name.md" +done + +eco_readme+="### Basket\n\n" + +for file in $(find ./x/ecocredit/basket/keeper/features -path -prune -o -name '*.feature' | sort -t '\0' -n); do + name=$(basename "${file%.*}") + eco_readme+="- [$name](./server/$name.html)\n" + "$GOBIN/gherkin2markdown" "$file" > "$eco_server_dir/$name.md" +done + +eco_readme+="### Marketplace\n\n" + +for file in $(find ./x/ecocredit/marketplace/keeper/features -path -prune -o -name '*.feature' | sort -t '\0' -n); do + name=$(basename "${file%.*}") + eco_readme+="- [$name](./server/$name.html)\n" + "$GOBIN/gherkin2markdown" "$file" > "$eco_server_dir/$name.md" +done + +echo -e "$eco_readme" > "$eco_dir/README.md" diff --git a/scripts/generate_swagger_docs.sh b/scripts/generate_swagger_docs.sh new file mode 100755 index 0000000..f1f085e --- /dev/null +++ b/scripts/generate_swagger_docs.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash + +set -eo pipefail + +SWAGGER_DIR=./app/client/docs +SWAGGER_UI_DIR=${SWAGGER_DIR}/swagger-ui + +SDK_VERSION=$(go list -m -f '{{ .Version }}' github.com/cosmos/cosmos-sdk) +IBC_VERSION=$(go list -m -f '{{ .Version }}' github.com/cosmos/ibc-go/v5) + +SDK_RAW_URL=https://raw.githubusercontent.com/cosmos/cosmos-sdk/${SDK_VERSION}/client/docs/swagger-ui/swagger.yaml +IBC_RAW_URL=https://raw.githubusercontent.com/cosmos/ibc-go/${IBC_VERSION}/docs/client/swagger-ui/swagger.yaml + +SWAGGER_UI_VERSION=4.11.0 +SWAGGER_UI_DOWNLOAD_URL=https://github.com/swagger-api/swagger-ui/archive/refs/tags/v${SWAGGER_UI_VERSION}.zip +SWAGGER_UI_PACKAGE_NAME=${SWAGGER_DIR}/swagger-ui-${SWAGGER_UI_VERSION} + +# install swagger-combine if not already installed +npm list -g | grep swagger-combine > /dev/null || npm install -g swagger-combine --no-shrinkwrap + +# install statik if not already installed +go install github.com/rakyll/statik@latest + +# download Cosmos SDK swagger yaml file +echo "SDK version ${SDK_VERSION}" +curl -o ${SWAGGER_DIR}/swagger-sdk.yaml -sfL "${SDK_RAW_URL}" + +# download IBC swagger yaml file +echo "IBC version ${IBC_VERSION}" +curl -o ${SWAGGER_DIR}/swagger-ibc.yaml -sfL "${IBC_RAW_URL}" + +# combine swagger yaml files using nodejs package `swagger-combine` +# all the individual swagger files need to be configured in `config.json` for merging +swagger-combine ${SWAGGER_DIR}/config.json -f yaml \ + -o ${SWAGGER_DIR}/swagger.yaml \ + --continueOnConflictingPaths true \ + --includeDefinitions true + +# if swagger-ui does not exist locally, download swagger-ui and move dist directory to +# swagger-ui directory, then remove zip file and unzipped swagger-ui directory +if [ ! -d ${SWAGGER_UI_DIR} ]; then + # download swagger-ui + curl -o ${SWAGGER_UI_PACKAGE_NAME}.zip -sfL ${SWAGGER_UI_DOWNLOAD_URL} + # unzip swagger-ui package + unzip ${SWAGGER_UI_PACKAGE_NAME}.zip -d ${SWAGGER_DIR} + # move swagger-ui dist directory to swagger-ui directory + mv ${SWAGGER_UI_PACKAGE_NAME}/dist ${SWAGGER_UI_DIR} + # remove swagger-ui zip file and unzipped swagger-ui directory + rm -rf ${SWAGGER_UI_PACKAGE_NAME}.zip ${SWAGGER_UI_PACKAGE_NAME} +fi + +# move generated swagger yaml file to swagger-ui directory +cp ${SWAGGER_DIR}/swagger.yaml ${SWAGGER_DIR}/swagger-ui/ + +# update swagger initializer to default to swagger.yaml +# Note: using -i.bak makes this compatible with both GNU and BSD/Mac +sed -i.bak "s|https://petstore.swagger.io/v2/swagger.json|swagger.yaml|" ${SWAGGER_UI_DIR}/swagger-initializer.js + +# generate statik golang code using updated swagger-ui directory +statik -src=${SWAGGER_DIR}/swagger-ui -dest=${SWAGGER_DIR} -f -m + +# log whether or not the swagger directory was updated +if [ -n "$(git status ${SWAGGER_DIR} --porcelain)" ]; then + echo "Swagger statik file updated" +else + echo "Swagger statik file already in sync" +fi diff --git a/scripts/githooks/commit-msg b/scripts/githooks/commit-msg new file mode 100755 index 0000000..5bbd953 --- /dev/null +++ b/scripts/githooks/commit-msg @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +commitTitle="$(cat $1 | head -n1)" + +# ignore merge +if echo "$commitTitle" | grep -qE "^Merge branch \'"; then + exit 0 +fi + +# check semantic commits +if ! echo "$commitTitle" | grep -qE '^(?:feat|fix|docs|style|refactor|perf|test|chore)\(?(?:\w+|\s|\-|_)?\)?:\s\w+'; then + echo "Your commit was successful but the commit message did not follow semantic commits." + echo "Semantic commits are not required but the first commit of a new branch should be semantic." + echo "See https://www.conventionalcommits.org/en/v1.0.0/ for more information about semantic commits." +fi diff --git a/scripts/githooks/pre-commit b/scripts/githooks/pre-commit new file mode 100755 index 0000000..fed2c4e --- /dev/null +++ b/scripts/githooks/pre-commit @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +CMDS='gofmt goimports misspell' +STAGED_GO_FILES=$(git diff --cached --name-only -- '*.go') +LOCAL_IMPORTS='github.com/tendermint/tendermint,github.com/cosmos/cosmos-sdk,github.com/cosmos/ibc-go,github.com/regen-network/regen-ledger' + +# check go tools +for cmd in ${CMDS}; do + which "${cmd}" &>/dev/null || echo "\"${cmd}\" not found, skipping pre-commit formatting" || exit 0 +done + +# format staged files +if [[ $STAGED_GO_FILES != "" ]]; then + echo "Running pre-commit formatting on staged files..." + + for file in $STAGED_GO_FILES; do + if [[ $file =~ \.pb\.go ]] || [[ $file =~ \.pulsar.go ]] || [[ $file =~ \.cosmos_orm.go ]] || [[ $file =~ \statik.go ]]; then + continue + fi + + gofmt -w -s "$file" + goimports -w -local $LOCAL_IMPORTS "$file" + misspell -w "$file" + git add "$file" + done +fi diff --git a/scripts/go-update-dep-all.sh b/scripts/go-update-dep-all.sh new file mode 100755 index 0000000..d276c96 --- /dev/null +++ b/scripts/go-update-dep-all.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +set -euo pipefail + +if [ -z ${1+x} ]; then + echo "USAGE: + ./scripts/go-update-dep-all.sh + This command updates a dependency in all of the go.mod files which import it. + It should be called with a single argument which is the go module path of the dependency, + with an optional version specified by @." + exit +fi + +dependency=$1 +# in case the user explicitly specified a dependency version with @, we separate +# the dependency module name into dependency_mod +IFS='@' read -ra dependency_mod <<< "$dependency" +dependency_mod=${dependency_mod[0]} + +for modfile in $(find . -name go.mod); do + if grep $dependency_mod $modfile &> /dev/null; then + echo "Updating $modfile" + DIR=$(dirname $modfile) + (cd $DIR; go get -u $dependency) + fi +done diff --git a/scripts/protoc-swagger-gen.sh b/scripts/protoc-swagger-gen.sh new file mode 100755 index 0000000..161b35c --- /dev/null +++ b/scripts/protoc-swagger-gen.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +set -eo pipefail + +SWAGGER_DIR=./app/client/docs + +cd ./proto + +# find all proto directories +proto_dirs=$(find ./regen -path -prune -o -name '*.proto' -print0 | xargs -0 -n1 dirname | sort | uniq) + +# loop through proto directories +for dir in $proto_dirs; do + # generate swagger files (filter query files) + query_file=$(find "${dir}" -maxdepth 1 \( -name 'query.proto' \)) + if [[ ! -z "$query_file" ]]; then + buf generate --template buf.gen.swagger.yaml $query_file + fi +done diff --git a/scripts/protocgen.sh b/scripts/protocgen.sh old mode 100644 new mode 100755 index f9fa21f..c2844c4 --- a/scripts/protocgen.sh +++ b/scripts/protocgen.sh @@ -4,30 +4,32 @@ set -eo pipefail protoc_gen_gocosmos() { if ! grep "github.com/gogo/protobuf => github.com/regen-network/protobuf" go.mod &>/dev/null ; then - echo -e "\tPlease run this command from somewhere inside the cosmos-sdk folder." + echo -e "\tPlease run this command from somewhere inside the regen-ledger folder." return 1 fi - go get github.com/regen-network/cosmos-proto/protoc-gen-gocosmos@latest 2>/dev/null - go get github.com/cosmos/cosmos-sdk@v0.45.6 2>/dev/null + go get github.com/regen-network/cosmos-proto/protoc-gen-gocosmos 2>/dev/null } protoc_gen_gocosmos -cosmos_sdk_dir=$(go list -f '{{ .Dir }}' -m github.com/cosmos/cosmos-sdk) -proto_dirs=$(find ./proto -path -prune -o -name '*.proto' -print0 | xargs -0 -n1 dirname | sort | uniq) +echo "Generating gogo proto code" +cd proto +proto_dirs=$(find ./regen -path -prune -o -name '*.proto' -print0 | xargs -0 -n1 dirname | sort | uniq) for dir in $proto_dirs; do - buf protoc \ - -I "proto" \ - -I "$cosmos_sdk_dir/third_party/proto" \ - -I "$cosmos_sdk_dir/proto" \ - --gocosmos_out=plugins=interfacetype+grpc,\ -Mgoogle/protobuf/any.proto=github.com/cosmos/cosmos-sdk/codec/types:. \ - --grpc-gateway_out=logtostderr=true:. \ - $(find "${dir}" -maxdepth 1 -name '*.proto') - + for file in $(find "${dir}" -maxdepth 1 -name '*.proto'); do + if grep go_package $file &> /dev/null ; then + buf generate --template buf.gen.gogo.yaml $file + fi + done done +cd .. + # move proto files to the right places -cp -r github.com/cosmic-horizon/qwoyn/* ./ +cp -r github.com/regen-network/regen-ledger/* ./ rm -rf github.com + +go mod tidy + +./scripts/protocgen2.sh diff --git a/scripts/protocgen2.sh b/scripts/protocgen2.sh new file mode 100755 index 0000000..3de2c1e --- /dev/null +++ b/scripts/protocgen2.sh @@ -0,0 +1,15 @@ +# this script is for generating protobuf files for the new google.golang.org/protobuf API + +set -eo pipefail + +protoc_gen_install() { + go install github.com/cosmos/cosmos-proto/cmd/protoc-gen-go-pulsar@latest #2>/dev/null + go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest #2>/dev/null + go install github.com/cosmos/cosmos-sdk/orm/cmd/protoc-gen-go-cosmos-orm@latest #2>/dev/null +} + +protoc_gen_install + +echo "Generating API module" +(cd api; find ./ -type f \( -iname \*.pulsar.go -o -iname \*.pb.go -o -iname \*.cosmos_orm.go -o -iname \*.pb.gw.go \) -delete; find . -empty -type d -delete; cd ..) +(cd proto; buf generate --template buf.gen.pulsar.yaml) diff --git a/scripts/release-setup.sh b/scripts/release-setup.sh old mode 100644 new mode 100755 diff --git a/scripts/start_testnode.sh b/scripts/start_testnode.sh new file mode 100755 index 0000000..c82ec9e --- /dev/null +++ b/scripts/start_testnode.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +exit_with_error() +{ + echo "$1" 1>&2 + exit 1 +} + +KeyringBackend=test +Chain=test + +# Get the options +while getopts ":kc:" option; do + case $option in + k) # Keyring backend + KeyringBackend=$OPTARG;; + c) # Enter a name + Chain=$OPTARG;; + \?) # Invalid option + echo "Error: Invalid option" + exit 1 + esac +done + +# Make sure the path is set correctly +export PATH=~/go/bin:$PATH + +echo "REGEN Version: `regen version`" + +regen keys add validator --keyring-backend ${KeyringBackend} || exit_with_error "Error: Validator add failed" +regen keys add delegator --keyring-backend ${KeyringBackend} || exit_with_error "Error: Delegator add failed" +regen init node --chain-id ${Chain} || exit_with_error "Error: Could not init node" + +# Change the staking token to uregen +# Note: sed works differently on different platforms +echo "Updating your staking token to uregen in the genesis file..." +OS=`uname` +if [[ $OS == "Linux"* ]]; then + echo "Your OS is a Linux variant..." + sed -i "s/stake/uregen/g" ~/.regen/config/genesis.json || exit_with_error "Error: Could not update staking token" +elif [[ $OS == "Darwin"* ]]; then + echo "Your OS is Mac OS/darwin..." + sed -i "" "s/stake/uregen/g" ~/.regen/config/genesis.json || exit_with_error "Error: Could not update staking token" +else + # Dunno + echo "Your OS is not supported" + exit 1 +fi + +echo "Adding validator to genesis.json..." +regen add-genesis-account validator 5000000000uregen --keyring-backend ${KeyringBackend} || exit_with_error "Error: Could not add validator to genesis" + +echo "Adding delegator to genesis.json..." +regen add-genesis-account delegator 2000000000uregen --keyring-backend ${KeyringBackend} || exit_with_error "Error: Could not add delegator to genesis" +echo "Creating genesis transaction..." +regen gentx validator 1000000uregen --chain-id ${Chain} --keyring-backend ${KeyringBackend} || exit_with_error "Error: Genesis transaction failed" + +echo "Adding genesis transaction to genesis.json..." +regen collect-gentxs || exit_with_error "Error: Could not add transaction to genesis" + +echo "If there were no errors above, you can now type 'regen start' to start your node" diff --git a/scripts/statesync.bash b/scripts/statesync.bash new file mode 100755 index 0000000..3be845b --- /dev/null +++ b/scripts/statesync.bash @@ -0,0 +1,59 @@ +#!/bin/bash +# This is the Regen Network State sync file, which is based on the Gaia State sync file, which is based on scripts written by Bitcanna and Microtick. The key difference is that this approach uses environment variables to override configuration files. +# http://public-rpc.regen.vitwit.com:26657 +# https://regen.stakesystems.io:2053 +# https://rpc.regen.forbole.com:443 + + + +set -uxe + +# set environment variables +export GOPATH=~/go +export PATH=$PATH:~/go/bin +export HOME_DIR=~/.regen + +MONIKER=$1 +if [ -z $MONIKER ] +then + MONIKER=test-sync +fi + +# MAKE HOME FOLDER AND GET GENESIS +regen init $MONIKER --home $HOME_DIR +wget https://raw.githubusercontent.com/regen-network/mainnet/main/regen-1/genesis.json -O $HOME_DIR/config/genesis.json + +INTERVAL=10 + +# GET TRUST HASH AND TRUST HEIGHT + +LATEST_HEIGHT=$(curl -s http://public-rpc.regen.vitwit.com:26657/block | jq -r .result.block.header.height); +BLOCK_HEIGHT=$(($LATEST_HEIGHT-$INTERVAL)) +TRUST_HASH=$(curl -s "http://public-rpc.regen.vitwit.com:26657/block?height=$BLOCK_HEIGHT" | jq -r .result.block_id.hash) + + +# TELL USER WHAT WE ARE DOING +echo "TRUST HEIGHT: $BLOCK_HEIGHT" +echo "TRUST HASH: $TRUST_HASH" + + +# expor state sync vars +export REGEN_STATESYNC_ENABLE=true +export REGEN_P2P_MAX_NUM_OUTBOUND_PEERS=200 +export REGEN_P2P_MAX_NUM_INBOUND_PEERS=200 +export REGEN_STATESYNC_RPC_SERVERS="http://public-rpc.regen.vitwit.com:26657,https://rpc.regen.forbole.com:443,https://regen.stakesystems.io:2053" +export REGEN_STATESYNC_TRUST_HEIGHT=$BLOCK_HEIGHT +export REGEN_STATESYNC_TRUST_HASH=$TRUST_HASH +export REGEN_P2P_SEEDS=$(curl -s https://raw.githubusercontent.com/regen-network/mainnet/main/regen-1/seed-nodes.txt | paste -sd,) +export REGEN_P2P_PERSISTENT_PEERS=$(curl -s https://raw.githubusercontent.com/regen-network/mainnet/main/regen-1/peer-nodes.txt | paste -sd,) + +sed -i '/persistent_peers =/c\persistent_peers = "'"$REGEN_P2P_PERSISTENT_PEERS"'"' $HOME_DIR/config/config.toml +sed -i '/seeds =/c\seeds = "'"$REGEN_P2P_SEEDS"'"' $HOME_DIR/config/config.toml +sed -i '/max_num_outbound_peers =/c\max_num_outbound_peers = '$REGEN_P2P_MAX_NUM_OUTBOUND_PEERS'' $HOME_DIR/config/config.toml +sed -i '/max_num_inbound_peers =/c\max_num_inbound_peers = '$REGEN_P2P_MAX_NUM_INBOUND_PEERS'' $HOME_DIR/config/config.toml +sed -i '/enable =/c\enable = true' $HOME_DIR/config/config.toml +sed -i '/rpc_servers =/c\rpc_servers = "'"$REGEN_STATESYNC_RPC_SERVERS"'"' $HOME_DIR/config/config.toml +sed -i '/trust_height =/c\trust_height = '$REGEN_STATESYNC_TRUST_HEIGHT'' $HOME_DIR/config/config.toml +sed -i '/trust_hash =/c\trust_hash = "'"$REGEN_STATESYNC_TRUST_HASH"'"' $HOME_DIR/config/config.toml + +regen start --x-crisis-skip-assert-invariants diff --git a/scripts/test_liveness.sh b/scripts/test_liveness.sh new file mode 100755 index 0000000..86c2265 --- /dev/null +++ b/scripts/test_liveness.sh @@ -0,0 +1,64 @@ +#!/bin/bash + +CNT=0 +ITER=$1 +SLEEP=$2 +NUM_BLOCKS=$3 +RESTART_FREQ=$4 +NODE_ADDR=$5 + +if [ -z "$1" ]; then + echo "Error: Need to input number of iterations to run." + exit 1 +fi + +if [ -z "$2" ]; then + echo "Error: Need to input number of seconds to sleep between iterations." + exit 1 +fi + +if [ -z "$3" ]; then + echo "Error: Need to input block height to declare completion." + exit 1 +fi + +if [ -z "$4" ]; then + echo "Error: Need to input random container restart frequency." + exit 1 +fi + +if [ -z "$5" ]; then + echo "Error: Need to input node address to poll." + exit 1 +fi + +docker_containers=( $(docker ps -q -f name=regen --format='{{.Names}}') ) + +while [ ${CNT} -lt "$ITER" ]; do + curr_block=$(curl -s "$NODE_ADDR":26657/status | jq -r '.result.sync_info.latest_block_height') + + if [ ! -z "${curr_block}" ] ; then + echo "Number of Blocks: ${curr_block}" + fi + + if [ ! -z "${curr_block}" ] && [ "${curr_block}" -gt "${NUM_BLOCKS}" ]; then + echo "Number of blocks reached. Success!" + exit 0 + fi + + # Emulate network chaos: + # - Pick a random container and restart it + # - RESTART_FREQ is the number of blocks between restarts + if ! ((CNT % RESTART_FREQ)); then + rand_container=${docker_containers["$(RANDOM % ${#docker_containers[@]})"]}; + echo "Restarting random docker container ${rand_container}" + docker restart "${rand_container}" &>/dev/null & + fi + + (CNT++) + + sleep "$SLEEP" +done + +echo "Timeout reached. Failure!" +exit 1 diff --git a/scripts/testnet_from_export.sh b/scripts/testnet_from_export.sh new file mode 100755 index 0000000..079d863 --- /dev/null +++ b/scripts/testnet_from_export.sh @@ -0,0 +1,233 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2094 + +set -e + +# home is directory path of node home +home=$HOME/.regen + +# export is the filepath of the state export +export=$HOME/Downloads/state_export.json + +# set script input options +while getopts ":h:e:" option; do + case $option in + h) + home=$OPTARG;; + e) + export=$OPTARG;; + \?) + echo "Error: invalid option" + exit 1 + esac +done + +# check home directory and confirm removal if exists +if [ -d "$home" ]; then + read -r -p "WARNING: This script will remove $home. Would you like to continue? [y/N] " confirm + case "$confirm" in + [yY][eE][sS]|[yY]) + rm -rf "$home" + ;; + *) + exit 0 + ;; + esac +fi + +# node_genesis is the node genesis file +node_genesis=$home/config/genesis.json + +# tmp_dir is the temporary directory used for +# exported state to be merged into node_genesis +tmp_dir=./tmp + +# tmp_genesis is the exported state from a single +# node network started by the new validator +tmp_genesis=$tmp_dir/genesis.json + +# chain_id is the chain id +chain_id=$(jq .chain_id "$export") + +# bond_denom is the staking token denom +bond_denom=$(jq .app_state.staking.params.bond_denom "$export") + +# amount is the amount of tokens to stake (must be +# more than 2/3 of the total amount staked) +amount="1000000000000000" + +# tokens is the token amount and denom to stake +tokens=$(echo "${amount}${bond_denom}" | tr -d '"') + +# tokens2 is token amount x2 providing a starting balance with amount +tokens2=$(echo "$(( "$amount" + "$amount" ))${bond_denom}" | tr -d '"') + +# gas_price is the minimum gas price for the node +gas_price=$(echo "0${bond_denom}" | tr -d '"') + +# build binary +make build + +# add test key using keyring backend +./build/regen keys add test --home "$home" --keyring-backend test + +# create genesis values for validator node +./build/regen init test --chain-id "$chain_id" --home "$home" +./build/regen add-genesis-account test "$tokens2" --home "$home" --keyring-backend test +./build/regen gentx test "$tokens" --chain-id "$chain_id" --home "$home" --keyring-backend test +./build/regen collect-gentxs --home "$home" + +# update default denom to bond denom +sed -i "s|\"stake\"|$bond_denom|g" "$node_genesis" + +# start node and deliver genesis transaction +./build/regen start --home "$home" --halt-height 1 --minimum-gas-prices "$gas_price" && wait $! + +# create temporary directory +mkdir -p "$tmp_dir" + +# copy single node network state to temporary genesis file +./build/regen export --home "$home" > "$tmp_genesis" + +# keys are the genesis file key names to update +keys=( + "app_state.auth.accounts" + "app_state.bank.balances" + "app_state.bank.supply" + "app_state.distribution.delegator_starting_infos" + "app_state.distribution.outstanding_rewards" + "app_state.distribution.validator_current_rewards" + "app_state.distribution.validator_historical_rewards" + "app_state.slashing.signing_infos" + "app_state.staking.delegations" + "app_state.staking.last_total_power" + "app_state.staking.last_validator_powers" + "app_state.staking.params.max_validators" + "app_state.staking.validators" + "validators" +) + +# copy single node network state to json files +for i in "${!keys[@]}"; do + + # simple var + k=${keys[$i]} + + # create json file for each state object (some are too large + # to pass directly to jq so we create a file for each) + cat <<< $(jq ".$k" "$tmp_genesis") > "$tmp_dir/$k.json" + +done + +# overwrite node genesis file with state export +cp "$export" "$node_genesis" + +# add single node network state to node genesis file +for i in "${!keys[@]}"; do + + # simple var + k=${keys[$i]} + + if [ "$k" == "app_state.bank.balances" ]; then + + # 1) add balance for validator account + + # append balances from single node network state + cat <<< $(jq --argfile v "$tmp_dir/$k.json" '.'"$k"' += $v' "$node_genesis") > "$node_genesis" + + # 2) update balance for "bonded tokens pool" + + # get account address for "bonded tokens pool" + a1=$(jq "[.app_state.auth.accounts[]|select(.name==\"bonded_tokens_pool\")][0].base_account.address" "$node_genesis") + + # get balance amount for "bonded tokens pool" + b1=$(jq "[.${k}[]|select(.address==$a1)][0].coins[]|select(.denom==$bond_denom).amount" "$node_genesis" | tr -d '"') + + # add new validator stake amount to balance amount + coins='[{"amount": "'$(( "$b1" + "$amount" ))'", "denom": '$bond_denom'}]' + + # create json object for account balance with deleted bond denom balance + json=$(jq '[.'"$k"'[]|select(.address=='"$a1"')][0]|del(.coins[]|select(.denom=='"$bond_denom"'))' "$node_genesis") + + # update account balance with updated bond denom balance + json=$(jq '.coins = '"$coins"'' <<< "$json") + + # delete old account balance + cat <<< $(jq 'del(.'"$k"'[]|select(.address=='"$a1"'))' "$node_genesis") > "$node_genesis" + + # add updated account balance + cat <<< $(jq --argjson v "[$json]" '.'"$k"' += $v' "$node_genesis") > "$node_genesis" + + # 3) update balance for "fee collector" + + # get account address for "fee collector" + a2=$(jq "[.app_state.auth.accounts[]|select(.name==\"fee_collector\")][0].base_account.address" "$node_genesis") + + # TODO: "fee collector" does not have a balance in state exports used when + # testing therefore simply adding the balance in step (1) is sufficient but + # this will need to be updated if "fee collector" has an existing balance + + elif [ "$k" == "app_state.bank.supply" ]; then + + # get supply amount for bond denom from single node network state + a1=$(jq "[.${k}[]|select(.denom==$bond_denom)][0].amount" "$tmp_genesis" | tr -d '"') + + # get supply amount for bond denom from node genesis + a2=$(jq "[.${k}[]|select(.denom==$bond_denom)][0].amount" "$node_genesis" | tr -d '"') + + # set new supply for bond denom + ts='[{"amount": "'$(( "$a1" + "$a2" ))'", "denom": '$bond_denom'}]' + + # delete supply for bond denom + cat <<< $(jq 'del(.'"$k"'[]|select(.denom=='"$bond_denom"'))' "$node_genesis") > "$node_genesis" + + # add updated supply for bond denom + cat <<< $(jq --argjson v "$ts" '.'"$k"' += $v' "$node_genesis") > "$node_genesis" + + elif [ "$k" == "app_state.staking.last_total_power" ]; then + + # get last total power from single node network state + p1=$(jq ".$k" "$tmp_genesis" | tr -d '"') + + # get last total power from state export + p2=$(jq ".$k" "$node_genesis" | tr -d '"') + + # set new last total power + tp=$(( "$p1" + "$p2" )) + + # update last total power + cat <<< $(jq --arg v "$tp" '.'"$k"' = $v' "$node_genesis") > "$node_genesis" + + elif [ "$k" == "app_state.staking.params.max_validators" ]; then + + # get max validators from state export + mv=$(jq ".$k" "$node_genesis" | tr -d '"') + + # update max validators to include new validator + cat <<< $(jq --arg v "$(( "$mv" + 1 ))" '.'"$k"' = $v' "$node_genesis") > "$node_genesis" + + else + + # append single node network state values + cat <<< $(jq --argfile v "$tmp_dir/$k.json" '.'"$k"' += $v' "$node_genesis") > "$node_genesis" + + fi +done + +# remove previous state (but keep validator state) +rm -rf "$home/data/application.db/" +rm -rf "$home/data/blockstore.db/" +rm -rf "$home/data/cs.wal/" +rm -rf "$home/data/evidence.db/" +rm -rf "$home/data/snapshots/" +rm -rf "$home/data/state.db/" +rm -rf "$home/data/tx_index.db/" + +# remove temporary directory +rm -rf $tmp_dir + +# reduce voting period to 20 seconds +cat <<< $(jq '.app_state.gov.voting_params.voting_period = "20s"' "$node_genesis") > "$node_genesis" + +# start node +./build/regen start --home "$home" --fast_sync=false --minimum-gas-prices "$gas_price"