Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Overhauling Scripts #1918

Merged
merged 11 commits into from
Dec 10, 2024
220 changes: 175 additions & 45 deletions scripts/run-node.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,28 @@
import requests
import json
import zipfile
import hashlib
import math
from io import BytesIO

# Set up logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# Advanced user configs
moniker = "pynode" # Custom moniker for the node
trust_height_delta = 20000 # Negative height offset for state sync
enable_unsafe_reset = True # Wipe database and keys before setup
moniker = "seinode" # Custom moniker for the node
trust_height_delta = 0 # Negative height offset for state sync
alexander-sei marked this conversation as resolved.
Show resolved Hide resolved
version_override = False # Override version fetching. if true, specify version(s) below
p2p_port = 26656
alexander-sei marked this conversation as resolved.
Show resolved Hide resolved
rpc_port = 26657
lcd_port = 1317
grpc_port = 9090
grpc_web_port = 9091
pprof_port = 6060

# Chain binary version ["version_override" must be true to use]
MAINNET_VERSION = "v3.9.0"
DEVNET_VERSION = "v5.2.2"
TESTNET_VERSION = "v5.2.2"
MAINNET_VERSION = "v5.9.0-hotfix"
alexander-sei marked this conversation as resolved.
Show resolved Hide resolved
TESTNET_VERSION = "v5.9.0-hotfix"
DEVNET_VERSION = "v5.9.0-hotfix"

# Map env to chain ID and optional manual version override
ENV_TO_CONFIG = {
Expand Down Expand Up @@ -56,12 +63,7 @@ def print_ascii_and_intro():

Welcome to the Sei node installer!
For more information please visit docs.sei.io
Please make sure you have the following installed locally:
\t- golang 1.21 (with PATH and GOPATH set properly)
\t- make
\t- gcc
\t- docker
This tool will build from scratch seid and wipe away existing state.
This tool will download the seid binary from sei-binaries and wipe any existing state.
Please backup any important existing data before proceeding.
""")

Expand All @@ -81,7 +83,8 @@ def take_manual_inputs():
env = input("Choose an environment: ")

env = ["local", "devnet", "testnet", "mainnet"][int(env) - 1]
db_choice = input("Choose the database backend (1: legacy [default], 2: sei-db): ").strip() or "1"

db_choice = input("Choose the database backend (1: sei-db [default], 2: legacy): ").strip() or "1"
if db_choice not in ["1", "2"]:
db_choice = "1" # Default to "1" if the input is invalid or empty
return env, db_choice
Expand Down Expand Up @@ -131,35 +134,137 @@ def fetch_latest_version():
logging.error(f"Failed to fetch latest version from GitHub API: {e}")
sys.exit(1)

# Install release based on version tag.
def install_release(version):
# Computes the sha256 hash of a file
def compute_sha256(file_path):
sha256 = hashlib.sha256()
with open(file_path, "rb") as f:
for chunk in iter(lambda: f.read(4096), b""):
sha256.update(chunk)
return sha256.hexdigest()

# Compile and install release based on version tag
def compile_and_install_release(version):
logging.info(f"Starting compilation and installation for version: {version}")
try:
zip_url = f"https://github.com/sei-protocol/sei-chain/archive/refs/tags/{version}.zip"
response = requests.get(zip_url)
logging.info(f"Constructed zip URL: {zip_url}")

logging.info("Initiating download of the release zip file...")
response = requests.get(zip_url, timeout=30)
response.raise_for_status()
logging.info("Download completed successfully.")

logging.info("Extracting the zip file...")
zip_file = zipfile.ZipFile(BytesIO(response.content))
zip_file.extractall(".")
logging.info("Extraction completed.")

# Get the name of the extracted directory
extracted_dir = zip_file.namelist()[0].rstrip('/')
logging.info(f"Extracted directory: {extracted_dir}")

# Define the new directory name
new_dir_name = "sei-chain"

# Check if the new directory name already exists
if os.path.exists(new_dir_name):
logging.error(f"The directory '{new_dir_name}' already exists. It will be removed.")
sys.exit(1)

# Rename the extracted directory to 'sei-chain'
logging.info(f"Renaming '{extracted_dir}' to '{new_dir_name}'")
os.rename(extracted_dir, new_dir_name)
logging.info(f"Renaming completed.")

# Change directory to the new directory
os.chdir(new_dir_name)
logging.info(f"Changed working directory to '{new_dir_name}'")

logging.info("Starting the 'make install' process...")
result = subprocess.run(
["make", "install"],
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
logging.info("Make install completed successfully.")
logging.debug(f"Make install stdout: {result.stdout}")
logging.debug(f"Make install stderr: {result.stderr}")

os.chdir(zip_file.namelist()[0])
subprocess.run(["make", "install"], check=True)
logging.info(f"Successfully installed version: {version}")
logging.info(f"Output from make install:\n{result.stdout}")

except requests.exceptions.HTTPError as e:
logging.error(f"HTTP error occurred: {e}") # Handle http error
logging.error(f"HTTP error occurred: {e}")
sys.exit(1)
except requests.exceptions.RequestException as e:
logging.error(f"Error downloading files: {e}") # Handle other errors
logging.error(f"Error downloading files: {e}")
sys.exit(1)
except zipfile.BadZipFile:
logging.error("Error unzipping file. The downloaded file may be corrupt.")
sys.exit(1)
except subprocess.CalledProcessError as e:
# Display the output and error from the make command if it fails
logging.error(f"Installation failed during 'make install': {e}")
logging.error(f"Error output: {e.stderr}")
sys.exit(1)
except Exception as e:
logging.error(f"An unexpected error occurred: {e}")
sys.exit(1)

def install_release(version):
try:
base_url = f"https://github.com/alexander-sei/sei-binaries/releases/download/{version}/"
tag = f"seid-{version}-linux-amd64"
binary_url = f"{base_url}{tag}"
sha256_url = f"{base_url}{tag}.sha256"
install_dir = "/usr/local/bin"
binary_path = os.path.join(install_dir, "seid")

logging.info(f"Downloading binary from {binary_url}")
binary_response = requests.get(binary_url)
binary_response.raise_for_status()

logging.info(f"Downloading SHA256 checksum from {sha256_url}")
sha256_response = requests.get(sha256_url)
sha256_response.raise_for_status()

# Extract the SHA256 hash from the .sha256 file
sha256_hash = sha256_response.text.strip().split()[0]
logging.info(f"Expected SHA256 hash: {sha256_hash}")

# Save the binary to a temporary file
temp_binary_path = "/tmp/seid-binary"
with open(temp_binary_path, "wb") as binary_file:
binary_file.write(binary_response.content)

# Compute the SHA256 hash of the downloaded binary
logging.info(f"Computing SHA256 hash of the binary at {temp_binary_path}")
sha256_computed = compute_sha256(temp_binary_path)
logging.info(f"Computed SHA256 hash: {sha256_computed}")

# Compare the computed hash with the expected hash
if sha256_computed != sha256_hash:
raise ValueError("SHA256 hash mismatch! The binary may be corrupted or tampered with.")
else:
logging.info("SHA256 hash verification passed.")

# Move the binary to the install directory and rename it to 'seid'
logging.info(f"Moving binary to {binary_path}")
subprocess.run(["sudo", "mv", temp_binary_path, binary_path], check=True)

# Make the binary executable
logging.info(f"Setting executable permissions for {binary_path}")
subprocess.run(["sudo", "chmod", "+x", binary_path], check=True)

logging.info(f"Successfully installed 'seid' version: {version} to {binary_path}")

except requests.HTTPError as http_err:
logging.error(f"HTTP error occurred: {http_err}")
except Exception as err:
logging.error(f"An error occurred: {err}")

# Fetch version from RPC unless "version_override = true"
def fetch_node_version(rpc_url):
if not version_override:
Expand All @@ -177,13 +282,26 @@ def fetch_node_version(rpc_url):
return None

# Fetch state sync params
def get_state_sync_params(rpc_url):
def get_state_sync_params(rpc_url, trust_height_delta, chain_id):
response = requests.get(f"{rpc_url}/status")
latest_height = int(response.json()['sync_info']['latest_block_height'])

# Calculate sync block height
sync_block_height = latest_height - trust_height_delta if latest_height > trust_height_delta else latest_height
response = requests.get(f"{rpc_url}/block?height={sync_block_height}")

# Determine the rounding based on the chain_id
if chain_id.lower() == 'pacific-1':
# Round sync block height to the next 100,000 + add 2 for mainnet
rounded_sync_block_height = math.floor(sync_block_height / 100000) * 100000 + 2
else:
# Round sync block height to the next 2,000 + add 2 for devnet or testnet
rounded_sync_block_height = math.floor(sync_block_height / 2000) * 2000 + 2

# Fetch block hash
response = requests.get(f"{rpc_url}/block?height={rounded_sync_block_height}")
sync_block_hash = response.json()['block_id']['hash']
return sync_block_height, sync_block_hash

return rounded_sync_block_height, sync_block_hash

# Fetch peers list
def get_persistent_peers(rpc_url):
Expand Down Expand Up @@ -246,30 +364,31 @@ def main():
dynamic_version = fetch_node_version(rpc_url) if not version_override else None
version = dynamic_version or version # Use the fetched version if not overridden

# Install selected release
install_release(version)

# Unsafe-reset-all only if directory exists, and by config at top of script
home_dir = os.path.expanduser('~/.sei')
if enable_unsafe_reset and os.path.exists(home_dir):
try:
subprocess.run(["seid", "tendermint", "unsafe-reset-all"], check=True)
except subprocess.CalledProcessError as e:
logging.error(f"Failed to execute 'seid tendermint unsafe-reset-all': {e}")
sys.exit(1)

# Clean up previous data, init seid with given chain ID and moniker
subprocess.run(["rm", "-rf", home_dir])
subprocess.run(["seid", "init", moniker, "--chain-id", chain_id], check=True)

if env == "local":
# Clean up previous data, init seid with given chain ID and moniker
compile_and_install_release(version)

subprocess.run(["rm", "-rf", home_dir])
subprocess.run(["seid", "init", moniker, "--chain-id", chain_id], check=True)

logging.info("Running local initialization script...")
local_script_path = os.path.expanduser('~/sei-chain/scripts/initialize_local_chain.sh')
run_command(f"chmod +x {local_script_path}")
run_command(local_script_path)

else:
# Install selected release
install_release(version)

# Clean up previous data, init seid with given chain ID and moniker
subprocess.run(["rm", "-rf", home_dir])
subprocess.run(["seid", "init", moniker, "--chain-id", chain_id], check=True)
subprocess.run(["sudo", "mount", "-t", "tmpfs", "-o", "size=12G,mode=1777", "overflow", "/tmp"], check=True)

# Fetch state-sync params and persistent peers
sync_block_height, sync_block_hash = get_state_sync_params(rpc_url)
sync_block_height, sync_block_hash = get_state_sync_params(rpc_url, trust_height_delta, chain_id)
persistent_peers = get_persistent_peers(rpc_url)

# Fetch and write genesis
Expand All @@ -292,16 +411,27 @@ def main():
config_data = config_data.replace('persistent-peers = ""', f'persistent-peers = "{persistent_peers}"')
config_data = config_data.replace('enable = false', 'enable = true')
config_data = config_data.replace('db-sync-enable = true', 'db-sync-enable = false')
config_data = config_data.replace('use-p2p = false', 'use-p2p = true')
config_data = config_data.replace('fetchers = "4"', 'fetchers = "2"')
config_data = config_data.replace('send-rate = 20480000', 'send-rate = 20480000000000')
config_data = config_data.replace('recv-rate = 20480000', 'recv-rate = 20480000000000')
config_data = config_data.replace('chunk-request-timeout = "15s"', 'chunk-request-timeout = "10s"')
alexander-sei marked this conversation as resolved.
Show resolved Hide resolved
config_data = config_data.replace('laddr = "tcp://127.0.0.1:26657"', f'laddr = "tcp://127.0.0.1:{rpc_port}"')
config_data = config_data.replace('laddr = "tcp://0.0.0.0:26656"', f'laddr = "tcp://0.0.0.0:{p2p_port}"')
config_data = config_data.replace('pprof-laddr = "localhost:6060"', f'pprof-laddr = "localhost:{pprof_port}"')

with open(config_path, 'w') as file:
file.write(config_data)

# Read and modify app.toml
with open(app_config_path, 'r') as file:
app_data = file.read()
app_data = app_data.replace('address = "tcp://0.0.0.0:1317"', f'address = "tcp://0.0.0.0:{lcd_port}"')
app_data = app_data.replace('address = "0.0.0.0:9090"', f'address = "0.0.0.0:{grpc_port}"')
app_data = app_data.replace('address = "0.0.0.0:9091"', f'address = "0.0.0.0:{grpc_web_port}"')
if db_choice == "1":
app_data = app_data.replace('sc-enable = false', 'sc-enable = true')
app_data = app_data.replace('ss-enable = false', 'ss-enable = true')

# Read modify and write app.toml if sei-db is selected
if db_choice == "2":
with open(app_config_path, 'r') as file:
app_data = file.read()
app_data = app_data.replace('sc-enable = false', 'sc-enable = true')
app_data = app_data.replace('ss-enable = false', 'ss-enable = true')
with open(app_config_path, 'w') as file:
file.write(app_data)

Expand Down
35 changes: 29 additions & 6 deletions scripts/run-sha256sum.sh
Original file line number Diff line number Diff line change
@@ -1,15 +1,38 @@
#!/bin/bash

# Check if 'sha256sum' command is available on the system
if ! command -v sha256sum &> /dev/null
then
printf "sha256sum could not be found"
exit
# If 'sha256sum' is not found, print an error message
printf "sha256sum could not be found\n"
# Exit the script with a non-zero status to indicate failure
exit 1
fi

# Assign the first command-line argument to the variable SHA_DIR
SHA_DIR=$1

# Ensure that SHA_DIR is provided and is a directory
if [ -z "$SHA_DIR" ]; then
printf "Usage: $0 <directory_path>\n"
exit 1
elif [ ! -d "$SHA_DIR" ]; then
printf "Error: $SHA_DIR is not a directory or does not exist.\n"
exit 1
fi

# Use 'find' to locate all files within SHA_DIR
# For each found file, execute 'sha256sum' to calculate its SHA256 checksum
# Append all checksums to 'checksum.list'
find "$SHA_DIR" -type f -exec sha256sum "{}" + > checksum.list

# Print a header message indicating that checksums have been calculated
printf "Checksum of all files in %s:\n" "$SHA_DIR"

# Calculate the SHA256 checksum of the entire 'checksum.list' file
# Extract only the checksum value using 'awk' (ignoring the filename)
# Print the aggregated checksum with indentation for readability
printf "\t%s\n" "$(sha256sum checksum.list | awk '{print $1}')"

find $SHA_DIR -type f -exec sha256sum "{}" + > checksum.list
printf "Checksum of all files in $SHA_DIR:\n"
printf "\t%s\n" $(sha256sum checksum.list | awk '{print $1}')
printf "Please view checksum.list for individual files\n"
# Inform the user that individual file checksums are available in 'checksum.list'
printf "Please view checksum.list for individual file checksums.\n"
Loading
Loading