diff --git a/README.md b/README.md index b9dce52b..21f0a93d 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,10 @@ 3. [Vault](#vault) 3. [Installation](#installation) 1. [Binary](#binary) - 2. [Docker Image](#docker-image) - 3. [Source Files](#source-files) - 4. [Kubernetes (advanced)](#kubernetes-advanced) + 2. [Install Script](#install-script-linux-and-macos) + 3. [Docker Image](#docker-image) + 4. [Source Files](#source-files) + 5. [Kubernetes (advanced)](#kubernetes-advanced) 4. [Usage](#usage) 1. [Step 1. Create mnemonic](#step-1-create-mnemonic) 2. [Step 2. Create validator keys](#step-2-create-validator-keys) @@ -113,6 +114,34 @@ use of flags is optional): Head to [Usage](#usage) to launch your operator service. +### Install script (Linux and macOS) + +To install a binary for the latest release, run: + +```bash +curl -sSfL https://raw.githubusercontent.com/stakewise/v3-operator/master/scripts/install.sh | sh -s +``` + +The binary will be installed inside the ~/bin directory. Add the binary to your path: + +```bash +export PATH=$PATH:~/bin +``` + +If you want to install a specific version to a custom location, run: + +```bash +curl -sSfL https://raw.githubusercontent.com/stakewise/v3-operator/master/scripts/install.sh | sh -s -- -b vX.X.X +``` + +You will execute Operator Service commands using the below format (note that the use of flags is optional): + +```bash +operator COMMAND --flagA=123 --flagB=xyz +``` + +Head to [Usage](#usage) to launch your operator service. + ### Docker Image Pull the latest docker operator docker image: @@ -239,8 +268,8 @@ Deposit data saved to /home/user/.stakewise/0x3320a...68/keystores/deposit_data. You may not want the operator service to have direct access to the validator keys. Validator keystores do not need to be present directly in the operator. You can check the [remote signer](https://docs.stakewise.io/for-operators/operator-service/running-with-remote-signer) -or [Hashicorp Vault](https://docs.stakewise.io/for-operators/operator-service/running-with-hashi-vault) guides on how to run Operator -Service with them. +or [Hashicorp Vault](https://docs.stakewise.io/for-operators/operator-service/running-with-hashi-vault) guides on how to +run Operator Service with them. **Remember to upload the newly generated validator keys to the validator(s). For that, please follow a guide for your consensus client. The password for your keystores is located in the `password.txt` file in the keystores folder.** @@ -323,8 +352,8 @@ and execution endpoints as flags. If you **did not** use Operator Service to generate hot wallet, you will need to add the following flags: -- `--hot-wallet-file` - path to the password-protected *.txt* file containing your hot wallet private key. -- `--hot-wallet-password-file` - path to a *.txt* file containing the password to open the protected hot wallet private +- `--hot-wallet-file` - path to the password-protected _.txt_ file containing your hot wallet private key. +- `--hot-wallet-password-file` - path to a _.txt_ file containing the password to open the protected hot wallet private key file. If you **did not** use Operator Service to generate validator keys, you will need to add the following flag: @@ -421,11 +450,11 @@ Validators 513571, 513572, 513861 exits successfully initiated ### Update Vault state (Harvest Vault) -Updating the *Vault state* distributes the Vault fee to the Vault fee address and updates each staker's position. If an +Updating the _Vault state_ distributes the Vault fee to the Vault fee address and updates each staker's position. If an ERC-20 token was chosen during Vault creation, the Vault specific ERC-20 reprices based on the rewards/penalties since the previous update and the Vault fees are distributed in newly minted ERC-20 tokens. -By default, each *Vault state* gets updated whenever a user interacts with the Vault (deposit, withdraw, etc.), with a +By default, each _Vault state_ gets updated whenever a user interacts with the Vault (deposit, withdraw, etc.), with a 12 hours cooldown. Vault state can also be updated by the Vault operator(s) by passing the `--harvest-vault` flag to the Operator Service `start` command. Harvest occurs every 12 hours and the gas fees are paid by the hot wallet linked to the Operator Service. diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100755 index 00000000..830975a2 --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,386 @@ +#!/bin/sh +# Scripts inspired from https://github.com/ava-labs/avalanche-cli + +set -e + +usage() { + this=$1 + cat </dev/null +} +echoerr() { + echo "$@" 1>&2 +} +log_prefix() { + echo "$0" +} +_logp=6 +log_set_priority() { + _logp="$1" +} +log_priority() { + if test -z "$1"; then + echo "$_logp" + return + fi + [ "$1" -le "$_logp" ] +} +log_tag() { + case $1 in + 0) echo "emerg" ;; + 1) echo "alert" ;; + 2) echo "crit" ;; + 3) echo "err" ;; + 4) echo "warning" ;; + 5) echo "notice" ;; + 6) echo "info" ;; + 7) echo "debug" ;; + *) echo "$1" ;; + esac +} +log_debug() { + log_priority 7 || return 0 + echoerr "$(log_prefix)" "$(log_tag 7)" "$@" +} +log_info() { + log_priority 6 || return 0 + echoerr "$(log_prefix)" "$(log_tag 6)" "$@" +} +log_err() { + log_priority 3 || return 0 + echoerr "$(log_prefix)" "$(log_tag 3)" "$@" +} +log_crit() { + log_priority 2 || return 0 + echoerr "$(log_prefix)" "$(log_tag 2)" "$@" +} +uname_os() { + os=$(uname -s | tr '[:upper:]' '[:lower:]') + case "$os" in + msys*) os="windows" ;; + mingw*) os="windows" ;; + cygwin*) os="windows" ;; + win*) os="windows" ;; + esac + echo "$os" +} +uname_arch() { + arch=$(uname -m) + case $arch in + x86_64) arch="amd64" ;; + x86) arch="386" ;; + i686) arch="386" ;; + i386) arch="386" ;; + aarch64) arch="arm64" ;; + armv5*) arch="armv5" ;; + armv6*) arch="armv6" ;; + armv7*) arch="armv7" ;; + esac + echo ${arch} +} +uname_os_check() { + os=$(uname_os) + case "$os" in + darwin) return 0 ;; + dragonfly) return 0 ;; + freebsd) return 0 ;; + linux) return 0 ;; + android) return 0 ;; + nacl) return 0 ;; + netbsd) return 0 ;; + openbsd) return 0 ;; + plan9) return 0 ;; + solaris) return 0 ;; + windows) return 0 ;; + esac + log_crit "uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value. Please file bug at https://github.com/client9/shlib" + return 1 +} +uname_arch_check() { + arch=$(uname_arch) + case "$arch" in + 386) return 0 ;; + amd64) return 0 ;; + arm64) return 0 ;; + armv5) return 0 ;; + armv6) return 0 ;; + armv7) return 0 ;; + ppc64) return 0 ;; + ppc64le) return 0 ;; + mips) return 0 ;; + mipsle) return 0 ;; + mips64) return 0 ;; + mips64le) return 0 ;; + s390x) return 0 ;; + amd64p32) return 0 ;; + esac + log_crit "uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value. Please file bug report at https://github.com/client9/shlib" + return 1 +} +untar() { + tarball=$1 + case "${tarball}" in + *.tar.gz | *.tgz) tar --no-same-owner --strip-components=1 -xzf "${tarball}" ;; + *.tar) tar --no-same-owner -xf "${tarball}" ;; + *.zip) unzip "${tarball}" ;; + *) + log_err "untar unknown archive format for ${tarball}" + return 1 + ;; + esac +} +http_download_curl() { + local_file=$1 + source_url=$2 + header=$3 + if [ -z "$header" ]; then + code=$(curl -w '%{http_code}' -sL -o "$local_file" "$source_url") + else + code=$(curl -w '%{http_code}' -sL -H "$header" -o "$local_file" "$source_url") + fi + if [ "$code" != "200" ]; then + log_debug "http_download_curl received HTTP status $code" + return 1 + fi + return 0 +} +http_download_wget() { + local_file=$1 + source_url=$2 + header=$3 + if [ -z "$header" ]; then + wget -q -O "$local_file" "$source_url" + else + wget -q --header "$header" -O "$local_file" "$source_url" + fi +} +http_download() { + log_debug "http_download $2" + if is_command curl; then + http_download_curl "$@" + return + elif is_command wget; then + http_download_wget "$@" + return + fi + log_crit "http_download unable to find wget or curl" + return 1 +} +http_copy() { + tmp=$(mktemp) + http_download "${tmp}" "$1" "$2" || return 1 + body=$(cat "$tmp") + rm -f "${tmp}" + echo "$body" +} +github_release() { + owner_repo=$1 + version=$2 + test -z "$version" && version="v1.0.8" + giturl="https://github.com/${owner_repo}/releases/${version}" + json=$(http_copy "$giturl" "Accept:application/json") + test -z "$json" && return 1 + version=$(echo "$json" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//') + test -z "$version" && return 1 + echo "$version" +} +hash_sha256() { + TARGET=${1:-/dev/stdin} + if is_command gsha256sum; then + hash=$(gsha256sum "$TARGET") || return 1 + echo "$hash" | cut -d ' ' -f 1 + elif is_command sha256sum; then + hash=$(sha256sum "$TARGET") || return 1 + echo "$hash" | cut -d ' ' -f 1 + elif is_command shasum; then + hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1 + echo "$hash" | cut -d ' ' -f 1 + elif is_command openssl; then + hash=$(openssl -dst openssl dgst -sha256 "$TARGET") || return 1 + echo "$hash" | cut -d ' ' -f a + else + log_crit "hash_sha256 unable to find command to compute sha-256 hash" + return 1 + fi +} +hash_sha256_verify() { + TARGET=$1 + checksums=$2 + if [ -z "$checksums" ]; then + log_err "hash_sha256_verify checksum file not specified in arg2" + return 1 + fi + BASENAME=${TARGET##*/} + want=$(cat "${checksums}" 2>/dev/null | tr '\t' ' ' | cut -d ' ' -f 1) + if [ -z "$want" ]; then + log_err "hash_sha256_verify unable to find checksum for '${TARGET}' in '${checksums}'" + return 1 + fi + got=$(hash_sha256 "$TARGET") + if [ "$want" != "$got" ]; then + log_err "hash_sha256_verify checksum for '$TARGET' did not verify ${want} vs $got" + return 1 + fi +} +cat /dev/null <