Feat/add networks (#1303)

* No commit suggestions generated

* No commit suggestions generated

* No commit suggestions generated
This commit is contained in:
Prad Nukala
2025-10-12 11:23:20 -04:00
committed by GitHub
parent 40eadc995e
commit ebfa2b062a
97 changed files with 3979 additions and 16119 deletions
+121
View File
@@ -0,0 +1,121 @@
# Sonr Script Library
This directory contains modular helper scripts for Sonr blockchain operations. These helpers provide reusable functions for common tasks like environment setup, configuration management, key handling, and transaction operations.
## Library Structure
### `env.sh` - Environment Management
Centralizes default environment variables and provides utility functions for logging, validation, and system checks.
**Key Functions:**
- `init_env()` - Initialize environment with required tools and cleanup
- `log_info()`, `log_warn()`, `log_error()`, `log_success()` - Colored logging
- `require_cmd()`, `ensure_file()`, `ensure_binary()` - Validation helpers
- `is_docker()` - Check if running in Docker container
**Environment Variables:**
- `DENOM=usnr` - Default denomination
- `CHAIN_BIN=snrd` - Binary name
- `CHAIN_DIR=~/.sonr` - Chain data directory
- `CHAIN_ID=sonrtest_1-1` - Default chain ID
### `jq_patch.sh` - JSON Operations
Provides safe JSON patching operations using `jq` with temporary files and validation.
**Key Functions:**
- `patch_json(file, jq_expr)` - Apply jq expression to file
- `set_json_string()`, `set_json_number()`, `set_json_bool()` - Type-safe setters
- `json_path_exists()`, `get_json_value()` - Query helpers
- `validate_json()` - JSON validation
### `config.sh` - Configuration Management
Handles TOML configuration files for Cosmos SDK nodes using `crudini` or `sed` fallbacks.
**Key Functions:**
- `configure_node()` - Complete node configuration with ports and settings
- `enable_rpc()`, `enable_rest()`, `enable_grpc()` - Enable services
- `set_consensus_timeouts()`, `set_min_gas_prices()` - Parameter setters
- `set_toml_value()` - Generic TOML value setter
### `keys.sh` - Key Management
Provides functions for importing, managing, and using cryptographic keys.
**Key Functions:**
- `import_mnemonic()` - Import key from mnemonic phrase
- `ensure_key()`, `get_key_address()` - Key validation and retrieval
- `fund_key()`, `delegate_to_validator()` - Account operations
- `wait_for_sync()` - Node synchronization waiting
### `tx.sh` - Transaction Operations
Handles blockchain transactions with error handling and retry logic.
**Key Functions:**
- `submit_tx()` - Submit transaction with gas and error handling
- `query_chain()` - Query blockchain state
- `submit_proposal()`, `vote_proposal()` - Governance operations
- `stake_tokens()`, `get_balance()` - Staking operations
### `genesis.sh` - Genesis File Operations
Specialized functions for genesis file creation and modification.
**Key Functions:**
- `generate_vrf_key()` - Generate VRF keypair for network
- `update_genesis_params()` - Apply Sonr-specific genesis parameters
- `add_constitution()` - Add constitution to governance
- `validate_genesis()` - Genesis file validation
## Usage Examples
### Basic Environment Setup
```bash
#!/bin/bash
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/lib/env.sh"
source "${SCRIPT_DIR}/lib/config.sh"
init_env
ensure_chain_dir
configure_node "$CHAIN_DIR" --rpc-port 26657 --rest-port 1317
```
### Key Management
```bash
#!/bin/bash
source "scripts/lib/keys.sh"
import_mnemonic "mykey" "word1 word2 word3..." "eth_secp256k1"
fund_key "mykey" "1000000usnr"
```
### Genesis Creation
```bash
#!/bin/bash
source "scripts/lib/genesis.sh"
update_genesis_params
add_constitution
generate_vrf_key "$CHAIN_DIR"
```
## Integration Guidelines
1. **Always source required libraries** at the top of scripts
2. **Call `init_env()`** early to set up logging and validation
3. **Use consistent error handling** with the provided logging functions
4. **Validate inputs** using `ensure_*` functions before operations
5. **Handle Docker vs local execution** in wrapper functions
6. **Use descriptive log messages** for user feedback
## Error Handling
All functions use consistent error handling:
- Functions return 0 on success, 1 on failure
- Use `log_error()` for user-visible errors
- Use `log_warn()` for non-fatal issues
- Cleanup temporary files automatically via `trap`
## Docker Compatibility
All functions support both local and Docker execution modes:
- Use `is_docker()` to detect environment
- Use `run_binary()` wrapper for command execution
- Mount volumes correctly for Docker containers
- Handle TTY and interactive mode appropriately
+341
View File
@@ -0,0 +1,341 @@
#!/bin/bash
# scripts/lib/config.sh - Configuration file utilities for TOML and other formats
set -euo pipefail
# Source environment and JSON helpers
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/env.sh"
source "${SCRIPT_DIR}/jq_patch.sh"
# Check if crudini is available for TOML operations
CRUDINI_AVAILABLE=false
if command -v crudini >/dev/null 2>&1; then
CRUDINI_AVAILABLE=true
fi
# Set TOML value using crudini if available, fallback to sed
# Usage: set_toml_value <file> <section> <key> <value>
set_toml_value() {
local file="$1"
local section="$2"
local key="$3"
local value="$4"
ensure_file "$file"
if [[ "$CRUDINI_AVAILABLE" == "true" ]]; then
if crudini --set "$file" "$section" "$key" "$value" 2>/dev/null; then
log_success "Set $section.$key = $value in $file"
return 0
fi
fi
# Fallback to sed for simple cases
log_warn "Using sed fallback for TOML update (install crudini for better support)"
# Escape special characters in value
local escaped_value
escaped_value=$(printf '%s\n' "$value" | sed 's/[[\.*^$()+?{|]/\\&/g')
# Try to find and replace the line
if sed -i "s|^\s*$key\s*=.*|$key = \"$escaped_value\"|g" "$file"; then
log_success "Set $key = $value in $file (using sed)"
return 0
fi
log_error "Failed to set $section.$key in $file"
return 1
}
# Enable RPC server
# Usage: enable_rpc <config_file> [port]
enable_rpc() {
local config_file="$1"
local port="${2:-26657}"
ensure_file "$config_file"
log_info "Enabling RPC server on port $port"
# Update laddr
set_toml_value "$config_file" "" "laddr" "tcp://0.0.0.0:$port"
# Enable CORS
set_toml_value "$config_file" "" "cors_allowed_origins" '["*"]'
}
# Enable REST API server
# Usage: enable_rest <app_config_file> [port]
enable_rest() {
local app_config_file="$1"
local port="${2:-1317}"
ensure_file "$app_config_file"
log_info "Enabling REST API server on port $port"
# Update address
set_toml_value "$app_config_file" "api" "address" "tcp://0.0.0.0:$port"
# Enable API
set_toml_value "$app_config_file" "api" "enable" "true"
# Enable unsafe CORS
set_toml_value "$app_config_file" "api" "enabled-unsafe-cors" "true"
}
# Enable gRPC server
# Usage: enable_grpc <app_config_file> [port]
enable_grpc() {
local app_config_file="$1"
local port="${2:-9090}"
ensure_file "$app_config_file"
log_info "Enabling gRPC server on port $port"
# Update address
set_toml_value "$app_config_file" "grpc" "address" "0.0.0.0:$port"
}
# Enable gRPC-Web server
# Usage: enable_grpc_web <app_config_file> [port]
enable_grpc_web() {
local app_config_file="$1"
local port="${2:-9091}"
ensure_file "$app_config_file"
log_info "Enabling gRPC-Web server on port $port"
# Update address
set_toml_value "$app_config_file" "grpc-web" "address" "0.0.0.0:$port"
}
# Enable JSON-RPC
# Usage: enable_json_rpc <app_config_file> [port] [ws_port]
enable_json_rpc() {
local app_config_file="$1"
local port="${2:-8545}"
local ws_port="${3:-8546}"
ensure_file "$app_config_file"
log_info "Enabling JSON-RPC on port $port (WebSocket: $ws_port)"
# Enable JSON-RPC
set_toml_value "$app_config_file" "json-rpc" "enable" "true"
# Set address
set_toml_value "$app_config_file" "json-rpc" "address" "0.0.0.0:$port"
# Set WebSocket address
set_toml_value "$app_config_file" "json-rpc" "ws-address" "0.0.0.0:$ws_port"
# Enable APIs
set_toml_value "$app_config_file" "json-rpc" "api" "eth,txpool,personal,net,debug,web3"
}
# Enable Rosetta API
# Usage: enable_rosetta <app_config_file> [port]
enable_rosetta() {
local app_config_file="$1"
local port="${2:-8080}"
ensure_file "$app_config_file"
log_info "Enabling Rosetta API on port $port"
# Update address
set_toml_value "$app_config_file" "rosetta" "address" "0.0.0.0:$port"
}
# Set consensus timeouts
# Usage: set_consensus_timeouts <config_file> [propose] [prevote] [precommit] [commit]
set_consensus_timeouts() {
local config_file="$1"
local timeout_propose="${2:-5s}"
local timeout_prevote="${3:-1s}"
local timeout_precommit="${4:-1s}"
local timeout_commit="${5:-5s}"
ensure_file "$config_file"
log_info "Setting consensus timeouts: propose=$timeout_propose, prevote=$timeout_prevote, precommit=$timeout_precommit, commit=$timeout_commit"
set_toml_value "$config_file" "consensus" "timeout_propose" "$timeout_propose"
set_toml_value "$config_file" "consensus" "timeout_prevote" "$timeout_prevote"
set_toml_value "$config_file" "consensus" "timeout_precommit" "$timeout_precommit"
set_toml_value "$config_file" "consensus" "timeout_commit" "$timeout_commit"
}
# Set pruning strategy
# Usage: set_pruning <app_config_file> [strategy]
set_pruning() {
local app_config_file="$1"
local strategy="${2:-default}"
ensure_file "$app_config_file"
log_info "Setting pruning strategy to $strategy"
set_toml_value "$app_config_file" "pruning" "strategy" "$strategy"
}
# Set minimum gas prices
# Usage: set_min_gas_prices <app_config_file> [price]
set_min_gas_prices() {
local app_config_file="$1"
local price="${2:-0${DENOM}}"
ensure_file "$app_config_file"
log_info "Setting minimum gas prices to $price"
set_toml_value "$app_config_file" "minimum-gas-prices" "minimum-gas-prices" "$price"
}
# Enable metrics
# Usage: enable_metrics <config_file> [retention_time]
enable_metrics() {
local config_file="$1"
local retention_time="${2:-3600}"
ensure_file "$config_file"
log_info "Enabling metrics with retention time ${retention_time}s"
# Enable prometheus in config.toml
set_toml_value "$config_file" "instrumentation" "prometheus" "true"
# Set retention time in app.toml
if [[ -f "$config_file" ]]; then
set_toml_value "$config_file" "telemetry" "prometheus-retention-time" "$retention_time"
fi
}
# Set keyring backend
# Usage: set_keyring_backend <client_config_file> [backend]
set_keyring_backend() {
local client_config_file="$1"
local backend="${2:-test}"
ensure_file "$client_config_file"
log_info "Setting keyring backend to $backend"
set_toml_value "$client_config_file" "keyring-backend" "keyring-backend" "$backend"
}
# Set chain ID in client config
# Usage: set_client_chain_id <client_config_file> [chain_id]
set_client_chain_id() {
local client_config_file="$1"
local chain_id="${2:-$CHAIN_ID}"
ensure_file "$client_config_file"
log_info "Setting client chain ID to $chain_id"
set_toml_value "$client_config_file" "chain-id" "chain-id" "$chain_id"
}
# Set client output format
# Usage: set_client_output <client_config_file> [format]
set_client_output() {
local client_config_file="$1"
local format="${2:-json}"
ensure_file "$client_config_file"
log_info "Setting client output format to $format"
set_toml_value "$client_config_file" "output" "output" "$format"
}
# Configure full node settings
# Usage: configure_node <config_dir> [options...]
configure_node() {
local config_dir="$1"
shift
ensure_chain_dir
local config_toml="$config_dir/config/config.toml"
local app_toml="$config_dir/config/app.toml"
local client_toml="$config_dir/config/client.toml"
log_info "Configuring node in $config_dir"
# Apply configurations based on arguments
while [[ $# -gt 0 ]]; do
case $1 in
--rpc-port)
enable_rpc "$config_toml" "$2"
shift 2
;;
--rest-port)
enable_rest "$app_toml" "$2"
shift 2
;;
--grpc-port)
enable_grpc "$app_toml" "$2"
shift 2
;;
--grpc-web-port)
enable_grpc_web "$app_toml" "$2"
shift 2
;;
--json-rpc-port)
enable_json_rpc "$app_toml" "$2" "$3"
shift 3
;;
--rosetta-port)
enable_rosetta "$app_toml" "$2"
shift 2
;;
--consensus-timeouts)
set_consensus_timeouts "$config_toml" "$2" "$3" "$4" "$5"
shift 5
;;
--pruning)
set_pruning "$app_toml" "$2"
shift 2
;;
--min-gas-prices)
set_min_gas_prices "$app_toml" "$2"
shift 2
;;
--metrics)
enable_metrics "$config_toml" "$2"
shift 2
;;
--keyring-backend)
set_keyring_backend "$client_toml" "$2"
shift 2
;;
--chain-id)
set_client_chain_id "$client_toml" "$2"
shift 2
;;
--output-format)
set_client_output "$client_toml" "$2"
shift 2
;;
*)
log_error "Unknown configuration option: $1"
return 1
;;
esac
done
log_success "Node configuration completed"
}
# Export functions
export -f set_toml_value enable_rpc enable_rest enable_grpc enable_grpc_web
export -f enable_json_rpc enable_rosetta set_consensus_timeouts set_pruning
export -f set_min_gas_prices enable_metrics set_keyring_backend set_client_chain_id
export -f set_client_output configure_node
+141
View File
@@ -0,0 +1,141 @@
#!/bin/bash
# scripts/lib/env.sh - Environment defaults and utility functions for Sonr scripts
set -euo pipefail
# Default environment variables for Sonr
export DENOM="${DENOM:=usnr}"
export CHAIN_BIN="${CHAIN_BIN:=snrd}"
export CHAIN_DIR="${CHAIN_DIR:=$HOME/.sonr}"
export CHAIN_ID="${CHAIN_ID:=sonrtest_1-1}"
export KEYRING_BACKEND="${KEYRING_BACKEND:=test}"
export NODE_URL="${NODE_URL:=http://0.0.0.0:26657}"
export GAS="${GAS:=auto}"
export GAS_ADJUSTMENT="${GAS_ADJUSTMENT:=1.5}"
# Colors for logging
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Logging functions
log_info() {
echo -e "${BLUE}[INFO]${NC} $*"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $*"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $*"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $*"
}
# Utility functions
require_cmd() {
local cmd="$1"
if ! command -v "$cmd" >/dev/null 2>&1; then
log_error "Required command '$cmd' not found. Please install it."
exit 1
fi
}
join_path() {
local base="$1"
local path="$2"
echo "$base/$path" | sed 's#//#/#g'
}
# Check if running in Docker
is_docker() {
[[ -f /.dockerenv ]] || [[ -n "${DOCKER_CONTAINER:-}" ]]
}
# Get absolute path
abs_path() {
local path="$1"
if [[ -d "$path" ]]; then
cd "$path" && pwd
else
cd "$(dirname "$path")" && echo "$(pwd)/$(basename "$path")"
fi
}
# Validate chain directory exists
ensure_chain_dir() {
if [[ ! -d "$CHAIN_DIR" ]]; then
log_error "Chain directory does not exist: $CHAIN_DIR"
exit 1
fi
}
# Validate binary exists
ensure_binary() {
if ! command -v "$CHAIN_BIN" >/dev/null 2>&1; then
log_error "Binary '$CHAIN_BIN' not found in PATH"
exit 1
fi
}
# Check if file exists
ensure_file() {
local file="$1"
if [[ ! -f "$file" ]]; then
log_error "Required file not found: $file"
exit 1
fi
}
# Retry function for operations that might fail
retry() {
local max_attempts="$1"
local delay="$2"
local cmd="$3"
shift 3
local attempt=1
while [[ $attempt -le $max_attempts ]]; do
log_info "Attempt $attempt/$max_attempts: $cmd $*"
if "$cmd" "$@"; then
return 0
fi
if [[ $attempt -lt $max_attempts ]]; then
log_warn "Command failed, retrying in ${delay}s..."
sleep "$delay"
fi
((attempt++))
done
log_error "Command failed after $max_attempts attempts: $cmd $*"
return 1
}
# Initialize environment
init_env() {
require_cmd jq
ensure_binary
# Set up cleanup trap
trap cleanup EXIT
log_info "Environment initialized for Sonr chain: $CHAIN_ID"
}
cleanup() {
# Remove temporary files if they exist
rm -f /tmp/genesis.json /tmp/config.toml /tmp/app.toml /tmp/client.toml
}
# Export functions for use in other scripts
export -f log_info log_warn log_error log_success
export -f require_cmd join_path is_docker abs_path
export -f ensure_chain_dir ensure_binary ensure_file
export -f retry init_env cleanup
+210
View File
@@ -0,0 +1,210 @@
#!/bin/bash
# scripts/lib/genesis.sh - Genesis file utilities for Sonr scripts
set -euo pipefail
# Source environment and JSON helpers
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/env.sh"
source "${SCRIPT_DIR}/jq_patch.sh"
# Generate VRF keypair for the network
# Usage: generate_vrf_key [chain_dir]
generate_vrf_key() {
local chain_dir="${1:-$CHAIN_DIR}"
ensure_chain_dir
local genesis_file="$chain_dir/config/genesis.json"
ensure_file "$genesis_file"
local chain_id
chain_id=$(get_json_value "$genesis_file" '.chain_id')
if [[ -z "$chain_id" || "$chain_id" == "null" ]]; then
log_error "Failed to extract chain-id from genesis file"
return 1
fi
log_info "Generating VRF keypair for network: $chain_id"
# Create deterministic entropy from chain-id using SHA256
local entropy_seed
entropy_seed=$(echo -n "$chain_id" | sha256sum | cut -d' ' -f1)
# Generate 64 bytes of deterministic randomness
local seed_part1="$entropy_seed"
local seed_part2
seed_part2=$(echo -n "$entropy_seed" | sha256sum | cut -d' ' -f1)
# Combine to create 64 bytes of hex data
local vrf_key_hex="${seed_part1}${seed_part2}"
# Ensure we have exactly 128 hex characters (64 bytes)
if [[ ${#vrf_key_hex} -ne 128 ]]; then
log_error "Generated VRF key has incorrect size: ${#vrf_key_hex}"
return 1
fi
# Path to store VRF secret key
local vrf_key_path="$chain_dir/vrf_secret.key"
# Ensure directory exists
mkdir -p "$chain_dir"
# Convert hex to binary and write to file
echo -n "$vrf_key_hex" | xxd -r -p > "$vrf_key_path"
# Set restrictive permissions (owner read/write only)
chmod 0600 "$vrf_key_path"
# Validate file was created with correct size (64 bytes)
local file_size
file_size=$(wc -c < "$vrf_key_path")
if [[ $file_size -ne 64 ]]; then
log_error "VRF key file has incorrect size: ${file_size} bytes"
rm -f "$vrf_key_path"
return 1
fi
log_success "VRF keypair generated for network: $chain_id"
log_success "VRF secret key stored securely: $vrf_key_path"
return 0
}
# Update genesis with Sonr-specific parameters
# Usage: update_genesis_params [genesis_file]
update_genesis_params() {
local genesis_file="${1:-$CHAIN_DIR/config/genesis.json}"
ensure_file "$genesis_file"
log_info "Updating genesis parameters for Sonr"
# Update stake denomination
set_json_string "$genesis_file" 'app_state.staking.params.bond_denom' "$DENOM"
# Update mint denomination
set_json_string "$genesis_file" 'app_state.mint.params.mint_denom' "$DENOM"
# Update crisis fee
set_json_object "$genesis_file" 'app_state.crisis.constant_fee' "{\"denom\":\"$DENOM\",\"amount\":\"1000\"}"
# Update minimum commission rate
set_json_string "$genesis_file" 'app_state.staking.params.min_commission_rate' "0.050000000000000000"
# Update block max gas
set_json_string "$genesis_file" 'consensus.params.block.max_gas' "100000000000"
# Update unbonding time
set_json_string "$genesis_file" 'app_state.staking.params.unbonding_time' "300s"
# Update downtime jail duration
set_json_string "$genesis_file" 'app_state.slashing.params.downtime_jail_duration' "60s"
# Update governance parameters for SDK v0.47+
if json_path_exists "$genesis_file" '.app_state.gov.params'; then
set_json_string "$genesis_file" 'app_state.gov.params.max_deposit_period' "30s"
set_json_string "$genesis_file" 'app_state.gov.params.min_deposit[0].amount' "10"
set_json_string "$genesis_file" 'app_state.gov.params.voting_period' "30s"
set_json_string "$genesis_file" 'app_state.gov.params.quorum' "0.000000000000000000"
set_json_string "$genesis_file" 'app_state.gov.params.threshold' "0.000000000000000000"
set_json_string "$genesis_file" 'app_state.gov.params.veto_threshold' "0.000000000000000000"
fi
# Update EVM parameters if present
if json_path_exists "$genesis_file" '.app_state.evm'; then
set_json_string "$genesis_file" 'app_state.evm.params.evm_denom' "$DENOM"
set_json_object "$genesis_file" 'app_state.evm.params.active_static_precompiles' '["0x0000000000000000000000000000000000000100","0x0000000000000000000000000000000000000400","0x0000000000000000000000000000000000000800","0x0000000000000000000000000000000000000801","0x0000000000000000000000000000000000000802","0x0000000000000000000000000000000000000803","0x0000000000000000000000000000000000000804","0x0000000000000000000000000000000000000805"]'
fi
# Update ERC20 parameters if present
if json_path_exists "$genesis_file" '.app_state.erc20'; then
set_json_object "$genesis_file" 'app_state.erc20.params.native_precompiles' '["0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"]'
set_json_object "$genesis_file" 'app_state.erc20.token_pairs' '[{"contract_owner":1,"erc20_address":"0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE","denom":"'"$DENOM"'","enabled":true}]'
fi
# Update feemarket parameters if present
if json_path_exists "$genesis_file" '.app_state.feemarket'; then
set_json_bool "$genesis_file" 'app_state.feemarket.params.no_base_fee' true
set_json_string "$genesis_file" 'app_state.feemarket.params.base_fee' "0.000000000000000000"
fi
# Update tokenfactory parameters if present
if json_path_exists "$genesis_file" '.app_state.tokenfactory'; then
set_json_object "$genesis_file" 'app_state.tokenfactory.params.denom_creation_fee' '[]'
set_json_number "$genesis_file" 'app_state.tokenfactory.params.denom_creation_gas_consume' 100000
fi
# Update ABCI parameters if present
if json_path_exists "$genesis_file" '.consensus.params.abci'; then
set_json_string "$genesis_file" 'consensus.params.abci.vote_extensions_enable_height' "1"
fi
log_success "Genesis parameters updated for Sonr"
}
# Add constitution to governance if CONSTITUTION.md exists
# Usage: add_constitution [genesis_file] [constitution_file]
add_constitution() {
local genesis_file="${1:-$CHAIN_DIR/config/genesis.json}"
local constitution_file="${2:-CONSTITUTION.md}"
ensure_file "$genesis_file"
# Look for CONSTITUTION.md in the git root directory
local script_dir
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
local git_root
git_root="$(cd "${script_dir}/.." && pwd)"
local constitution_path="$git_root/$constitution_file"
if [[ ! -f "$constitution_path" ]]; then
log_warn "Constitution file not found: $constitution_path"
return 0
fi
log_info "Adding constitution from: $constitution_path"
local constitution_content
constitution_content=$(cat "$constitution_path" | jq -Rs .)
set_json_object "$genesis_file" 'app_state.gov.constitution' "$constitution_content"
log_success "Constitution added to governance"
}
# Validate genesis file
# Usage: validate_genesis [genesis_file]
validate_genesis() {
local genesis_file="${1:-$CHAIN_DIR/config/genesis.json}"
ensure_file "$genesis_file"
log_info "Validating genesis file..."
# Validate JSON
validate_json "$genesis_file"
# Check required fields
local chain_id
chain_id=$(get_json_value "$genesis_file" '.chain_id')
if [[ -z "$chain_id" || "$chain_id" == "null" ]]; then
log_error "Genesis file missing chain_id"
return 1
fi
# Check app_state exists
if ! json_path_exists "$genesis_file" '.app_state'; then
log_error "Genesis file missing app_state"
return 1
fi
log_success "Genesis file validation passed"
}
# Export functions
export -f generate_vrf_key update_genesis_params add_constitution validate_genesis
+154
View File
@@ -0,0 +1,154 @@
#!/bin/bash
# scripts/lib/jq_patch.sh - JSON patching utilities for genesis and config files
set -euo pipefail
# Source environment helpers
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/env.sh"
# Patch JSON file with jq expression and save to temp file then move back
# Usage: patch_json <file> <jq_expression>
patch_json() {
local file="$1"
local jq_expr="$2"
ensure_file "$file"
log_info "Patching $file with: $jq_expr"
# Create temp file in same directory to avoid cross-filesystem issues
local temp_file
temp_file="$(dirname "$file")/.tmp.$(basename "$file").$$"
if ! jq -r "$jq_expr" "$file" > "$temp_file" 2>/dev/null; then
rm -f "$temp_file"
log_error "Failed to patch JSON file: $file"
return 1
fi
mv "$temp_file" "$file"
log_success "Successfully patched $file"
}
# Ensure array contains specific value
# Usage: ensure_array_contains <file> <path> <value>
ensure_array_contains() {
local file="$1"
local path="$2"
local value="$3"
ensure_file "$file"
local current_value
current_value=$(jq -r "$path // []" "$file")
# Check if value already exists
if [[ "$current_value" == *"\"$value\""* ]]; then
log_info "Value '$value' already exists in $path"
return 0
fi
# Add value to array
local jq_expr
jq_expr=".${path} += [\"$value\"]"
patch_json "$file" "$jq_expr"
}
# Set string value at path
# Usage: set_json_string <file> <path> <value>
set_json_string() {
local file="$1"
local path="$2"
local value="$3"
local jq_expr
jq_expr=".${path} = \"$value\""
patch_json "$file" "$jq_expr"
}
# Set numeric value at path
# Usage: set_json_number <file> <path> <value>
set_json_number() {
local file="$1"
local path="$2"
local value="$3"
local jq_expr
jq_expr=".${path} = $value"
patch_json "$file" "$jq_expr"
}
# Set boolean value at path
# Usage: set_json_bool <file> <path> <value>
set_json_bool() {
local file="$1"
local path="$2"
local value="$3"
local jq_expr
jq_expr=".${path} = $value"
patch_json "$file" "$jq_expr"
}
# Set object value at path
# Usage: set_json_object <file> <path> <json_object>
set_json_object() {
local file="$1"
local path="$2"
local json_object="$3"
local jq_expr
jq_expr=".${path} = $json_object"
patch_json "$file" "$jq_expr"
}
# Check if path exists and is not null
# Usage: json_path_exists <file> <path>
json_path_exists() {
local file="$1"
local path="$2"
ensure_file "$file"
local result
result=$(jq -r "$path // empty" "$file" 2>/dev/null)
[[ -n "$result" && "$result" != "null" ]]
}
# Get value at path
# Usage: get_json_value <file> <path>
get_json_value() {
local file="$1"
local path="$2"
ensure_file "$file"
jq -r "$path" "$file"
}
# Validate JSON file
# Usage: validate_json <file>
validate_json() {
local file="$1"
ensure_file "$file"
if ! jq empty "$file" >/dev/null 2>&1; then
log_error "Invalid JSON in file: $file"
return 1
fi
log_success "JSON validation passed for $file"
}
# Export functions
export -f patch_json ensure_array_contains set_json_string set_json_number
export -f set_json_bool set_json_object json_path_exists get_json_value validate_json
+211
View File
@@ -0,0 +1,211 @@
#!/bin/bash
# scripts/lib/keys.sh - Key management utilities for Sonr scripts
set -euo pipefail
# Source environment helpers
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/env.sh"
# Import mnemonic and add key to keyring
# Usage: import_mnemonic <key_name> <mnemonic> [algo]
import_mnemonic() {
local key_name="$1"
local mnemonic="$2"
local algo="${3:-eth_secp256k1}"
log_info "Importing key '$key_name'"
if [[ -z "$mnemonic" ]]; then
log_error "Mnemonic cannot be empty"
return 1
fi
# Use Docker wrapper if needed
if is_docker; then
echo "$mnemonic" | $CHAIN_BIN keys add "$key_name" \
--keyring-backend "$KEYRING_BACKEND" \
--algo "$algo" \
--recover
else
echo "$mnemonic" | $CHAIN_BIN keys add "$key_name" \
--keyring-backend "$KEYRING_BACKEND" \
--algo "$algo" \
--home "$CHAIN_DIR" \
--recover
fi
log_success "Key '$key_name' imported successfully"
}
# Ensure key exists in keyring
# Usage: ensure_key <key_name>
ensure_key() {
local key_name="$1"
if ! $CHAIN_BIN keys show "$key_name" \
--keyring-backend "$KEYRING_BACKEND" \
--home "$CHAIN_DIR" \
--output json >/dev/null 2>&1; then
log_error "Key '$key_name' not found in keyring"
return 1
fi
log_info "Key '$key_name' exists in keyring"
}
# Get key address
# Usage: get_key_address <key_name>
get_key_address() {
local key_name="$1"
ensure_key "$key_name"
local address
address=$($CHAIN_BIN keys show "$key_name" \
--keyring-backend "$KEYRING_BACKEND" \
--home "$CHAIN_DIR" \
--output json | jq -r '.address')
if [[ -z "$address" || "$address" == "null" ]]; then
log_error "Failed to get address for key '$key_name'"
return 1
fi
echo "$address"
}
# Fund key with tokens from genesis
# Usage: fund_key <key_name> <amount>
fund_key() {
local key_name="$1"
local amount="$2"
ensure_key "$key_name"
local address
address=$(get_key_address "$key_name")
log_info "Funding key '$key_name' ($address) with $amount"
if is_docker; then
$CHAIN_BIN genesis add-genesis-account "$address" "$amount" \
--keyring-backend "$KEYRING_BACKEND"
else
$CHAIN_BIN genesis add-genesis-account "$address" "$amount" \
--keyring-backend "$KEYRING_BACKEND" \
--home "$CHAIN_DIR"
fi
log_success "Funded key '$key_name' with $amount"
}
# Delegate tokens to validator
# Usage: delegate_to_validator <delegator_key> <validator_address> <amount>
delegate_to_validator() {
local delegator_key="$1"
local validator_address="$2"
local amount="$3"
ensure_key "$delegator_key"
log_info "Delegating $amount from '$delegator_key' to validator $validator_address"
$CHAIN_BIN tx staking delegate "$validator_address" "$amount" \
--from "$delegator_key" \
--chain-id "$CHAIN_ID" \
--node "$NODE_URL" \
--keyring-backend "$KEYRING_BACKEND" \
--gas "$GAS" \
--gas-adjustment "$GAS_ADJUSTMENT" \
--yes
log_success "Delegation completed"
}
# Create validator with key
# Usage: create_validator <key_name> <moniker> <amount> [options...]
create_validator() {
local key_name="$1"
local moniker="$2"
local amount="$3"
shift 3
ensure_key "$key_name"
local validator_address
validator_address=$(get_key_address "$key_name")
log_info "Creating validator '$moniker' with key '$key_name'"
# Build validator JSON
local validator_json
validator_json=$(cat <<EOF
{
"pubkey": "$($CHAIN_BIN tendermint show-validator)",
"amount": "$amount",
"moniker": "$moniker",
"commission-rate": "0.1",
"commission-max-rate": "0.2",
"commission-max-change-rate": "0.01",
"min-self-delegation": "1000000"
}
EOF
)
echo "$validator_json" > /tmp/validator.json
$CHAIN_BIN tx staking create-validator /tmp/validator.json \
--from "$key_name" \
--chain-id "$CHAIN_ID" \
--node "$NODE_URL" \
--keyring-backend "$KEYRING_BACKEND" \
--gas "$GAS" \
--gas-adjustment "$GAS_ADJUSTMENT" \
--yes
rm -f /tmp/validator.json
log_success "Validator '$moniker' created successfully"
}
# Wait for node to sync
# Usage: wait_for_sync [max_tries]
wait_for_sync() {
local max_tries="${1:-10}"
log_info "Waiting for node to sync..."
local tries=0
while [[ $tries -lt $max_tries ]]; do
if $CHAIN_BIN status \
--node "$NODE_URL" \
--output json 2>/dev/null | jq -e '.SyncInfo.catching_up == false' >/dev/null 2>&1; then
log_success "Node is synced"
return 0
fi
log_info "Still syncing... ($((tries + 1))/$max_tries)"
sleep 30
((tries++))
done
log_error "Node failed to sync after $max_tries attempts"
return 1
}
# List keys in keyring
# Usage: list_keys
list_keys() {
$CHAIN_BIN keys list \
--keyring-backend "$KEYRING_BACKEND" \
--home "$CHAIN_DIR" \
--output json | jq
}
# Export functions
export -f import_mnemonic ensure_key get_key_address fund_key
export -f delegate_to_validator create_validator wait_for_sync list_keys
+228
View File
@@ -0,0 +1,228 @@
#!/bin/bash
# scripts/lib/tx.sh - Transaction utilities for Sonr scripts
set -euo pipefail
# Source environment and key helpers
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/env.sh"
source "${SCRIPT_DIR}/keys.sh"
# Submit transaction with automatic gas and error handling
# Usage: submit_tx <from_key> <command...>
submit_tx() {
local from_key="$1"
shift
ensure_key "$from_key"
log_info "Submitting transaction from '$from_key': $*"
local tx_result
tx_result=$($CHAIN_BIN tx "$@" \
--from "$from_key" \
--chain-id "$CHAIN_ID" \
--node "$NODE_URL" \
--keyring-backend "$KEYRING_BACKEND" \
--gas "$GAS" \
--gas-adjustment "$GAS_ADJUSTMENT" \
--output json \
--yes 2>&1)
# Check for transaction hash
local tx_hash
tx_hash=$(echo "$tx_result" | jq -r '.txhash // empty')
if [[ -n "$tx_hash" && "$tx_hash" != "null" ]]; then
log_success "Transaction submitted: $tx_hash"
# Wait for confirmation
sleep 5
# Query transaction result
local tx_query
tx_query=$($CHAIN_BIN query tx "$tx_hash" \
--node "$NODE_URL" \
--output json 2>/dev/null)
local tx_code
tx_code=$(echo "$tx_query" | jq -r '.code // 0')
if [[ "$tx_code" == "0" ]]; then
log_success "Transaction confirmed successfully"
echo "$tx_hash"
return 0
else
local tx_log
tx_log=$(echo "$tx_query" | jq -r '.raw_log // "Unknown error"')
log_error "Transaction failed (code $tx_code): $tx_log"
return 1
fi
else
log_error "Transaction submission failed: $tx_result"
return 1
fi
}
# Query with error handling
# Usage: query_chain <command...>
query_chain() {
log_info "Querying chain: $*"
local result
result=$($CHAIN_BIN query "$@" \
--node "$NODE_URL" \
--output json 2>/dev/null)
if [[ -z "$result" || "$result" == "null" ]]; then
log_error "Query failed or returned null"
return 1
fi
echo "$result"
}
# Get validator address (first available)
# Usage: get_validator_address
get_validator_address() {
log_info "Getting validator address..."
local validators
validators=$(query_chain staking validators)
local validator_address
validator_address=$(echo "$validators" | jq -r '.validators[0].operator_address // empty')
if [[ -z "$validator_address" || "$validator_address" == "null" ]]; then
log_error "No validators found"
return 1
fi
log_success "Using validator: $validator_address"
echo "$validator_address"
}
# Submit governance proposal
# Usage: submit_proposal <from_key> <proposal_file> [proposal_type]
submit_proposal() {
local from_key="$1"
local proposal_file="$2"
local proposal_type="${3:-}"
ensure_file "$proposal_file"
ensure_key "$from_key"
log_info "Submitting governance proposal from '$from_key'"
local submit_cmd="submit-proposal"
if [[ -n "$proposal_type" ]]; then
submit_cmd="$submit_cmd $proposal_type"
fi
# Check if legacy proposal command is needed
if ! $CHAIN_BIN tx gov submit-proposal --help 2>/dev/null | grep -q "submit-proposal"; then
submit_cmd="submit-legacy-proposal"
fi
submit_tx "$from_key" gov "$submit_cmd" "$proposal_file"
}
# Vote on governance proposal
# Usage: vote_proposal <from_key> <proposal_id> <vote_option>
vote_proposal() {
local from_key="$1"
local proposal_id="$2"
local vote_option="${3:-yes}"
ensure_key "$from_key"
log_info "Voting '$vote_option' on proposal $proposal_id from '$from_key'"
submit_tx "$from_key" gov vote "$proposal_id" "$vote_option"
}
# Wait for proposal to pass
# Usage: wait_for_proposal <proposal_id> [max_tries] [interval]
wait_for_proposal() {
local proposal_id="$1"
local max_tries="${2:-3}"
local interval="${3:-30}"
log_info "Waiting for proposal $proposal_id to pass..."
local tries=0
while [[ $tries -lt $max_tries ]]; do
local status
status=$(query_chain gov proposal "$proposal_id" | jq -r '.status // "unknown"')
case "$status" in
"PROPOSAL_STATUS_PASSED")
log_success "Proposal $proposal_id has passed"
return 0
;;
"PROPOSAL_STATUS_REJECTED")
log_error "Proposal $proposal_id was rejected"
return 1
;;
"PROPOSAL_STATUS_FAILED")
log_error "Proposal $proposal_id failed"
return 1
;;
*)
log_info "Proposal status: $status ($((tries + 1))/$max_tries)"
sleep "$interval"
((tries++))
;;
esac
done
log_error "Proposal $proposal_id did not pass after $max_tries attempts"
return 1
}
# Stake tokens to validator
# Usage: stake_tokens <from_key> <validator_address> <amount>
stake_tokens() {
local from_key="$1"
local validator_address="$2"
local amount="$3"
ensure_key "$from_key"
log_info "Staking $amount from '$from_key' to $validator_address"
submit_tx "$from_key" staking delegate "$validator_address" "$amount"
}
# Query account balance
# Usage: get_balance <address> [denom]
get_balance() {
local address="$1"
local denom="${2:-$DENOM}"
log_info "Getting balance for $address (denom: $denom)"
local balance
balance=$(query_chain bank balances "$address" | jq -r ".balances[] | select(.denom == \"$denom\") | .amount // \"0\"")
echo "$balance"
}
# Send tokens
# Usage: send_tokens <from_key> <to_address> <amount>
send_tokens() {
local from_key="$1"
local to_address="$2"
local amount="$3"
ensure_key "$from_key"
log_info "Sending $amount from '$from_key' to $to_address"
submit_tx "$from_key" bank send "$from_key" "$to_address" "$amount"
}
# Export functions
export -f submit_tx query_chain get_validator_address submit_proposal
export -f vote_proposal wait_for_proposal stake_tokens get_balance send_tokens