├── CNAME ├── favicon.png ├── assets └── screenshot.png ├── osmosis-installer.sh ├── .github └── workflows │ ├── test-client.yaml │ └── update-version.yaml ├── index.html ├── README.md └── i.py /CNAME: -------------------------------------------------------------------------------- 1 | get.osmosis.zone -------------------------------------------------------------------------------- /favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osmosis-labs/osmosis-installer/HEAD/favicon.png -------------------------------------------------------------------------------- /assets/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osmosis-labs/osmosis-installer/HEAD/assets/screenshot.png -------------------------------------------------------------------------------- /osmosis-installer.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # run get.osmosis.zone python script 4 | curl -sL https://get.osmosis.zone/install > i.py && python3 i.py 5 | 6 | # after completion, source the profile 7 | source ~/.profile 8 | -------------------------------------------------------------------------------- /.github/workflows/test-client.yaml: -------------------------------------------------------------------------------- 1 | name: Test Installer (Client) 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - dev 8 | pull_request: 9 | branches: 10 | - main 11 | - dev 12 | 13 | jobs: 14 | install: 15 | name: test-installer-client 16 | runs-on: ${{ matrix.os }} 17 | 18 | strategy: 19 | matrix: 20 | os: [ubuntu-latest, macos-latest] 21 | network: [osmosis-1, osmo-test-5] 22 | fail-fast: false 23 | 24 | steps: 25 | - 26 | name: Checkout Repository 27 | uses: actions/checkout@v3 28 | - 29 | name: Set up Python 30 | uses: actions/setup-python@v4 31 | with: 32 | python-version: '3.10' 33 | - 34 | name: Run Installer 35 | env: 36 | TERM: xterm 37 | run: | 38 | python i.py \ 39 | --install client \ 40 | --network ${{ matrix.network }} \ 41 | --home ${{ runner.temp }}/.osmosisd \ 42 | --moniker osmosis \ 43 | --overwrite 44 | - 45 | name: Run osmosisd 46 | run: | 47 | osmosisd status --home ${{ runner.temp }}/.osmosisd 48 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Osmosis Installer 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 43 | 44 | 55 | 56 | 57 |
58 |
59 |

Osmosis Installer

60 |
61 |
62 | 63 |
64 |
65 | 66 |

67 | To install Osmosisd, run the following in your terminal, then follow the on-screen instructions. 68 |

69 |
70 | 71 | 72 |
73 | 74 |
75 |
76 | 77 |
78 |

79 | Please visit the official documentation to learn more about the Osmosis Bash Installer. 80 |

81 |
82 |
83 |
84 | 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Osmosis Installer 🧪 ⚙️ 2 | 3 |

4 | Screenshot 5 |

6 | 7 | The Osmosis Installer is a simple tool that provides an easy and streamlined way to install and configure Osmosis. 8 | 9 | ## ✨ Features 10 | 11 | The Osmosis Installer offers the following key features: 12 | 13 | - 🔧 **Dependency Installation**: Automatically installs the necessary dependencies for running Osmosis. 14 | 15 | - 🌐 **Network Joining**: Allows you to choose between joining the testnet or mainnet. 16 | 17 | - ⬇️ **Binary Download**: Downloads the Osmosis binary for the selected network. 18 | 19 | - ⚙️ **Configuration Customization**: Provides options for customizing your Osmosis configuration. 20 | 21 | - 🔌 **Background Service Setup**: Sets up either cosmovisor or osmosisd as background services for continuous operation. 22 | 23 | ## Installation 24 | 25 | To install `osmosisd`, follow these steps: 26 | 27 | 1. Open your terminal. 28 | 29 | 2. Run the following command: 30 | 31 | ```bash 32 | source <(curl -sL https://get.osmosis.zone/run) 33 | ``` 34 | 35 | > ⚠️ Requires `wget` to be present in the machine 36 | 37 | This command will download and execute the Osmosis Installer script. 38 | 39 | Follow the on-screen instructions to complete the installation. The installer will guide you through the installation process, allowing you to make choices such as selecting the installation type (node or client) and customizing various settings. 40 | 41 | > 💡 Optional Flags: 42 | > You can use some flag to specify the choices. For example, if you want to install the node, you can use the `--install node` flag. If you want to install the client, you can use the `--install client` flag. 43 | 44 | ### Further Optimizations 45 | 46 | #### Adding Swap 47 | 48 | Adding Swap Space 49 | For optimal performance, it is recommended to have at least 64GB of RAM when running an Osmosis node. If your system does not meet this requirement, setting up swap space can help ensure smooth operation and prevent out-of-memory (OOM) errors. 50 | 51 | To add swap space to your system, follow these steps: 52 | 53 | 1. Calculate the additional swap space needed: Subtract the amount of RAM you have from 64GB to determine the required swap space. 54 | For example, if you have 32GB of RAM, you would need to add 32GB of swap space. 55 | 56 | 2. Open a terminal and execute the following commands: 57 | 58 | ```bash 59 | sudo swapoff -a 60 | sudo fallocate -l G /swapfile 61 | sudo chmod 600 /swapfile 62 | sudo mkswap /swapfile 63 | sudo swapon /swapfile 64 | sudo cp /etc/fstab /etc/fstab.bak 65 | echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab 66 | ``` 67 | 68 | Replace with the size of the additional swap space you calculated. For example, if you need to add 32GB of swap space, replace with 32. 69 | 70 | 3. Verify that the swap space has been successfully added by executing the following command: 71 | 72 | ```bash 73 | sudo swapon --show 74 | ``` 75 | 76 | #### Increasing Open File Limits 77 | 78 | In Linux, the number of files limit affects the maximum number of connections a node can have, 79 | By default, the operating system imposes limits on the number of files a process can open simultaneously to prevent resource exhaustion. 80 | 81 | To increase the open file limits, follow these steps: 82 | 83 | 1. Open the terminal and run the command: 84 | ```bash 85 | sudo /etc/security/limits.conf 86 | ``` 87 | Replace `` with your preferred text editor (`nano`, `vi`, etc.). 88 | 89 | 2. Add the following lines at the end of the file: 90 | ``` 91 | * hard nofile 65536 92 | * soft nofile 65536 93 | ``` 94 | 95 | 3. Save and close the file. 96 | 97 | 4. Run the command: 98 | ```bash 99 | sudo /etc/pam.d/common-session 100 | ``` 101 | 102 | 5. Add the following line at the end of the file: 103 | ``` 104 | session required pam_limits.so 105 | ``` 106 | 107 | 6. Save and close the file. 108 | 109 | 7. Restart your system. 110 | -------------------------------------------------------------------------------- /.github/workflows/update-version.yaml: -------------------------------------------------------------------------------- 1 | name: Update Installer 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' # Runs every day at midnight 6 | workflow_dispatch: 7 | 8 | permissions: 9 | contents: write 10 | pull-requests: write 11 | 12 | jobs: 13 | update-testnet-version: 14 | runs-on: ubuntu-latest 15 | env: 16 | RPC_URL: https://rpc.testnet.osmosis.zone/abci_info 17 | VERSION_VAR: TESTNET_VERSION 18 | 19 | steps: 20 | - name: Checkout repository 21 | uses: actions/checkout@v4 22 | 23 | - name: Check current version from RPC 24 | run: | 25 | RESPONSE=$(curl -s --retry 5 --retry-delay 5 --connect-timeout 30 -H "Accept: application/json" ${{ env.RPC_URL }}) 26 | NETWORK_VERSION=$(echo $RESPONSE | jq -r '.result.response.version') 27 | echo "NETWORK_VERSION=$NETWORK_VERSION" >> $GITHUB_ENV 28 | 29 | - name: Get current version from file 30 | run: | 31 | FILE_VERSION=$(grep '${{ env.VERSION_VAR }}' i.py | cut -d'"' -f2 | head -n 1) 32 | echo "FILE_VERSION=$FILE_VERSION" >> $GITHUB_ENV 33 | 34 | - name: Validate version format 35 | run: | 36 | if [[ ! ${{ env.NETWORK_VERSION }} =~ ^[0-9]+\.[0-9]+\.[0-9]+(-rc[0-9]+)?$ ]]; then 37 | echo "Version ${{ env.NETWORK_VERSION }} does not match the format X.Y.Z or X.Y.Z-rcN" 38 | exit 1 39 | fi 40 | 41 | - name: Compare versions 42 | id: compare_versions 43 | run: | 44 | compare_versions() { 45 | local v1=$1 46 | local v2=$2 47 | if [[ $v1 == $v2 ]]; then 48 | echo "COMPARE_RESULT=0" >> $GITHUB_ENV 49 | return 50 | fi 51 | local IFS=. 52 | local i 53 | local version1=($v1) 54 | local version2=($v2) 55 | for ((i=0; i<${#version1[@]}; i++)); do 56 | if [[ ${version1[i]} -gt ${version2[i]} ]]; then 57 | echo "COMPARE_RESULT=1" >> $GITHUB_ENV 58 | return 59 | fi 60 | if [[ ${version1[i]} -lt ${version2[i]} ]]; then 61 | echo "COMPARE_RESULT=2" >> $GITHUB_ENV 62 | return 63 | fi 64 | done 65 | echo "COMPARE_RESULT=0" >> $GITHUB_ENV 66 | } 67 | 68 | compare_versions ${{ env.NETWORK_VERSION }} ${{ env.FILE_VERSION }} 69 | 70 | - name: Generate branch name 71 | if: env.COMPARE_RESULT == 1 72 | run: | 73 | RANDOM_ID=$(openssl rand -hex 6) 74 | BRANCH_NAME="UPDATE-${{ env.VERSION_VAR }}-TO-${{ env.NETWORK_VERSION }}-$RANDOM_ID" 75 | echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV 76 | 77 | - name: Update version if different 78 | if: env.COMPARE_RESULT == 1 79 | run: | 80 | sed -i "s/${{ env.VERSION_VAR }} = \".*\"/${{ env.VERSION_VAR }} = \"${{ env.NETWORK_VERSION }}\"/" i.py 81 | 82 | - name: Create Pull Request 83 | if: env.COMPARE_RESULT == 1 84 | uses: peter-evans/create-pull-request@v6 85 | with: 86 | token: ${{ secrets.GITHUB_TOKEN }} 87 | commit-message: Update ${{ env.VERSION_VAR }} version to ${{ env.NETWORK_VERSION }} 88 | base: main 89 | branch: ${{env.BRANCH_NAME}} 90 | title: Update ${{ env.VERSION_VAR }} version to ${{ env.NETWORK_VERSION }} 91 | body: | 92 | This PR updates the ${{ env.VERSION_VAR }} version to ${{ env.NETWORK_VERSION }}. 93 | 94 | 🤖 This PR was autogenerated. 95 | 96 | 97 | update-mainnet-version: 98 | runs-on: ubuntu-latest 99 | env: 100 | RPC_URL: https://rpc.osmosis.zone/abci_info 101 | VERSION_VAR: MAINNET_VERSION 102 | 103 | steps: 104 | - name: Checkout repository 105 | uses: actions/checkout@v4 106 | 107 | - name: Check current version from RPC 108 | run: | 109 | RESPONSE=$(curl -s --retry 5 --retry-delay 5 --connect-timeout 30 -H "Accept: application/json" ${{ env.RPC_URL }}) 110 | NETWORK_VERSION=$(echo $RESPONSE | jq -r '.result.response.version') 111 | echo "NETWORK_VERSION=$NETWORK_VERSION" >> $GITHUB_ENV 112 | 113 | - name: Get current version from file 114 | run: | 115 | FILE_VERSION=$(grep '${{ env.VERSION_VAR }}' i.py | cut -d'"' -f2 | head -n 1) 116 | echo "FILE_VERSION=$FILE_VERSION" >> $GITHUB_ENV 117 | 118 | - name: Validate version format 119 | run: | 120 | if [[ ! ${{ env.NETWORK_VERSION }} =~ ^[0-9]+\.[0-9]+\.[0-9]+(-rc[0-9]+)?$ ]]; then 121 | echo "Version ${{ env.NETWORK_VERSION }} does not match the format X.Y.Z or X.Y.Z-rcN" 122 | exit 1 123 | fi 124 | 125 | - name: Compare versions 126 | id: compare_versions 127 | run: | 128 | compare_versions() { 129 | local v1=$1 130 | local v2=$2 131 | if [[ $v1 == $v2 ]]; then 132 | echo "COMPARE_RESULT=0" >> $GITHUB_ENV 133 | return 134 | fi 135 | local IFS=. 136 | local i 137 | local version1=($v1) 138 | local version2=($v2) 139 | for ((i=0; i<${#version1[@]}; i++)); do 140 | if [[ ${version1[i]} -gt ${version2[i]} ]]; then 141 | echo "COMPARE_RESULT=1" >> $GITHUB_ENV 142 | return 143 | fi 144 | if [[ ${version1[i]} -lt ${version2[i]} ]]; then 145 | echo "COMPARE_RESULT=2" >> $GITHUB_ENV 146 | return 147 | fi 148 | done 149 | echo "COMPARE_RESULT=0" >> $GITHUB_ENV 150 | } 151 | 152 | compare_versions ${{ env.NETWORK_VERSION }} ${{ env.FILE_VERSION }} 153 | 154 | - name: Generate branch name 155 | if: env.COMPARE_RESULT == 1 156 | run: | 157 | RANDOM_ID=$(openssl rand -hex 6) 158 | BRANCH_NAME="UPDATE-${{ env.VERSION_VAR }}-TO-${{ env.NETWORK_VERSION }}-$RANDOM_ID" 159 | echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV 160 | 161 | - name: Update version if different 162 | if: env.COMPARE_RESULT == 1 163 | run: | 164 | sed -i "s/${{ env.VERSION_VAR }} = \".*\"/${{ env.VERSION_VAR }} = \"${{ env.NETWORK_VERSION }}\"/" i.py 165 | 166 | - name: Create Pull Request 167 | if: env.COMPARE_RESULT == 1 168 | uses: peter-evans/create-pull-request@v6 169 | with: 170 | token: ${{ secrets.GITHUB_TOKEN }} 171 | commit-message: Update ${{ env.VERSION_VAR }} version to ${{ env.NETWORK_VERSION }} 172 | base: main 173 | branch: ${{env.BRANCH_NAME}} 174 | title: Update ${{ env.VERSION_VAR }} version to ${{ env.NETWORK_VERSION }} 175 | body: | 176 | This PR updates the ${{ env.VERSION_VAR }} version to ${{ env.NETWORK_VERSION }}. 177 | 178 | 🤖 This PR was autogenerated. 179 | -------------------------------------------------------------------------------- /i.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import argparse 4 | import subprocess 5 | import platform 6 | import random 7 | import textwrap 8 | import urllib.request as urlrq 9 | import ssl 10 | import json 11 | import tempfile 12 | from enum import Enum 13 | 14 | DEFAULT_OSMOSIS_HOME = os.path.expanduser("~/.osmosisd") 15 | DEFAULT_MONIKER = "osmosis" 16 | 17 | NETWORK_CHOICES = ['osmosis-1', 'osmo-test-5'] 18 | INSTALL_CHOICES = ['node', 'client', 'localosmosis'] 19 | PRUNING_CHOICES = ['default', 'nothing', 'everything'] 20 | 21 | MAINNET_VERSION = "31.0.0" 22 | TESTNET_VERSION = "31.0.0-rc0" 23 | 24 | # CLI arguments 25 | parser = argparse.ArgumentParser(description="Osmosis Installer") 26 | 27 | parser.add_argument( 28 | "--home", 29 | type=str, 30 | help=f"Osmosis installation location", 31 | ) 32 | 33 | parser.add_argument( 34 | '-m', 35 | "--moniker", 36 | type=str, 37 | help="Moniker name for the node (Default: 'osmosis')", 38 | ) 39 | 40 | parser.add_argument( 41 | '-v', 42 | '--verbose', 43 | action='store_true', 44 | help="Enable verbose output", 45 | dest="verbose" 46 | ) 47 | 48 | parser.add_argument( 49 | '-o', 50 | '--overwrite', 51 | action='store_true', 52 | help="Overwrite existing Osmosis home and binary without prompt", 53 | dest="overwrite" 54 | ) 55 | 56 | parser.add_argument( 57 | '-n', 58 | '--network', 59 | type=str, 60 | choices=NETWORK_CHOICES, 61 | help=f"Network to join: {NETWORK_CHOICES})", 62 | ) 63 | 64 | parser.add_argument( 65 | '-p', 66 | '--pruning', 67 | type=str, 68 | choices=PRUNING_CHOICES, 69 | help=f"Pruning settings: {PRUNING_CHOICES})", 70 | ) 71 | 72 | parser.add_argument( 73 | '-i', 74 | '--install', 75 | type=str, 76 | choices=INSTALL_CHOICES, 77 | help=f"Which installation to do: {INSTALL_CHOICES})", 78 | ) 79 | 80 | parser.add_argument( 81 | "--binary_path", 82 | type=str, 83 | help=f"Path where to download the binary", 84 | default="/usr/local/bin" 85 | ) 86 | 87 | parser.add_argument( 88 | '-c', 89 | '--cosmovisor', 90 | action='store_true', 91 | help="Install cosmovisor" 92 | ) 93 | 94 | parser.add_argument( 95 | '-s', 96 | '--service', 97 | action='store_true', 98 | help="Setup systemd service (Linux only)" 99 | ) 100 | 101 | args = parser.parse_args() 102 | 103 | # Choices 104 | class InstallChoice(str, Enum): 105 | NODE = "1" 106 | CLIENT = "2" 107 | LOCALOSMOSIS = "3" 108 | 109 | class NetworkChoice(str, Enum): 110 | MAINNET = "1" 111 | TESTNET = "2" 112 | 113 | class PruningChoice(str, Enum): 114 | DEFAULT = "1" 115 | NOTHING = "2" 116 | EVERYTHING = "3" 117 | 118 | class Answer(str, Enum): 119 | YES = "1" 120 | NO = "2" 121 | 122 | # Network configurations 123 | class Network: 124 | def __init__(self, chain_id, version, genesis_url, binary_url, peers, rpc_node, addrbook_url, snapshot_url): 125 | self.chain_id = chain_id 126 | self.version = version 127 | self.genesis_url = genesis_url 128 | self.binary_url = binary_url 129 | self.peers = peers 130 | self.rpc_node = rpc_node 131 | self.addrbook_url = addrbook_url 132 | self.snapshot_url = snapshot_url 133 | 134 | TESTNET = Network( 135 | chain_id = "osmo-test-5", 136 | version = f"v${TESTNET_VERSION}", 137 | genesis_url = "https://osmosis.fra1.digitaloceanspaces.com/osmo-test-5/genesis.json", 138 | binary_url = { 139 | "linux": { 140 | "amd64": f"https://osmosis.fra1.digitaloceanspaces.com/binaries/v{TESTNET_VERSION}/osmosisd-{TESTNET_VERSION}-linux-amd64", 141 | "arm64": f"https://osmosis.fra1.digitaloceanspaces.com/binaries/v{TESTNET_VERSION}/osmosisd-{TESTNET_VERSION}-linux-arm64" 142 | }, 143 | "darwin": { 144 | "amd64": f"https://osmosis.fra1.digitaloceanspaces.com/binaries/v{TESTNET_VERSION}/osmosisd-{TESTNET_VERSION}-darwin-amd64", 145 | "arm64": f"https://osmosis.fra1.digitaloceanspaces.com/binaries/v{TESTNET_VERSION}/osmosisd-{TESTNET_VERSION}-darwin-arm64" 146 | }, 147 | }, 148 | peers = [ 149 | "a5f81c035ff4f985d5e7c940c7c3b846389b7374@167.235.115.14:26656", 150 | "05c41cc1fc7c8cb379e54d784bcd3b3907a1568e@157.245.26.231:26656", 151 | "7c2b9e76be5c2142c76b429d9c29e902599ceb44@157.245.21.183:26656", 152 | "f440c4980357d8b56db87ddd50f06bd551f1319a@5.78.98.19:26656", 153 | "ade4d8bc,8cbe014af6ebdf3cb7b1e9ad36f412c0@testnet-seeds.polkachu.com:12556", 154 | ], 155 | rpc_node = "https://rpc.testnet.osmosis.zone:443", 156 | addrbook_url = "https://rpc.testnet.osmosis.zone/addrbook", 157 | snapshot_url = "https://snapshots.testnet.osmosis.zone/latest" 158 | ) 159 | 160 | MAINNET = Network( 161 | chain_id = "osmosis-1", 162 | version = f"v{MAINNET_VERSION}", 163 | genesis_url = "https://osmosis.fra1.digitaloceanspaces.com/osmosis-1/genesis.json", 164 | binary_url = { 165 | "linux": { 166 | "amd64": f"https://osmosis.fra1.digitaloceanspaces.com/binaries/v{MAINNET_VERSION}/osmosisd-{MAINNET_VERSION}-linux-amd64", 167 | "arm64": f"https://osmosis.fra1.digitaloceanspaces.com/binaries/v{MAINNET_VERSION}/osmosisd-{MAINNET_VERSION}-linux-arm64" 168 | }, 169 | "darwin": { 170 | "amd64": f"https://osmosis.fra1.digitaloceanspaces.com/binaries/v{MAINNET_VERSION}/osmosisd-{MAINNET_VERSION}-darwin-amd64", 171 | "arm64": f"https://osmosis.fra1.digitaloceanspaces.com/binaries/v{MAINNET_VERSION}/osmosisd-{MAINNET_VERSION}-darwin-arm64" 172 | }, 173 | }, 174 | peers = None, 175 | rpc_node = "https://rpc.osmosis.zone:443", 176 | addrbook_url = "https://rpc.osmosis.zone/addrbook", 177 | snapshot_url = "https://snapshots.osmosis.zone/latest" 178 | ) 179 | 180 | COSMOVISOR_URL = { 181 | "darwin": { 182 | "amd64": "https://osmosis.fra1.digitaloceanspaces.com/binaries/cosmovisor/cosmovisor-v1.2.0-darwin-amd64", 183 | "arm64": "https://osmosis.fra1.digitaloceanspaces.com/binaries/cosmovisor/cosmovisor-v1.2.0-darwin-arm64" 184 | }, 185 | "linux": { 186 | "amd64": "https://osmosis.fra1.digitaloceanspaces.com/binaries/cosmovisor/cosmovisor-v1.2.0-linux-amd64", 187 | "arm64": "https://osmosis.fra1.digitaloceanspaces.com/binaries/cosmovisor/cosmovisor-v1.2.0-linux-arm64" 188 | } 189 | } 190 | # Terminal utils 191 | 192 | class bcolors: 193 | OKGREEN = '\033[92m' 194 | RED = '\033[91m' 195 | ENDC = '\033[0m' 196 | PURPLE = '\033[95m' 197 | 198 | def clear_screen(): 199 | os.system('clear') 200 | 201 | # Messages 202 | 203 | def welcome_message(): 204 | print(bcolors.OKGREEN + """ 205 | ██████╗ ███████╗███╗ ███╗ ██████╗ ███████╗██╗███████╗ 206 | ██╔═══██╗██╔════╝████╗ ████║██╔═══██╗██╔════╝██║██╔════╝ 207 | ██║ ██║███████╗██╔████╔██║██║ ██║███████╗██║███████╗ 208 | ██║ ██║╚════██║██║╚██╔╝██║██║ ██║╚════██║██║╚════██║ 209 | ╚██████╔╝███████║██║ ╚═╝ ██║╚██████╔╝███████║██║███████║ 210 | ╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝╚══════╝ 211 | 212 | Welcome to the Osmosis node installer! 213 | 214 | 215 | For more information, please visit https://docs.osmosis.zone 216 | 217 | If you have an old Osmosis installation, 218 | - backup any important data before proceeding 219 | - ensure that no osmosis services are running in the background 220 | """ + bcolors.ENDC) 221 | 222 | 223 | def client_complete_message(osmosis_home): 224 | print(bcolors.OKGREEN + """ 225 | ✨ Congratulations! You have successfully completed setting up an Osmosis client! ✨ 226 | """ + bcolors.ENDC) 227 | 228 | print("🧪 Try running: " + bcolors.OKGREEN + f"osmosisd status --home {osmosis_home}" + bcolors.ENDC) 229 | print() 230 | 231 | 232 | def node_complete_message(using_cosmovisor, using_service, osmosis_home): 233 | print(bcolors.OKGREEN + """ 234 | ✨ Congratulations! You have successfully completed setting up an Osmosis node! ✨ 235 | """ + bcolors.ENDC) 236 | 237 | if using_service: 238 | 239 | if using_cosmovisor: 240 | print("🧪 To start the cosmovisor service run: ") 241 | print(bcolors.OKGREEN + f"sudo systemctl start cosmovisor" + bcolors.ENDC) 242 | else: 243 | print("🧪 To start the osmosisd service run: ") 244 | print(bcolors.OKGREEN + f"sudo systemctl start osmosisd" + bcolors.ENDC) 245 | 246 | else: 247 | if using_cosmovisor: 248 | print("🧪 To start cosmovisor run: ") 249 | print(bcolors.OKGREEN + f"DAEMON_NAME=osmosisd DAEMON_HOME={osmosis_home} cosmovisor run start" + bcolors.ENDC) 250 | else: 251 | print("🧪 To start osmosisd run: ") 252 | print(bcolors.OKGREEN + f"osmosisd start --home {osmosis_home}" + bcolors.ENDC) 253 | 254 | 255 | 256 | print() 257 | 258 | # Options 259 | 260 | def select_install(): 261 | 262 | # Check if setup is specified in args 263 | if args.install: 264 | if args.install == "node": 265 | choice = InstallChoice.NODE 266 | elif args.install == "client": 267 | choice = InstallChoice.CLIENT 268 | elif args.install == "localosmosis": 269 | choice = InstallChoice.LOCALOSMOSIS 270 | else: 271 | print(bcolors.RED + f"Invalid setup {args.install}. Please choose a valid setup.\n" + bcolors.ENDC) 272 | sys.exit(1) 273 | 274 | else: 275 | 276 | print(bcolors.OKGREEN + """ 277 | Please choose the desired installation: 278 | 279 | 1) node - run an osmosis node and join mainnet or testnet 280 | 2) client - setup osmosisd to query a public node 281 | 3) localosmosis - setup a local osmosis development node 282 | 283 | 💡 You can select the installation using the --install flag. 284 | """ + bcolors.ENDC) 285 | 286 | while True: 287 | choice = input("Enter your choice, or 'exit' to quit: ").strip() 288 | 289 | if choice.lower() == "exit": 290 | print("Exiting the program...") 291 | sys.exit(0) 292 | 293 | if choice not in [InstallChoice.NODE, InstallChoice.CLIENT, InstallChoice.LOCALOSMOSIS]: 294 | print("Invalid input. Please choose a valid option.") 295 | else: 296 | break 297 | 298 | if args.verbose: 299 | clear_screen() 300 | print(f"Chosen install: {INSTALL_CHOICES[int(choice) - 1]}") 301 | 302 | clear_screen() 303 | return choice 304 | 305 | 306 | def select_network(): 307 | """ 308 | Selects a network based on user input or command-line arguments. 309 | 310 | Returns: 311 | chosen_network (NetworkChoice): The chosen network, either MAINNET or TESTNET. 312 | 313 | Raises: 314 | SystemExit: If an invalid network is specified or the user chooses to exit the program. 315 | """ 316 | 317 | # Check if network is specified in args 318 | if args.network: 319 | if args.network == MAINNET.chain_id: 320 | choice = NetworkChoice.MAINNET 321 | elif args.network == TESTNET.chain_id: 322 | choice = NetworkChoice.TESTNET 323 | else: 324 | print(bcolors.RED + f"Invalid network {args.network}. Please choose a valid network." + bcolors.ENDC) 325 | sys.exit(1) 326 | 327 | # If not, ask the user to choose a network 328 | else: 329 | print(bcolors.OKGREEN + f""" 330 | Please choose the desired network: 331 | 332 | 1) Mainnet ({MAINNET.chain_id}) 333 | 2) Testnet ({TESTNET.chain_id}) 334 | 335 | 💡 You can select the network using the --network flag. 336 | """ + bcolors.ENDC) 337 | 338 | while True: 339 | choice = input("Enter your choice, or 'exit' to quit: ").strip() 340 | 341 | if choice.lower() == "exit": 342 | print("Exiting the program...") 343 | sys.exit(0) 344 | 345 | if choice not in [NetworkChoice.MAINNET, NetworkChoice.TESTNET]: 346 | print(bcolors.RED + "Invalid input. Please choose a valid option. Accepted values: [ 1 , 2 ] \n" + bcolors.ENDC) 347 | else: 348 | break 349 | 350 | if args.verbose: 351 | clear_screen() 352 | print(f"Chosen network: {NETWORK_CHOICES[int(choice) - 1]}") 353 | 354 | clear_screen() 355 | return choice 356 | 357 | 358 | def select_osmosis_home(): 359 | """ 360 | Selects the path for running the 'osmosisd init --home ' command. 361 | 362 | Returns: 363 | osmosis_home (str): The selected path. 364 | 365 | """ 366 | if args.home: 367 | osmosis_home = args.home 368 | else: 369 | default_home = os.path.expanduser("~/.osmosisd") 370 | print(bcolors.OKGREEN + f""" 371 | Do you want to install Osmosis in the default location?: 372 | 373 | 1) Yes, use default location {DEFAULT_OSMOSIS_HOME} (recommended) 374 | 2) No, specify custom location 375 | 376 | 💡 You can specify the home using the --home flag. 377 | """ + bcolors.ENDC) 378 | 379 | while True: 380 | choice = input("Enter your choice, or 'exit' to quit: ").strip() 381 | 382 | if choice.lower() == "exit": 383 | print("Exiting the program...") 384 | sys.exit(0) 385 | 386 | if choice == Answer.YES: 387 | osmosis_home = default_home 388 | break 389 | 390 | elif choice == Answer.NO: 391 | while True: 392 | custom_home = input("Enter the path for Osmosis home: ").strip() 393 | if custom_home != "": 394 | osmosis_home = custom_home 395 | break 396 | else: 397 | print("Invalid path. Please enter a valid directory.") 398 | break 399 | else: 400 | print("Invalid choice. Please enter 1 or 2.") 401 | 402 | clear_screen() 403 | return osmosis_home 404 | 405 | 406 | def select_moniker(): 407 | """ 408 | Selects the moniker for the Osmosis node. 409 | 410 | Returns: 411 | moniker (str): The selected moniker. 412 | 413 | """ 414 | if args.moniker: 415 | moniker = args.moniker 416 | else: 417 | print(bcolors.OKGREEN + f""" 418 | Do you want to use the default moniker? 419 | 420 | 1) Yes, use default moniker ({DEFAULT_MONIKER}) 421 | 2) No, specify custom moniker 422 | 423 | 💡 You can specify the moniker using the --moniker flag. 424 | """ + bcolors.ENDC) 425 | 426 | while True: 427 | choice = input("Enter your choice, or 'exit' to quit: ").strip() 428 | 429 | if choice.lower() == "exit": 430 | print("Exiting the program...") 431 | sys.exit(0) 432 | 433 | if choice == Answer.YES: 434 | moniker = DEFAULT_MONIKER 435 | break 436 | elif choice == Answer.NO: 437 | while True: 438 | custom_moniker = input("Enter the custom moniker: ") 439 | if custom_moniker.strip() != "": 440 | moniker = custom_moniker 441 | break 442 | else: 443 | print("Invalid moniker. Please enter a valid moniker.") 444 | break 445 | else: 446 | print("Invalid choice. Please enter 1 or 2.") 447 | 448 | clear_screen() 449 | return moniker 450 | 451 | 452 | def initialize_osmosis_home(osmosis_home, moniker): 453 | """ 454 | Initializes the Osmosis home directory with the specified moniker. 455 | 456 | Args: 457 | osmosis_home (str): The chosen home directory. 458 | moniker (str): The moniker for the Osmosis node. 459 | 460 | """ 461 | if not args.overwrite: 462 | 463 | while True: 464 | print(bcolors.OKGREEN + f""" 465 | Do you want to initialize the Osmosis home directory at '{osmosis_home}'? 466 | """ + bcolors.ENDC, end="") 467 | 468 | print(bcolors.RED + f""" 469 | ⚠️ All contents of the directory will be deleted. 470 | """ + bcolors.ENDC, end="") 471 | 472 | print(bcolors.OKGREEN + f""" 473 | 1) Yes, proceed with initialization 474 | 2) No, quit 475 | 476 | 💡 You can overwrite the osmosis home using --overwrite flag. 477 | """ + bcolors.ENDC) 478 | 479 | choice = input("Enter your choice, or 'exit' to quit: ").strip() 480 | 481 | if choice.lower() == "exit": 482 | print("Exiting the program...") 483 | sys.exit(0) 484 | 485 | if choice == Answer.YES: 486 | break 487 | 488 | elif choice == Answer.NO: 489 | sys.exit(0) 490 | 491 | else: 492 | print("Invalid choice. Please enter 1 or 2.") 493 | 494 | print(f"Initializing Osmosis home directory at '{osmosis_home}'...") 495 | try: 496 | subprocess.run( 497 | ["rm", "-rf", osmosis_home], 498 | stderr=subprocess.DEVNULL, check=True) 499 | 500 | subprocess.run( 501 | ["osmosisd", "init", moniker, "-o", "--home", osmosis_home], 502 | stderr=subprocess.DEVNULL, check=True) 503 | 504 | print("Initialization completed successfully.") 505 | 506 | except subprocess.CalledProcessError as e: 507 | print("Initialization failed.") 508 | print("Please check if the home directory is valid and has write permissions.") 509 | print(e) 510 | sys.exit(1) 511 | 512 | clear_screen() 513 | 514 | 515 | def select_pruning(osmosis_home): 516 | """ 517 | Allows the user to choose pruning settings and performs actions based on the selected option. 518 | 519 | """ 520 | 521 | # Check if pruning settings are specified in args 522 | if args.pruning: 523 | if args.pruning == "default": 524 | choice = PruningChoice.DEFAULT 525 | elif args.pruning == "nothing": 526 | choice = PruningChoice.NOTHING 527 | elif args.pruning == "everything": 528 | choice = PruningChoice.EVERYTHING 529 | else: 530 | print(bcolors.RED + f"Invalid pruning setting {args.pruning}. Please choose a valid setting.\n" + bcolors.ENDC) 531 | sys.exit(1) 532 | 533 | else: 534 | 535 | print(bcolors.OKGREEN + """ 536 | Please choose your desired pruning settings: 537 | 538 | 1) Default: (keep last 100,000 states to query the last week worth of data and prune at 100 block intervals) 539 | 2) Nothing: (keep everything, select this if running an archive node) 540 | 3) Everything: (keep last 10,000 states and prune at a random prime block interval) 541 | 542 | 💡 You can select the pruning settings using the --pruning flag. 543 | """ + bcolors.ENDC) 544 | 545 | while True: 546 | choice = input("Enter your choice, or 'exit' to quit: ").strip() 547 | 548 | if choice.lower() == "exit": 549 | print("Exiting the program...") 550 | sys.exit(0) 551 | 552 | if choice not in [PruningChoice.DEFAULT, PruningChoice.NOTHING, PruningChoice.EVERYTHING]: 553 | print("Invalid input. Please choose a valid option.") 554 | else: 555 | break 556 | 557 | if args.verbose: 558 | clear_screen() 559 | print(f"Chosen setting: {PRUNING_CHOICES[int(choice) - 1]}") 560 | 561 | app_toml = os.path.join(osmosis_home, "config", "app.toml") 562 | 563 | if choice == PruningChoice.DEFAULT: 564 | # Nothing to do 565 | pass 566 | 567 | elif choice == PruningChoice.NOTHING: 568 | subprocess.run(["sed -i -E 's/pruning = \"default\"/pruning = \"nothing\"/g' " + app_toml], shell=True) 569 | 570 | elif choice == PruningChoice.EVERYTHING: 571 | primeNum = random.choice([x for x in range(11, 97) if not [t for t in range(2, x) if not x % t]]) 572 | subprocess.run(["sed -i -E 's/pruning = \"default\"/pruning = \"custom\"/g' " + app_toml], shell=True) 573 | subprocess.run(["sed -i -E 's/pruning-keep-recent = \"0\"/pruning-keep-recent = \"10000\"/g' " + app_toml], shell=True) 574 | subprocess.run(["sed -i -E 's/pruning-interval = \"0\"/pruning-interval = \"" + str(primeNum) + "\"/g' " + app_toml], shell=True) 575 | 576 | else: 577 | print(bcolors.RED + f"Invalid pruning setting {choice}. Please choose a valid setting.\n" + bcolors.ENDC) 578 | sys.exit(1) 579 | 580 | clear_screen() 581 | 582 | 583 | def customize_config(home, network): 584 | """ 585 | Customizes the TOML configurations based on the network. 586 | 587 | Args: 588 | home (str): The home directory. 589 | network (str): The network identifier. 590 | 591 | """ 592 | 593 | # osmo-test-5 configuration 594 | if network == NetworkChoice.TESTNET: 595 | 596 | # patch client.toml 597 | client_toml = os.path.join(home, "config", "client.toml") 598 | 599 | with open(client_toml, "r") as config_file: 600 | lines = config_file.readlines() 601 | 602 | for i, line in enumerate(lines): 603 | if line.startswith("chain-id"): 604 | lines[i] = f'chain-id = "{TESTNET.chain_id}"\n' 605 | elif line.startswith("node"): 606 | lines[i] = f'node = "{TESTNET.rpc_node}"\n' 607 | 608 | with open(client_toml, "w") as config_file: 609 | config_file.writelines(lines) 610 | 611 | # patch config.toml 612 | config_toml = os.path.join(home, "config", "config.toml") 613 | 614 | peers = ','.join(TESTNET.peers) 615 | subprocess.run(["sed -i -E 's/persistent_peers = \"\"/persistent_peers = \"" + peers + "\"/g' " + config_toml], shell=True) 616 | 617 | # osmosis-1 configuration 618 | elif network == NetworkChoice.MAINNET: 619 | client_toml = os.path.join(home, "config", "client.toml") 620 | 621 | with open(client_toml, "r") as config_file: 622 | lines = config_file.readlines() 623 | 624 | for i, line in enumerate(lines): 625 | if line.startswith("chain-id"): 626 | lines[i] = f'chain-id = "{MAINNET.chain_id}"\n' 627 | elif line.startswith("node"): 628 | lines[i] = f'node = "{MAINNET.rpc_node}"\n' 629 | 630 | with open(client_toml, "w") as config_file: 631 | config_file.writelines(lines) 632 | 633 | else: 634 | print(bcolors.RED + f"Invalid network {network}. Please choose a valid setting.\n" + bcolors.ENDC) 635 | sys.exit(1) 636 | 637 | clear_screen() 638 | 639 | 640 | def download_binary(network): 641 | """ 642 | Downloads the binary for the specified network based on the operating system and architecture. 643 | 644 | Args: 645 | network (NetworkChoice): The network type, either MAINNET or TESTNET. 646 | 647 | Raises: 648 | SystemExit: If the binary download URL is not available for the current operating system and architecture. 649 | """ 650 | binary_path = os.path.join(args.binary_path, "osmosisd") 651 | 652 | if not args.overwrite: 653 | # Check if osmosisd is already installed 654 | try: 655 | subprocess.run([binary_path, "version"], check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 656 | print("osmosisd is already installed at " + bcolors.OKGREEN + f"{binary_path}" + bcolors.ENDC) 657 | while True: 658 | choice = input("Do you want to skip the download or overwrite the binary? (skip/overwrite): ").strip().lower() 659 | if choice == "skip": 660 | print("Skipping download.") 661 | return 662 | elif choice == "overwrite": 663 | print("Proceeding with overwrite.") 664 | break 665 | else: 666 | print("Invalid input. Please enter 'skip' or 'overwrite'.") 667 | except FileNotFoundError: 668 | print("osmosisd is not installed. Proceeding with download.") 669 | 670 | operating_system = platform.system().lower() 671 | architecture = platform.machine() 672 | 673 | if architecture == "x86_64": 674 | architecture = "amd64" 675 | elif architecture == "aarch64": 676 | architecture = "arm64" 677 | 678 | if architecture not in ["arm64", "amd64"]: 679 | print(f"Unsupported architecture {architecture}.") 680 | sys.exit(1) 681 | 682 | if network == NetworkChoice.TESTNET: 683 | binary_urls = TESTNET.binary_url 684 | else: 685 | binary_urls = MAINNET.binary_url 686 | 687 | if operating_system in binary_urls and architecture in binary_urls[operating_system]: 688 | binary_url = binary_urls[operating_system][architecture] 689 | else: 690 | print(f"Binary download URL not available for {operating_system}/{architecture}") 691 | sys.exit(0) 692 | 693 | try: 694 | print("Downloading " + bcolors.PURPLE + "osmosisd" + bcolors.ENDC, end="\n\n") 695 | print("from " + bcolors.OKGREEN + f"{binary_url}" + bcolors.ENDC, end=" ") 696 | print("to " + bcolors.OKGREEN + f"{binary_path}" + bcolors.ENDC) 697 | print() 698 | print(bcolors.OKGREEN + "💡 You can change the path using --binary_path" + bcolors.ENDC) 699 | 700 | subprocess.run(["wget", binary_url, "-q", "-O", "/tmp/osmosisd"], check=True) 701 | os.chmod("/tmp/osmosisd", 0o755) 702 | 703 | if platform.system() == "Linux": 704 | subprocess.run(["sudo", "mv", "/tmp/osmosisd", binary_path], check=True) 705 | subprocess.run(["sudo", "chown", f"{os.environ['USER']}:{os.environ['USER']}", binary_path], check=True) 706 | subprocess.run(["sudo", "chmod", "+x", binary_path], check=True) 707 | else: 708 | subprocess.run(["mv", "/tmp/osmosisd", binary_path], check=True) 709 | 710 | # Test binary 711 | subprocess.run(["osmosisd", "version"], check=True) 712 | 713 | print("Binary downloaded successfully.") 714 | 715 | except subprocess.CalledProcessError as e: 716 | print(e) 717 | print("Failed to download the binary.") 718 | sys.exit(1) 719 | 720 | clear_screen() 721 | 722 | def download_genesis(network, osmosis_home): 723 | """ 724 | Downloads the genesis file for the specified network. 725 | 726 | Args: 727 | network (NetworkChoice): The network type, either MAINNET or TESTNET. 728 | osmosis_home (str): The path to the Osmosis home directory. 729 | 730 | Raises: 731 | SystemExit: If the genesis download URL is not available for the current network. 732 | 733 | """ 734 | if network == NetworkChoice.TESTNET: 735 | genesis_url = TESTNET.genesis_url 736 | else: 737 | genesis_url = MAINNET.genesis_url 738 | 739 | if genesis_url: 740 | try: 741 | print("Downloading " + bcolors.PURPLE + "genesis.json" + bcolors.ENDC + f" from {genesis_url}") 742 | genesis_path = os.path.join(osmosis_home, "config", "genesis.json") 743 | 744 | subprocess.run(["wget", genesis_url, "-q", "-O", genesis_path], check=True) 745 | print("Genesis downloaded successfully.\n") 746 | 747 | except subprocess.CalledProcessError: 748 | print("Failed to download the genesis.") 749 | sys.exit(1) 750 | 751 | 752 | def download_addrbook(network, osmosis_home): 753 | """ 754 | Downloads the addrbook for the specified network. 755 | 756 | Args: 757 | network (NetworkChoice): The network type, either MAINNET or TESTNET. 758 | osmosis_home (str): The path to the Osmosis home directory. 759 | 760 | Raises: 761 | SystemExit: If the genesis download URL is not available for the current network. 762 | 763 | """ 764 | if network == NetworkChoice.TESTNET: 765 | addrbook_url = TESTNET.addrbook_url 766 | else: 767 | addrbook_url = MAINNET.addrbook_url 768 | 769 | if addrbook_url: 770 | try: 771 | print("Downloading " + bcolors.PURPLE + "addrbook.json" + bcolors.ENDC + f" from {addrbook_url}") 772 | addrbook_path = os.path.join(osmosis_home, "config", "addrbook.json") 773 | 774 | subprocess.run(["wget", addrbook_url, "-q", "-O", addrbook_path], check=True) 775 | print("Addrbook downloaded successfully.") 776 | 777 | except subprocess.CalledProcessError: 778 | print("Failed to download the addrbook.") 779 | sys.exit(1) 780 | 781 | clear_screen() 782 | 783 | 784 | def download_snapshot(network, osmosis_home): 785 | """ 786 | Downloads the snapshot for the specified network. 787 | 788 | Args: 789 | network (NetworkChoice): The network type, either MAINNET or TESTNET. 790 | osmosis_home (str): The path to the Osmosis home directory. 791 | 792 | Raises: 793 | SystemExit: If the genesis download URL is not available for the current network. 794 | 795 | """ 796 | 797 | def install_snapshot_prerequisites(): 798 | """ 799 | Installs the prerequisites: Homebrew (brew) package manager and lz4 compression library. 800 | 801 | Args: 802 | osmosis_home (str): The path of the Osmosis home directory. 803 | 804 | """ 805 | while True: 806 | print(bcolors.OKGREEN + f""" 807 | To download the snapshot, we need the lz4 compression library. 808 | Do you want me to install it? 809 | 810 | 1) Yes, install lz4 811 | 2) No, continue without installing lz4 812 | """ + bcolors.ENDC) 813 | 814 | choice = input("Enter your choice, or 'exit' to quit: ").strip() 815 | 816 | if choice.lower() == "exit": 817 | print("Exiting the program...") 818 | sys.exit(0) 819 | 820 | if choice == Answer.YES: 821 | break 822 | 823 | elif choice == Answer.NO: 824 | clear_screen() 825 | return 826 | 827 | else: 828 | print("Invalid choice. Please enter 1 or 2.") 829 | 830 | operating_system = platform.system().lower() 831 | if operating_system == "linux": 832 | print("Installing lz4...") 833 | subprocess.run(["sudo apt-get install wget liblz4-tool aria2 -y"], 834 | stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, shell=True) 835 | else: 836 | print("Installing Homebrew...") 837 | subprocess.run(['bash', '-c', '$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)']) 838 | 839 | print("Installing lz4...") 840 | subprocess.run(['brew', 'install', 'lz4']) 841 | 842 | print("Installing aria2...") 843 | subprocess.run(['brew', 'install', 'aria2']) 844 | 845 | print("Installation completed successfully.") 846 | clear_screen() 847 | 848 | 849 | def parse_snapshot_info(network): 850 | """ 851 | Creates a dictionary containing the snapshot information for the specified network. 852 | It merges the snapshot information from the osmosis official snapshot JSON and 853 | quicksync from chianlayer https://dl2.quicksync.io/json/osmosis.json 854 | 855 | Returns: 856 | dict: Dictionary containing the parsed snapshot information. 857 | 858 | """ 859 | snapshot_info = [] 860 | 861 | if network == NetworkChoice.TESTNET: 862 | snapshot_url = TESTNET.snapshot_url 863 | chain_id = TESTNET.chain_id 864 | quicksync_prefix = "osmotestnet-5" 865 | elif network == NetworkChoice.MAINNET: 866 | snapshot_url = MAINNET.snapshot_url 867 | chain_id = MAINNET.chain_id 868 | quicksync_prefix = "osmosis-1" 869 | else: 870 | print(f"Invalid network choice - {network}") 871 | sys.exit(1) 872 | 873 | # Set SSL context 874 | context = ssl.create_default_context() 875 | context.check_hostname = False 876 | context.verify_mode = ssl.CERT_NONE 877 | 878 | req = urlrq.Request(snapshot_url, headers={'User-Agent': 'Mozilla/5.0'}) 879 | resp = urlrq.urlopen(req, context=context) 880 | latest_snapshot_url = resp.read().decode() 881 | 882 | snapshot_info.append({ 883 | "network": chain_id, 884 | "mirror": "Germany", 885 | "url": latest_snapshot_url.rstrip('\n'), 886 | "type": "pruned", 887 | "provider": "osmosis" 888 | }) 889 | 890 | # Parse quicksync snapshot json 891 | try: 892 | url = "https://dl2.quicksync.io/json/osmosis.json" 893 | resp = urlrq.urlopen(url, context=context) 894 | data = resp.read().decode() 895 | 896 | snapshots = json.loads(data) 897 | 898 | for snapshot in snapshots: 899 | 900 | if not snapshot["file"].startswith(quicksync_prefix): 901 | continue 902 | 903 | snapshot_info.append({ 904 | "network": chain_id, 905 | "mirror": snapshot["mirror"], 906 | "url": snapshot["url"], 907 | "type": snapshot["network"], 908 | "provider": "chainlayer" 909 | }) 910 | 911 | except (urlrq.URLError, json.JSONDecodeError) as e: 912 | print(f"Error: Failed to fetch or parse snapshot JSON - {e}") 913 | 914 | return snapshot_info 915 | 916 | 917 | def print_snapshot_download_info(snapshot_info): 918 | """ 919 | Prints the information about the snapshot download. 920 | """ 921 | 922 | print(bcolors.OKGREEN + f""" 923 | Choose one of the following snapshots: 924 | """ + bcolors.ENDC) 925 | 926 | # Prepare table headers 927 | column_widths = [1, 12, 12, 12] 928 | headers = ["#", "Provider", "Location", "Type"] 929 | header_row = " | ".join(f"{header:{width}}" for header, width in zip(headers, column_widths)) 930 | 931 | # Print table header 932 | print(header_row) 933 | print("-" * len(header_row)) 934 | 935 | # Print table content 936 | for idx, snapshot in enumerate(snapshot_info): 937 | 938 | row_data = [str(idx + 1), snapshot["provider"], snapshot["mirror"], snapshot["type"]] 939 | wrapped_data = [textwrap.fill(data, width=width) for data, width in zip(row_data, column_widths)] 940 | formatted_row = " | ".join(f"{data:{width}}" for data, width in zip(wrapped_data, column_widths)) 941 | print(formatted_row) 942 | 943 | print() 944 | 945 | install_snapshot_prerequisites() 946 | snapshots = parse_snapshot_info(network) 947 | 948 | while True: 949 | 950 | print_snapshot_download_info(snapshots) 951 | choice = input("Enter your choice, or 'exit' to quit: ").strip() 952 | 953 | if choice.lower() == "exit": 954 | print("Exiting the program...") 955 | sys.exit(0) 956 | 957 | if int(choice) < 0 or int(choice) > len(snapshots): 958 | clear_screen() 959 | print(bcolors.RED + "Invalid input. Please choose a valid option." + bcolors.ENDC) 960 | else: 961 | break 962 | 963 | snapshot_url = snapshots[int(choice) - 1]['url'] 964 | 965 | try: 966 | print(f"\n🔽 Downloading snapshots from {snapshot_url}") 967 | download_process = subprocess.Popen(["aria2c", snapshot_url], stdout=subprocess.PIPE) 968 | lz4_process = subprocess.Popen(["lz4", "-d"], stdin=download_process.stdout, stdout=subprocess.PIPE) 969 | tar_process = subprocess.Popen(["tar", "-C", osmosis_home, "-xf", "-"], stdin=lz4_process.stdout, stdout=subprocess.PIPE) 970 | 971 | tar_process.wait() 972 | print("Snapshot download and extraction completed successfully.") 973 | 974 | except subprocess.CalledProcessError as e: 975 | print("Failed to download the snapshot.") 976 | print(f"Error: {e}") 977 | sys.exit(1) 978 | 979 | clear_screen() 980 | 981 | 982 | def download_cosmovisor(osmosis_home): 983 | """ 984 | Downloads and installs cosmovisor. 985 | 986 | Returns: 987 | use_cosmovisor(bool): Whether to use cosmovisor or not. 988 | 989 | """ 990 | if not args.cosmovisor: 991 | print(bcolors.OKGREEN + f""" 992 | Do you want to install cosmovisor? 993 | 994 | 1) Yes, download and install cosmovisor (default) 995 | 2) No 996 | 997 | 💡 You can specify the cosmovisor setup using the --cosmovisor flag. 998 | """ + bcolors.ENDC) 999 | 1000 | while True: 1001 | choice = input("Enter your choice, or 'exit' to quit: ").strip() 1002 | 1003 | if choice.lower() == "exit": 1004 | print("Exiting the program...") 1005 | sys.exit(0) 1006 | 1007 | if choice == Answer.YES: 1008 | break 1009 | elif choice == Answer.NO: 1010 | print("Skipping cosmovisor installation.") 1011 | clear_screen() 1012 | return False 1013 | else: 1014 | print("Invalid choice. Please enter 1 or 2.") 1015 | 1016 | # Download and install cosmovisor 1017 | operating_system = platform.system().lower() 1018 | architecture = platform.machine() 1019 | 1020 | if architecture == "x86_64": 1021 | architecture = "amd64" 1022 | elif architecture == "aarch64": 1023 | architecture = "arm64" 1024 | 1025 | if architecture not in ["arm64", "amd64"]: 1026 | print(f"Unsupported architecture {architecture}.") 1027 | sys.exit(1) 1028 | 1029 | if operating_system in COSMOVISOR_URL and architecture in COSMOVISOR_URL[operating_system]: 1030 | binary_url = COSMOVISOR_URL[operating_system][architecture] 1031 | else: 1032 | print(f"Binary download URL not available for {os}/{architecture}") 1033 | sys.exit(0) 1034 | 1035 | try: 1036 | binary_path = os.path.join(args.binary_path, "cosmovisor") 1037 | 1038 | print("Downloading " + bcolors.PURPLE+ "cosmovisor" + bcolors.ENDC, end="\n\n") 1039 | print("from " + bcolors.OKGREEN + f"{binary_url}" + bcolors.ENDC, end=" ") 1040 | print("to " + bcolors.OKGREEN + f"{binary_path}" + bcolors.ENDC) 1041 | print() 1042 | print(bcolors.OKGREEN + "💡 You can change the path using --binary_path" + bcolors.ENDC) 1043 | 1044 | clear_screen() 1045 | temp_dir = tempfile.mkdtemp() 1046 | temp_binary_path = os.path.join(temp_dir, "cosmovisor") 1047 | 1048 | subprocess.run(["wget", binary_url,"-q", "-O", temp_binary_path], check=True) 1049 | os.chmod(temp_binary_path, 0o755) 1050 | 1051 | if platform.system() == "Linux": 1052 | subprocess.run(["sudo", "mv", temp_binary_path, binary_path], check=True) 1053 | subprocess.run(["sudo", "chown", f"{os.environ['USER']}:{os.environ['USER']}", binary_path], check=True) 1054 | subprocess.run(["sudo", "chmod", "+x", binary_path], check=True) 1055 | else: 1056 | subprocess.run(["mv", temp_binary_path, binary_path], check=True) 1057 | 1058 | # Test binary 1059 | subprocess.run(["cosmovisor", "help"], check=True) 1060 | 1061 | print("Binary downloaded successfully.") 1062 | 1063 | except subprocess.CalledProcessError: 1064 | print("Failed to download the binary.") 1065 | sys.exit(1) 1066 | 1067 | clear_screen() 1068 | 1069 | # Initialize cosmovisor 1070 | print("Setting up cosmovisor directory...") 1071 | 1072 | # Set environment variables 1073 | env = { 1074 | "DAEMON_NAME": "osmosisd", 1075 | "DAEMON_HOME": osmosis_home 1076 | } 1077 | 1078 | try: 1079 | subprocess.run(["/usr/local/bin/cosmovisor", "init", "/usr/local/bin/osmosisd"], check=True, env=env) 1080 | except subprocess.CalledProcessError: 1081 | print("Failed to initialize cosmovisor.") 1082 | sys.exit(1) 1083 | 1084 | clear_screen() 1085 | return True 1086 | 1087 | 1088 | def setup_cosmovisor_service(osmosis_home): 1089 | """ 1090 | Setup cosmovisor service on Linux. 1091 | """ 1092 | 1093 | operating_system = platform.system() 1094 | 1095 | if operating_system != "Linux": 1096 | return False 1097 | 1098 | if not args.service: 1099 | print(bcolors.OKGREEN + f""" 1100 | Do you want to setup cosmovisor as a background service? 1101 | 1102 | 1) Yes, setup cosmovisor as a service 1103 | 2) No 1104 | 1105 | 💡 You can specify the service setup using the --service flag. 1106 | """ + bcolors.ENDC) 1107 | 1108 | while True: 1109 | choice = input("Enter your choice, or 'exit' to quit: ").strip() 1110 | 1111 | if choice.lower() == "exit": 1112 | print("Exiting the program...") 1113 | sys.exit(0) 1114 | 1115 | if choice == Answer.YES: 1116 | break 1117 | elif choice == Answer.NO: 1118 | return 1119 | 1120 | user = os.environ.get("USER") 1121 | 1122 | unit_file_contents = f"""[Unit] 1123 | Description=Cosmovisor daemon 1124 | After=network-online.target 1125 | 1126 | [Service] 1127 | Environment="DAEMON_NAME=osmosisd" 1128 | Environment="DAEMON_HOME={osmosis_home}" 1129 | Environment="DAEMON_RESTART_AFTER_UPGRADE=true" 1130 | Environment="DAEMON_ALLOW_DOWNLOAD_BINARIES=false" 1131 | Environment="DAEMON_LOG_BUFFER_SIZE=512" 1132 | Environment="UNSAFE_SKIP_BACKUP=true" 1133 | User={user} 1134 | ExecStart=/usr/local/bin/cosmovisor run start --home {osmosis_home} 1135 | Restart=always 1136 | RestartSec=3 1137 | LimitNOFILE=infinity 1138 | LimitNPROC=infinity 1139 | 1140 | [Install] 1141 | WantedBy=multi-user.target 1142 | """ 1143 | 1144 | unit_file_path = "/lib/systemd/system/cosmovisor.service" 1145 | 1146 | with open("cosmovisor.service", "w") as f: 1147 | f.write(unit_file_contents) 1148 | 1149 | subprocess.run(["sudo", "mv", "cosmovisor.service", unit_file_path]) 1150 | subprocess.run(["sudo", "systemctl", "daemon-reload"]) 1151 | subprocess.run(["systemctl", "restart", "systemd-journald"]) 1152 | 1153 | clear_screen() 1154 | return True 1155 | 1156 | 1157 | def setup_osmosisd_service(osmosis_home): 1158 | """ 1159 | Setup osmosisd service on Linux. 1160 | """ 1161 | 1162 | operating_system = platform.system() 1163 | 1164 | if operating_system != "Linux": 1165 | return False 1166 | 1167 | if not args.service: 1168 | print(bcolors.OKGREEN + """ 1169 | Do you want to set up osmosisd as a background service? 1170 | 1171 | 1) Yes, set up osmosisd as a service 1172 | 2) No 1173 | 1174 | 💡 You can specify the service setup using the --service flag. 1175 | """ + bcolors.ENDC) 1176 | 1177 | while True: 1178 | choice = input("Enter your choice, or 'exit' to quit: ").strip() 1179 | 1180 | if choice.lower() == "exit": 1181 | print("Exiting the program...") 1182 | sys.exit(0) 1183 | 1184 | if choice == Answer.YES: 1185 | break 1186 | elif choice == Answer.NO: 1187 | return 1188 | 1189 | user = os.environ.get("USER") 1190 | 1191 | unit_file_contents = f"""[Unit] 1192 | Description=Osmosis Daemon 1193 | After=network-online.target 1194 | 1195 | [Service] 1196 | User={user} 1197 | ExecStart=/usr/local/bin/osmosisd start --home {osmosis_home} 1198 | Restart=always 1199 | RestartSec=3 1200 | LimitNOFILE=infinity 1201 | LimitNPROC=infinity 1202 | 1203 | [Install] 1204 | WantedBy=multi-user.target 1205 | """ 1206 | 1207 | unit_file_path = "/lib/systemd/system/osmosisd.service" 1208 | 1209 | with open("osmosisd.service", "w") as f: 1210 | f.write(unit_file_contents) 1211 | 1212 | subprocess.run(["sudo", "mv", "osmosisd.service", unit_file_path]) 1213 | subprocess.run(["sudo", "systemctl", "daemon-reload"]) 1214 | subprocess.run(["systemctl", "restart", "systemd-journald"]) 1215 | 1216 | clear_screen() 1217 | return True 1218 | 1219 | 1220 | def main(): 1221 | 1222 | welcome_message() 1223 | 1224 | # Start the installation 1225 | chosen_install = select_install() 1226 | 1227 | if chosen_install == InstallChoice.NODE: 1228 | network = select_network() 1229 | download_binary(network) 1230 | osmosis_home = select_osmosis_home() 1231 | moniker = select_moniker() 1232 | initialize_osmosis_home(osmosis_home, moniker) 1233 | using_cosmovisor = download_cosmovisor(osmosis_home) 1234 | download_genesis(network, osmosis_home) 1235 | download_addrbook(network, osmosis_home) 1236 | select_pruning(osmosis_home) 1237 | download_snapshot(network, osmosis_home) 1238 | if using_cosmovisor: 1239 | using_service = setup_cosmovisor_service(osmosis_home) 1240 | else: 1241 | using_service = setup_osmosisd_service(osmosis_home) 1242 | node_complete_message(using_cosmovisor, using_service, osmosis_home) 1243 | 1244 | elif chosen_install == InstallChoice.CLIENT: 1245 | network = select_network() 1246 | download_binary(network) 1247 | osmosis_home = select_osmosis_home() 1248 | moniker = select_moniker() 1249 | initialize_osmosis_home(osmosis_home, moniker) 1250 | customize_config(osmosis_home, network) 1251 | client_complete_message(osmosis_home) 1252 | 1253 | elif chosen_install == InstallChoice.LOCALOSMOSIS: 1254 | print("Setting up a LocalOsmosis node not yet supported.") 1255 | sys.exit(1) 1256 | 1257 | main() 1258 | --------------------------------------------------------------------------------