├── plugins ├── eth-validator-cli │ ├── env.example │ ├── plugin_eth-validator-cli.sh │ └── menu.sh ├── csm │ ├── csm_env_vars.example │ ├── csm_nimbusvalidator.service.example │ └── plugin_csm_validator.sh ├── dora │ ├── dora.service.example │ ├── menu.sh │ ├── explorer-config.yaml.example │ └── plugin_dora.sh ├── contributoor │ ├── contributoor.service.example │ ├── config.yaml.example │ ├── menu.sh │ └── plugin_contributoor.sh ├── client-stats │ ├── client-stats.service.example │ ├── menu.sh │ └── plugin_client_stats.sh ├── aztec │ ├── .env.example │ ├── docker-compose.yml.example │ ├── helper_root.sh │ └── plugin_aztec.sh └── sentinel │ ├── plugin_csm_sentinel.sh │ └── sentinel-installer.sh ├── .github └── workflows │ ├── tagged-release.yml │ └── dev.yml ├── motd ├── .env.overrides.example ├── patches ├── 002-locale.sh └── 001-alerts.sh ├── resync_execution.sh ├── env ├── helpers ├── install_fail2ban.sh ├── set_swappiness.sh ├── install_docker.sh ├── set_noatime.sh ├── install_unattendedupgrades.sh └── install_2fa.sh ├── resync_consensus.sh ├── ethdo.sh ├── eth-duties.sh ├── config.py ├── update_mevboost.sh ├── client_requirements.py ├── install.sh ├── uninstall.sh ├── install-node.sh ├── README.md ├── view_logs.sh ├── update_execution.sh ├── ethereum-metrics-exporter.sh ├── update_consensus.sh └── alert.rules.yml /plugins/eth-validator-cli/env.example: -------------------------------------------------------------------------------- 1 | # JSON-RPC endpoint for your execution client 2 | JSON_RPC_URL=http://localhost:8545 3 | # Beacon node API endpoint 4 | BEACON_API_URL=http://localhost:5052 5 | # Maximum number of validator operations per block 6 | MAX_REQUESTS_PER_BLOCK=5 -------------------------------------------------------------------------------- /plugins/csm/csm_env_vars.example: -------------------------------------------------------------------------------- 1 | # Values used by CSM Nimbus Validator Client plugin. Change if required and restart service. 2 | FEE_RECIPIENT="" 3 | BEACON_NODE_ADDRESS="http://127.0.0.1:5052" 4 | METRICS_PORT="8108" 5 | METRICS_ADDRESS="127.0.0.1" 6 | DATA_DIR="/opt/ethpillar/plugin-csm/csm_nimbus_validator" 7 | GRAFFITI="🏠🥩🪙🛡️Eth💊rCSM" 8 | PAYLOAD_BUILDER="true" 9 | DOPPELGANGER_DETECTION="off" 10 | -------------------------------------------------------------------------------- /.github/workflows/tagged-release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "tagged-release" 3 | 4 | on: 5 | push: 6 | tags: 7 | - "v*" 8 | 9 | jobs: 10 | tagged-release: 11 | name: "Tagged Release" 12 | runs-on: "ubuntu-latest" 13 | 14 | steps: 15 | - uses: "marvinpinto/action-automatic-releases@latest" 16 | with: 17 | repo_token: "${{ secrets.GITHUB_TOKEN }}" 18 | prerelease: false 19 | -------------------------------------------------------------------------------- /plugins/dora/dora.service.example: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Dora Lightweight Beaconchain Explorer 3 | Wants=network-online.target 4 | After=network-online.target 5 | Documentation=https://docs.coincashew.com 6 | 7 | [Service] 8 | User=dora 9 | Group=dora 10 | Type=simple 11 | Restart=on-failure 12 | RestartSec=5 13 | WorkingDirectory=/opt/ethpillar/plugin-dora 14 | ExecStart=/opt/ethpillar/plugin-dora/dora-explorer -config=/opt/ethpillar/plugin-dora/explorer-config.yaml 15 | 16 | [Install] 17 | WantedBy=multi-user.target -------------------------------------------------------------------------------- /plugins/contributoor/contributoor.service.example: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Contributoor Service 3 | After=network-online.target 4 | Wants=network-online.target 5 | Documentation=https://docs.coincashew.com 6 | 7 | [Service] 8 | Type=simple 9 | User=contributoor 10 | Group=contributoor 11 | Restart=always 12 | RestartSec=5 13 | WorkingDirectory=/opt/ethpillar/plugin-contributoor 14 | ExecStart=/opt/ethpillar/plugin-contributoor/sentry --config /opt/ethpillar/plugin-contributoor/config.yaml 15 | 16 | [Install] 17 | WantedBy=multi-user.target 18 | -------------------------------------------------------------------------------- /.github/workflows/dev.yml: -------------------------------------------------------------------------------- 1 | name: test_deployment_full_staking_node 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | run-lido-csm-staking-node: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v4 14 | - name: Install dependencies 15 | run: ./install-node.sh deploy-nimbus-nethermind.py true 16 | - name: Deploy a Lido CSM Staking node, consisting of EL CL VC and mevboost 17 | run: python3 ./deploy-nimbus-nethermind.py --skip_prompts="true" --network="HOODI" --install_config="Lido CSM Staking Node" 18 | -------------------------------------------------------------------------------- /motd: -------------------------------------------------------------------------------- 1 | 2 | 3 | Your Ethereum node is powered by EthPillar 🚀 4 | 5 | 📢 Staking Updates for you - Updated Oct 27: 6 | * 🙌 Big thanks to our 10 GG24 donors for 0.089 ETH. Keep it going! 7 | * ▶️ Last call for Gitcoin Grants 24 https://giveth.io/project/ethpillar-streamlining-ethereum-staking-for-everyone 8 | * 📣 Fusaka: Mainnet releases: 3 Nov 2025, (tentative) Mainnet date: 3 Dec 2025 9 | * 🆕 Aztec L2 Testnet Plugin: https://docs.coincashew.com/ethpillar/aztec 10 | * Join EthPillar stakers on discord at https://discord.gg/WS8E3PMzrb 11 | 12 | To access, enter on terminal: ethpillar 13 | 14 | -------------------------------------------------------------------------------- /.env.overrides.example: -------------------------------------------------------------------------------- 1 | ################################################################################# 2 | # OVERRIDES for Environment Variables 3 | # 4 | # Purpose: Used for non-standard node configurations such as 5 | # failover staking nodes or standalone validator client only installs 6 | # Why: Enables ethpillar scripts to function properly 7 | ################################################################################# 8 | 9 | #To use, uncomment the hashtag variable and modify the value 10 | 11 | #EL_RPC_PORT=8545 12 | #EL_IP_ADDRESS=192.168.1.123 13 | #CL_REST_PORT=5052 14 | #CL_IP_ADDRESS=192.168.1.123 15 | 16 | # Disk Space Check Overrides 17 | #MOUNT_POINTS=("/" "/mnt/nvme2") 18 | #THRESHOLD=15 19 | #ALERT_FILE="/tmp/mount_alerts.txt" 20 | 21 | # Overrides default text editor (nano) 22 | #EDITOR=vim -------------------------------------------------------------------------------- /plugins/csm/csm_nimbusvalidator.service.example: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Nimbus CSM Validator Client 3 | Wants=network-online.target 4 | After=network-online.target 5 | Documentation=https://docs.coincashew.com 6 | 7 | [Service] 8 | User=csm_nimbus_validator 9 | Group=csm_nimbus_validator 10 | Type=simple 11 | Restart=always 12 | RestartSec=5 13 | LimitNOFILE=65536 14 | EnvironmentFile=/opt/ethpillar/plugin-csm/csm_env_vars 15 | ExecStart=/opt/ethpillar/plugin-csm/nimbus_validator_client --data-dir=${DATA_DIR} --payload-builder=${PAYLOAD_BUILDER} --beacon-node=${BEACON_NODE_ADDRESS} --metrics --metrics-port=${METRICS_PORT} --metrics-address=${METRICS_ADDRESS} --suggested-fee-recipient=${FEE_RECIPIENT} --graffiti=${GRAFFITI} --doppelganger-detection=${DOPPELGANGER_DETECTION} --non-interactive 16 | 17 | [Install] 18 | WantedBy=multi-user.target 19 | -------------------------------------------------------------------------------- /plugins/client-stats/client-stats.service.example: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Prysm Client Stats: Collects metrics from your validator or beacon node processes and push them to the beaconcha.in stats service 3 | Wants=network-online.target 4 | After=network-online.target 5 | Documentation=https://docs.coincashew.com 6 | Requires=validator.service 7 | 8 | [Service] 9 | Type=simple 10 | User=client-stats 11 | Group=client-stats 12 | Restart=on-failure 13 | RestartSec=3 14 | KillSignal=SIGINT 15 | TimeoutStopSec=900 16 | ExecStart=/opt/ethpillar/plugin-client-stats/client-stats \ 17 | --beacon-node-metrics-url=http://localhost:8008/metrics \ 18 | --validator-metrics-url=http://localhost:8009/metrics \ 19 | --clientstats-api-url=https://beaconcha.in/api/v1/stats/__APIKEY/__MACHINE_NAME \ 20 | --scrape-interval 3m 21 | 22 | [Install] 23 | WantedBy=multi-user.target -------------------------------------------------------------------------------- /plugins/aztec/.env.example: -------------------------------------------------------------------------------- 1 | #Version of docker image: aztecprotocol/aztec to use 2 | DOCKER_TAG=2.1.2 3 | 4 | #List of Ethereum RPC nodes URLs (comma separated). If multiple provided, the first is used by defaults. e.g. http://192.168.1.123:8545 5 | ETHEREUM_HOSTS=http://host.docker.internal:8545 6 | 7 | #List of Ethereum RPC consensus nodes URLs (comma separated). If multiple provided, the first is used by defaults. e.g. http://192.168.1.123:5052 8 | L1_CONSENSUS_HOST_URLS=http://host.docker.internal:5052 9 | 10 | #The IP and port for the P2P service 11 | P2P_IP= 12 | P2P_PORT=40400 13 | 14 | #Port to run the Aztec Services on 15 | AZTEC_PORT=8080 16 | 17 | #The private key to be used by the publisher. 18 | #SEQ_PUBLISHER_PRIVATE_KEY= 19 | 20 | #Private key of the validator participating in attestation duties. Fund VALIDATOR_ADDRESS with at least 0.01 sepoliaETH. 21 | VALIDATOR_PRIVATE_KEYS= 22 | VALIDATOR_ADDRESS= 23 | 24 | #Block reward recipient ETH address. On mainnet, use a unique hardware wallet secured ETH address. 25 | COINBASE= 26 | 27 | #Logging verbosity for aztec container (error|warn|info|debug|trace) 28 | LOG_LEVEL=info 29 | 30 | # Governance proposer payload address for Aztec protocol 31 | GOVERNANCE_PROPOSER_PAYLOAD_ADDRESS=0xDCd9DdeAbEF70108cE02576df1eB333c4244C666 32 | -------------------------------------------------------------------------------- /plugins/contributoor/config.yaml.example: -------------------------------------------------------------------------------- 1 | #If you encounter configuration issues, you can: 2 | #Compare your config with the example below 3 | #Remove the config file and re-run "contributoor install" to generate a fresh one 4 | #Check the debug logs for detailed error messages 5 | 6 | # The address of your beacon node's HTTP API. 7 | beaconNodeAddress: http://127.0.0.1:5052 8 | 9 | # The address to serve metrics on (optional, disabled if empty). 10 | metricsAddress: "" 11 | 12 | # The address to serve a health check on (optional, disabled if empty). 13 | healthCheckAddress: "" 14 | 15 | # The log level (debug, info, warn, error). 16 | logLevel: info 17 | 18 | # Specifies a network name override. This is only used when connecting to testnets where 19 | # the beacon node reports a generic network name like "testnet". For known networks 20 | # (mainnet, sepolia, holesky, hoodi, etc.), the network is always derived automatically from 21 | # the beacon node's configuration. 22 | #networkName: 23 | 24 | # The output server configuration (credentials are base64 encoded and required if a pandaops server is used). 25 | outputServer: 26 | address: xatu.primary.production.platform.ethpandaops.io:443 27 | credentials: 28 | tls: true 29 | 30 | # The contributoor version to use. 31 | #version: 0.0.63 32 | 33 | # The directory where contributoor stores its configuration and data. 34 | contributoorDirectory: /opt/ethpillar/plugin-contributoor 35 | 36 | # The method to run contributoor (RUN_METHOD_DOCKER, RUN_METHOD_BINARY, RUN_METHOD_SYSTEMD). 37 | runMethod: RUN_METHOD_SYSTEMD -------------------------------------------------------------------------------- /patches/002-locale.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Author: coincashew.eth | coincashew.com 4 | # License: GNU GPL 5 | # Source: https://github.com/coincashew/ethpillar 6 | # 7 | # Made for home and solo stakers 🏠🥩 8 | 9 | ###################################################################### 10 | # Patch 2 : Fix terminal formatting issues - strange chars, missing emojis 11 | # If you installed EthPillar before v4.4.0, run this to patch 12 | ###################################################################### 13 | 14 | detect_os() { 15 | if [ -f /etc/os-release ]; then 16 | . /etc/os-release 17 | case "$ID" in 18 | debian|ubuntu|raspbian) 19 | echo "debian" 20 | ;; 21 | *) 22 | echo "unsupported" 23 | ;; 24 | esac 25 | else 26 | echo "unsupported" 27 | fi 28 | } 29 | 30 | OS=$(detect_os) 31 | 32 | if [ "$OS" = "unsupported" ]; then 33 | echo "❌ Patch 2: Unsupported OS=$OS" 34 | exit 1 35 | fi 36 | 37 | echo "📦 Running Locale patch to fix terminal formatting issues..." 38 | 39 | # Get the current locale setting 40 | current_locale=$(locale | grep '^LANG=' | awk -F= '{print $2}') 41 | 42 | # Check if the current locale contains "UTF" 43 | if [[ "$current_locale" == *"UTF"* ]]; then 44 | echo "✅ Patch 2: Locale is UTF compatible. No patching required." 45 | # Flag patch as completed 46 | sudo mkdir -p /opt/ethpillar/patches 47 | touch 002-locale.completed && sudo mv 002-locale.completed /opt/ethpillar/patches 48 | else 49 | echo "Locale not set. Patching..." 50 | sudo update-locale "LANG=en_US.UTF-8" 51 | sudo locale-gen --purge "en_US.UTF-8" 52 | sudo dpkg-reconfigure --frontend noninteractive locales 53 | echo "Updated locale to en_US.UTF-8" 54 | echo "Logout and login for terminal locale updates to take effect." 55 | echo "✅ Patch 2: Locale patching complete" 56 | fi -------------------------------------------------------------------------------- /resync_execution.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Author: coincashew.eth | coincashew.com 4 | # License: GNU GPL 5 | # Source: https://github.com/coincashew 6 | # 7 | # Made for home and solo stakers 🏠🥩 8 | 9 | BASE_DIR=$(pwd) 10 | 11 | function getClient(){ 12 | EL=$(cat /etc/systemd/system/execution.service | grep Description= | awk -F'=' '{print $2}' | awk '{print $1}') 13 | } 14 | 15 | function promptYesNo(){ 16 | case $EL in 17 | Nethermind) 18 | MSG="This will take a few hours." 19 | ;; 20 | *) 21 | MSG="This will about a day or so." 22 | ;; 23 | esac 24 | if whiptail --title "Resync Execution - $EL" --yesno "$MSG\nConsider configuring a RescueNode to minimize downtime.\nAre you sure you want to resync $EL?" 10 78; then 25 | resyncClient 26 | promptViewLogs 27 | fi 28 | } 29 | 30 | function promptViewLogs(){ 31 | if whiptail --title "Resync $EL started" --yesno "Would you like to view logs and confirm everything is running properly?" 8 78; then 32 | sudo bash -c 'journalctl -fu execution | ccze -A' 33 | fi 34 | } 35 | 36 | function resyncClient(){ 37 | clear 38 | case $EL in 39 | Nethermind) 40 | sudo systemctl stop execution 41 | sudo rm -rf /var/lib/nethermind/* 42 | sudo systemctl restart execution 43 | ;; 44 | Besu) 45 | sudo systemctl stop execution 46 | sudo rm -rf /var/lib/besu/* 47 | sudo systemctl restart execution 48 | ;; 49 | Geth) 50 | sudo systemctl stop execution 51 | sudo rm -rf /var/lib/geth/* 52 | sudo systemctl restart execution 53 | ;; 54 | Erigon) 55 | sudo systemctl stop execution 56 | sudo rm -rf /var/lib/erigon/* 57 | sudo systemctl restart execution 58 | ;; 59 | Reth) 60 | sudo systemctl stop execution 61 | sudo rm -rf /var/lib/reth/* 62 | sudo systemctl restart execution 63 | ;; 64 | esac 65 | } 66 | 67 | getClient 68 | promptYesNo -------------------------------------------------------------------------------- /patches/001-alerts.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Author: coincashew.eth | coincashew.com 4 | # License: GNU GPL 5 | # Source: https://github.com/coincashew/ethpillar 6 | # 7 | # Made for home and solo stakers 🏠🥩 8 | 9 | ###################################################################### 10 | # Patch 1 : Adding Grafana Alerts 11 | # If you installed ethpillar before v1.7.0, run this to enable alerts 12 | ###################################################################### 13 | 14 | # Step 1: Backup and then update prometheus.yml file 15 | sudo mv /etc/prometheus/prometheus.yml /etc/prometheus/prometheus.yml.backup 16 | sudo bash -c "cat << 'EOF' > /etc/prometheus/prometheus.yml 17 | rule_files: 18 | - alert.rules.yml 19 | 20 | global: 21 | scrape_interval: 15s # By default, scrape targets every 15 seconds. 22 | 23 | # A scrape configuration containing exactly one endpoint to scrape: 24 | # Here it's Prometheus itself. 25 | scrape_configs: 26 | - job_name: 'ethereum-metrics-exporter' 27 | static_configs: 28 | - targets: ['localhost:9099'] 29 | - job_name: 'node_exporter' 30 | static_configs: 31 | - targets: ['localhost:9100'] 32 | EOF" 33 | 34 | # Step 2: Install default alert rules and restart prometheus 35 | sudo cp ~/git/ethpillar/alert.rules.yml /etc/prometheus 36 | sudo systemctl restart prometheus 37 | 38 | # Step 3: Show instructions 39 | whiptail --title "Configure Alerting with Grafana" --msgbox "Grafana enables users to create custom alert systems that notify them via multiple channels, including email, messaging apps like Telegram and Discord. 40 | \nWith the default install, basic alerts for CPU/DISK/RAM are configured. 41 | \nTo receive these alerts: 42 | \n- Navigate to Grafana in your web browser 43 | \n- Click Alerting (the alert bell icon) on the left-hand side menu 44 | \n- Create contact points and notification policies" 20 78 45 | -------------------------------------------------------------------------------- /env: -------------------------------------------------------------------------------- 1 | ################################################## 2 | # CONFIGURATION OF INITIAL NODE INSTALL 3 | # DO NOT MODIFY BELOW 4 | # To modify, go to Client > Edit configuration 5 | ################################################## 6 | 7 | # Default Values 8 | EL_P2P_PORT=30303 9 | EL_P2P_PORT_2=30304 10 | EL_RPC_PORT=8545 11 | EL_MAX_PEER_COUNT=50 12 | EL_IP_ADDRESS=127.0.0.1 13 | CL_P2P_PORT=9000 14 | CL_P2P_PORT_2=9001 15 | CL_REST_PORT=5052 16 | CL_MAX_PEER_COUNT=100 17 | CL_IP_ADDRESS=127.0.0.1 18 | JWTSECRET_PATH="/secrets/jwtsecret" 19 | 20 | # Graffiti for validator. Maximum length is 16 characters. 21 | GRAFFITI="🏠🥩🪙🛡️🦓EthPillar" 22 | 23 | # ETH address receiving priority fees (tips) and MEV rewards 24 | FEE_RECIPIENT_ADDRESS= 25 | 26 | # MEV must be greater than this value or a block is produced locally 27 | MEV_MIN_BID="0.006" 28 | 29 | # Ethereum Clients to be installed 30 | CONSENSUS_CLIENT=NIMBUS 31 | EXECUTION_CLIENT=NETHERMIND 32 | VALIDATOR_CLIENT=NIMBUS 33 | 34 | # LIDO Community Staking Module (CSM) settings 35 | CSM_FEE_RECIPIENT_ADDRESS_MAINNET="0x388C818CA8B9251b393131C08a736A67ccB19297" 36 | CSM_FEE_RECIPIENT_ADDRESS_HOODI="0x9b108015fe433F173696Af3Aa0CF7CDb3E104258" 37 | CSM_FEE_RECIPIENT_ADDRESS_HOLESKY="0xE73a3602b99f1f913e72F8bdcBC235e206794Ac8" 38 | CSM_GRAFFITI="🏠🥩🪙🛡️EthPillarCSM" 39 | CSM_MEV_MIN_BID="0" 40 | CSM_WITHDRAWAL_ADDRESS_MAINNET="0xB9D7934878B5FB9610B3fE8A5e441e8fad7E293f" 41 | CSM_WITHDRAWAL_ADDRESS_HOODI="0x4473dCDDbf77679A643BdB654dbd86D67F8d32f2" 42 | CSM_WITHDRAWAL_ADDRESS_HOLESKY="0xF0179dEC45a37423EAD4FaD5fCb136197872EAd9" 43 | LAUNCHPAD_URL_LIDO_MAINNET="https://csm.lido.fi/?ref=ethpillar" 44 | LAUNCHPAD_URL_LIDO_HOODI="https://csm.testnet.fi/?ref=ethpillar" 45 | LAUNCHPAD_URL_LIDO_HOLESKY="https://csm-holesky.testnet.fi/?ref=ethpillar" 46 | 47 | # Disk Space Check 48 | MOUNT_POINTS=("/") 49 | THRESHOLD=10 50 | ALERT_FILE="/tmp/mount_alerts.txt" -------------------------------------------------------------------------------- /plugins/aztec/docker-compose.yml.example: -------------------------------------------------------------------------------- 1 | services: 2 | aztec-node: 3 | container_name: aztec-sequencer 4 | image: aztecprotocol/aztec:${DOCKER_TAG:-2.1.2} 5 | restart: unless-stopped 6 | stop_grace_period: 1m 7 | mem_limit: 16g # Limit to 16GB RAM 8 | mem_reservation: 8g # Reserve 8GB RAM 9 | security_opt: 10 | - no-new-privileges:true # Prevent gaining additional privileges 11 | tmpfs: 12 | - /tmp:size=1g # Provide writable /tmp 13 | cap_drop: # Drop all capabilities 14 | - ALL 15 | cap_add: # Add only required capabilities 16 | - NET_BIND_SERVICE # Needed for binding to ports 17 | environment: 18 | NETWORK: testnet 19 | ETHEREUM_HOSTS: ${ETHEREUM_HOSTS} 20 | L1_CONSENSUS_HOST_URLS: ${L1_CONSENSUS_HOST_URLS} 21 | DATA_DIRECTORY: /data 22 | VALIDATOR_PRIVATE_KEYS: ${VALIDATOR_PRIVATE_KEYS:-} 23 | COINBASE: ${COINBASE} 24 | P2P_IP: ${P2P_IP:-} 25 | P2P_PORT: ${P2P_PORT} 26 | AZTEC_PORT: ${AZTEC_PORT} 27 | LOG_LEVEL: ${LOG_LEVEL:-info} 28 | SEQ_PUBLISHER_PRIVATE_KEY: ${SEQ_PUBLISHER_PRIVATE_KEY:-} 29 | GOVERNANCE_PROPOSER_PAYLOAD_ADDRESS: ${GOVERNANCE_PROPOSER_PAYLOAD_ADDRESS} 30 | entrypoint: > 31 | sh -c 'node --no-warnings /usr/src/yarn-project/aztec/dest/bin/index.js start --node --archiver --sequencer --sync-mode full' 32 | ports: 33 | - ${AZTEC_PORT}:${AZTEC_PORT} 34 | - ${P2P_PORT}:${P2P_PORT} 35 | - ${P2P_PORT}:${P2P_PORT}/udp 36 | volumes: 37 | - /opt/ethpillar/aztec/data:/data 38 | logging: 39 | driver: "json-file" 40 | options: 41 | max-size: "100m" 42 | max-file: "3" 43 | compress: "true" 44 | extra_hosts: 45 | - "host.docker.internal:host-gateway" 46 | -------------------------------------------------------------------------------- /helpers/install_fail2ban.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Author: coincashew.eth | coincashew.com 3 | # License: GNU GPL 4 | # Source: https://github.com/coincashew/ethpillar 5 | # Description: EthPillar is a one-liner setup tool and node management TUI 6 | # 7 | # Made for home and solo stakers 🏠🥩 8 | 9 | # 🫶 Make improvements and suggestions on GitHub: 10 | # * https://github.com/coincashew/ethpillar 11 | # 🙌 Ask questions on Discord: 12 | # * https://discord.gg/dEpAVWgFNB 13 | 14 | # Check if the user is root 15 | if [[ $EUID -ne 0 ]]; then 16 | echo "This script must be run as root. Please use sudo." 17 | exit 1 18 | fi 19 | 20 | clear 21 | echo "########################################################################################" 22 | echo "Fail2Ban: Automatically protecting your node from common attack patterns" 23 | echo "########################################################################################" 24 | echo "Key Points:" 25 | echo "* Monitors log files for suspicious activity (repeated login failures, brute force attacks)" 26 | echo "* Fail2Ban automatically blocks malicious IPs" 27 | echo "" 28 | read -rp "Do you wish to install? [y|n]" yn 29 | if [[ ! ${yn} = [Yy]* ]]; then 30 | exit 0 31 | fi 32 | 33 | # Prompt user to change SSH port 34 | read -rp "Do you want to change the SSH port? [y/n] " change_port 35 | 36 | if [[ "$change_port" == "y" ]]; then 37 | read -rp "Enter the new SSH port: " new_port 38 | else 39 | new_port="22" 40 | fi 41 | 42 | # Install fail2ban 43 | echo "Installing fail2ban..." 44 | apt update && apt install -y fail2ban 45 | 46 | # Create config file 47 | config_path="/etc/fail2ban/jail.local" 48 | echo "[sshd] 49 | enabled = true 50 | port = $new_port 51 | filter = sshd 52 | logpath = /var/log/auth.log 53 | maxretry = 3" > $config_path 54 | 55 | # Start fail2ban service 56 | echo "Starting fail2ban service..." 57 | systemctl start fail2ban 58 | 59 | # Enable fail2ban service to start on boot 60 | echo "Enabling fail2ban to start on boot..." 61 | systemctl enable fail2ban 62 | 63 | # Display success message 64 | echo "SUCCESS: Fail2ban installation complete. SSH port set to $new_port." 65 | echo "Press ENTER to return to menu" 66 | read -------------------------------------------------------------------------------- /plugins/client-stats/menu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Author: coincashew.eth | coincashew.com 4 | # License: GNU GPL 5 | # Source: https://github.com/coincashew/ethpillar 6 | # Description: prysm-client-stats helper script 7 | # 8 | # Made for home and solo stakers 🏠🥩 9 | 10 | # Source directory 11 | SOURCE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 12 | 13 | # Load functions 14 | source $SOURCE_DIR/../../functions.sh 15 | 16 | while true; do 17 | # Define the options for the submenu 18 | SUBOPTIONS=( 19 | 1 "View Logs" 20 | 2 "Start client-stats" 21 | 3 "Stop client-stats" 22 | 4 "Restart client-stats" 23 | 5 "Edit service configuration" 24 | 6 "Update to latest release" 25 | 7 "Uninstall plugin" 26 | - "" 27 | 10 "Back to main menu" 28 | ) 29 | 30 | # Display the submenu and get the user's choice 31 | SUBCHOICE=$(whiptail --clear --cancel-button "Back" \ 32 | --backtitle "$BACKTITLE" \ 33 | --title "Plugin - 🌈 Prysm client-stats collects metrics. Monitoring with beaconcha.in" \ 34 | --menu "\nChoose one of the following options:" \ 35 | 0 0 0 \ 36 | "${SUBOPTIONS[@]}" \ 37 | 3>&1 1>&2 2>&3) 38 | 39 | if [ $? -gt 0 ]; then # user pressed button 40 | break 41 | fi 42 | # Handle the user's choice from the submenu 43 | case $SUBCHOICE in 44 | 1) 45 | sudo bash -c 'journalctl -fu client-stats | ccze -A' 46 | ;; 47 | 2) 48 | sudo systemctl start client-stats 49 | sudo systemctl enable client-stats 50 | ;; 51 | 3) 52 | sudo systemctl stop client-stats 53 | sudo systemctl disable client-stats 54 | ;; 55 | 4) 56 | sudo systemctl restart client-stats 57 | ;; 58 | 5) 59 | sudo "${EDITOR}" /etc/systemd/system/client-stats.service 60 | if whiptail --title "Restart services" --yesno "Do you want to restart with updated configurations?" 8 78; then 61 | sudo systemctl daemon-reload 62 | sudo systemctl restart client-stats 63 | fi 64 | ;; 65 | 6) 66 | runScript plugins/client-stats/plugin_client_stats.sh -u 67 | ;; 68 | 7) 69 | runScript plugins/client-stats/plugin_client_stats.sh -r 70 | ;; 71 | 10) 72 | break 73 | ;; 74 | esac 75 | done 76 | -------------------------------------------------------------------------------- /helpers/set_swappiness.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Author: coincashew.eth | coincashew.com 3 | # License: GNU GPL 4 | # Source: https://github.com/coincashew/ethpillar 5 | # Description: EthPillar is a one-liner setup tool and node management TUI 6 | # 7 | # Made for home and solo stakers 🏠🥩 8 | 9 | # 🫶 Make improvements and suggestions on GitHub: 10 | # * https://github.com/coincashew/ethpillar 11 | # 🙌 Ask questions on Discord: 12 | # * https://discord.gg/dEpAVWgFNB 13 | 14 | # Exit on first error 15 | set -e 16 | 17 | # Check if the user is root 18 | if [[ $EUID -ne 0 ]]; then 19 | echo "This script must be run as root. Please use sudo." 20 | exit 1 21 | fi 22 | 23 | clear 24 | echo "########################################################################################" 25 | echo "Swappiness: How aggressively Linux swaps pages between memory and the swap space" 26 | echo "########################################################################################" 27 | echo "Key Points:" 28 | echo "* Linux systems tend to heavily utilize swap space by default" 29 | echo "* This can negatively impact performance-sensitive applications, such as Ethereum nodes" 30 | echo "* Benefits: Systems > 16GB RAM improve performance by setting 'swappiness' value to 1" 31 | echo "* Defaults: Swappiness value on most Linux distributions is 60" 32 | echo "* Valid Values: Swappiness value can be set between 0 and 100" 33 | echo "" 34 | while true; do 35 | read -r -p "${tty_blue}Enter your swappiness value: (Press enter to use default, 1)${tty_reset} " SWAPPINESS_VALUE 36 | # Default swappiness to 1 37 | SWAPPINESS_VALUE=${SWAPPINESS_VALUE:-1} 38 | # Validate swappiness value 39 | if [[ "$SWAPPINESS_VALUE" =~ ^-?[0-9]+$ ]] && (( "$SWAPPINESS_VALUE" >= 0 && "$SWAPPINESS_VALUE" <= 100 )); then 40 | break 41 | else 42 | echo "ERROR: Swappiness value must be a number between 0 and 100" 43 | fi 44 | done 45 | 46 | echo "INFO: Setting vm.swappiness to $SWAPPINESS_VALUE" 47 | 48 | # Create sysctl.d directory if it doesn't exist 49 | sudo mkdir -p /etc/sysctl.d 50 | 51 | # Create or update the swappiness configuration file 52 | echo "vm.swappiness=$SWAPPINESS_VALUE" | sudo tee /etc/sysctl.d/99-swappiness.conf > /dev/null 53 | 54 | # Apply the new setting 55 | sudo sysctl -p /etc/sysctl.d/99-swappiness.conf 56 | 57 | echo "INFO: Configuration saved to /etc/sysctl.d/99-swappiness.conf" 58 | echo "INFO: Successfully set vm.swappiness to $SWAPPINESS_VALUE" 59 | echo "Press ENTER to return to menu" 60 | read -------------------------------------------------------------------------------- /resync_consensus.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Author: coincashew.eth | coincashew.com 4 | # License: GNU GPL 5 | # Source: https://github.com/coincashew/ethpillar 6 | # 7 | # Made for home and solo stakers 🏠🥩 8 | 9 | # Load functions 10 | BASE_DIR=$(pwd) 11 | source $BASE_DIR/functions.sh 12 | 13 | function getClient(){ 14 | CL=$(cat /etc/systemd/system/consensus.service | grep Description= | awk -F'=' '{print $2}' | awk '{print $1}') 15 | } 16 | 17 | function promptYesNo(){ 18 | if whiptail --title "Resync Consensus - $CL" --yesno "This will only take a minute or two.\nAre you sure you want to resync $CL?" 9 78; then 19 | resyncClient 20 | promptViewLogs 21 | fi 22 | } 23 | 24 | function promptViewLogs(){ 25 | if whiptail --title "Resync $CL complete" --yesno "Would you like to view logs and confirm everything is running properly?" 8 78; then 26 | sudo bash -c 'journalctl -fu consensus | ccze -A' 27 | fi 28 | } 29 | 30 | function resyncClient(){ 31 | case $CL in 32 | Lighthouse) 33 | sudo systemctl stop consensus 34 | sudo rm -rf /var/lib/lighthouse/beacon 35 | sudo systemctl restart consensus 36 | ;; 37 | Lodestar) 38 | sudo systemctl stop consensus 39 | sudo rm -rf /var/lib/lodestar/chain-db 40 | sudo systemctl restart consensus 41 | ;; 42 | Teku) 43 | sudo systemctl stop consensus 44 | sudo rm -rf /var/lib/teku/beacon 45 | sudo systemctl restart consensus 46 | ;; 47 | Nimbus) 48 | getNetwork 49 | case $NETWORK in 50 | Holesky) 51 | _checkpointsync="--network=holesky --trusted-node-url=https://holesky.beaconstate.ethstaker.cc" 52 | ;; 53 | Mainnet) 54 | _checkpointsync="--network=mainnet --trusted-node-url=https://beaconstate.ethstaker.cc" 55 | ;; 56 | Sepolia) 57 | _checkpointsync="--network=sepolia --trusted-node-url=https://sepolia.beaconstate.info" 58 | ;; 59 | Ephemery) 60 | _checkpointsync="--network=/opt/ethpillar/testnet --trusted-node-url=https://ephemery.beaconstate.ethstaker.cc" 61 | ;; 62 | Hoodi) 63 | _checkpointsync="--network=hoodi --trusted-node-url=https://checkpoint-sync.hoodi.ethpandaops.io" 64 | ;; 65 | esac 66 | 67 | sudo systemctl stop consensus 68 | sudo rm -rf /var/lib/nimbus/db 69 | 70 | sudo -u consensus /usr/local/bin/nimbus_beacon_node trustedNodeSync \ 71 | ${_checkpointsync} \ 72 | --data-dir=/var/lib/nimbus \ 73 | --backfill=false 74 | 75 | sudo systemctl restart consensus 76 | ;; 77 | Prysm) 78 | sudo systemctl stop consensus 79 | sudo rm -rf /var/lib/prysm/beacon/beaconchaindata 80 | sudo systemctl restart consensus 81 | ;; 82 | esac 83 | } 84 | 85 | getClient 86 | promptYesNo -------------------------------------------------------------------------------- /plugins/dora/menu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Author: coincashew.eth | coincashew.com 4 | # License: GNU GPL 5 | # Source: https://github.com/coincashew/ethpillar 6 | # Description: Dora the Explorer helper script 7 | # 8 | # Made for home and solo stakers 🏠🥩 9 | 10 | # Source directory 11 | SOURCE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 12 | 13 | # Load functions 14 | source $SOURCE_DIR/../../functions.sh 15 | 16 | while true; do 17 | # Define the options for the submenu 18 | SUBOPTIONS=( 19 | 1 "View Logs" 20 | 2 "Start dora" 21 | 3 "Stop dora" 22 | 4 "Restart dora" 23 | 5 "Edit dora configuration" 24 | 6 "Edit service configuration" 25 | 7 "Update to latest release" 26 | 8 "Uninstall plugin" 27 | - "" 28 | 10 "Back to main menu" 29 | ) 30 | 31 | # Display the submenu and get the user's choice 32 | SUBCHOICE=$(whiptail --clear --cancel-button "Back" \ 33 | --backtitle "$BACKTITLE" \ 34 | --title "Plugin - Dora: Lightweight Block Explorer " \ 35 | --menu "\nAccess Dora at: http://127.0.0.1:8080 or http://$ip_current:8080\n\nChoose one of the following options:" \ 36 | 0 0 0 \ 37 | "${SUBOPTIONS[@]}" \ 38 | 3>&1 1>&2 2>&3) 39 | 40 | if [ $? -gt 0 ]; then # user pressed button 41 | break 42 | fi 43 | # Handle the user's choice from the submenu 44 | case $SUBCHOICE in 45 | 1) 46 | sudo bash -c 'journalctl -fu dora | ccze -A' 47 | ;; 48 | 2) 49 | sudo systemctl start dora 50 | sudo systemctl enable dora 51 | ;; 52 | 3) 53 | sudo systemctl stop dora 54 | sudo systemctl disable dora 55 | ;; 56 | 4) 57 | sudo systemctl restart dora 58 | ;; 59 | 5) 60 | sudo "${EDITOR}" /opt/ethpillar/plugin-dora/explorer-config.yaml 61 | if whiptail --title "Change explorer-config.yaml and restart services" --yesno "Do you want to restart with updated explorer-config.yaml?" 8 78; then 62 | sudo systemctl restart dora 63 | fi 64 | ;; 65 | 6) 66 | sudo "${EDITOR}" /etc/systemd/system/dora.service 67 | if whiptail --title "Restart services" --yesno "Do you want to restart with updated configurations?" 8 78; then 68 | sudo systemctl daemon-reload 69 | sudo systemctl restart dora 70 | fi 71 | ;; 72 | 7) 73 | runScript plugins/dora/plugin_dora.sh -u 74 | ;; 75 | 8) 76 | runScript plugins/dora/plugin_dora.sh -r 77 | ;; 78 | 10) 79 | break 80 | ;; 81 | esac 82 | done -------------------------------------------------------------------------------- /plugins/contributoor/menu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Author: coincashew.eth | coincashew.com 4 | # License: GNU GPL 5 | # Source: https://github.com/coincashew/ethpillar 6 | # Description: Contributoor helper script 7 | # 8 | # Made for home and solo stakers 🏠🥩 9 | 10 | # Source directory 11 | SOURCE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 12 | 13 | # Load functions 14 | # shellcheck disable=SC1091 15 | source "$SOURCE_DIR"/../../functions.sh 16 | 17 | # Load version 18 | PLUGIN_INSTALL_PATH=/opt/ethpillar/plugin-contributoor 19 | [[ -f $PLUGIN_INSTALL_PATH/current_version ]] && VERSION=$(cat $PLUGIN_INSTALL_PATH/current_version) 20 | 21 | while true; do 22 | # Define the options for the submenu 23 | SUBOPTIONS=( 24 | 1 "View Logs" 25 | 2 "Start contributoor" 26 | 3 "Stop contributoor" 27 | 4 "Restart contributoor" 28 | 5 "Edit contributoor config.yaml" 29 | 6 "Edit service configuration" 30 | 7 "Update to latest release" 31 | 8 "Uninstall plugin" 32 | - "" 33 | 10 "Back to main menu" 34 | ) 35 | 36 | # Display the submenu and get the user's choice 37 | SUBCHOICE=$(whiptail --clear --cancel-button "Back" \ 38 | --backtitle "$BACKTITLE" \ 39 | --title "Plugin - 🐼 Contributoor $VERSION by ethpandaops.io" \ 40 | --menu "\nChoose one of the following options:" \ 41 | 0 0 0 \ 42 | "${SUBOPTIONS[@]}" \ 43 | 3>&1 1>&2 2>&3) 44 | 45 | if [ $? -gt 0 ]; then # user pressed button 46 | break 47 | fi 48 | # Handle the user's choice from the submenu 49 | case $SUBCHOICE in 50 | 1) 51 | sudo bash -c 'journalctl -fu contributoor | ccze -A' 52 | ;; 53 | 2) 54 | sudo systemctl enable contributoor 55 | sudo systemctl start contributoor 56 | ;; 57 | 3) 58 | sudo systemctl disable contributoor 59 | sudo systemctl stop contributoor 60 | ;; 61 | 4) 62 | sudo systemctl restart contributoor 63 | ;; 64 | 5) 65 | sudo "${EDITOR}" /opt/ethpillar/plugin-contributoor/config.yaml 66 | if whiptail --title "Restart services" --yesno "Do you want to restart with updated configurations?" 8 78; then 67 | sudo systemctl daemon-reload 68 | sudo systemctl restart contributoor 69 | fi 70 | ;; 71 | 6) 72 | sudo "${EDITOR}" /etc/systemd/system/contributoor.service 73 | if whiptail --title "Restart services" --yesno "Do you want to restart with updated configurations?" 8 78; then 74 | sudo systemctl daemon-reload 75 | sudo systemctl restart contributoor 76 | fi 77 | ;; 78 | 7) 79 | runScript plugins/contributoor/plugin_contributoor.sh -u 80 | ;; 81 | 8) 82 | runScript plugins/contributoor/plugin_contributoor.sh -r 83 | ;; 84 | 10) 85 | break 86 | ;; 87 | esac 88 | done 89 | -------------------------------------------------------------------------------- /helpers/install_docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | echo "🔄 Updating and upgrading all system packages..." 6 | sudo apt update -y && sudo apt upgrade -y 7 | 8 | echo "🧹 Removing old or conflicting Docker packages..." 9 | for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove -y $pkg || true; done 10 | 11 | echo "🔑 Adding Docker’s official GPG key..." 12 | sudo install -m 0755 -d /etc/apt/keyrings 13 | sudo apt-get install -y ca-certificates curl gnupg 14 | # shellcheck disable=SC1091 15 | source /etc/os-release 16 | curl -fsSL https://download.docker.com/linux/"${ID}"/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg 17 | sudo chmod a+r /etc/apt/keyrings/docker.gpg 18 | 19 | echo "📚 Adding Docker’s official repository..." 20 | echo \ 21 | "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/${ID} \ 22 | ${VERSION_CODENAME} stable" | \ 23 | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null 24 | echo "🔄 Updating and upgrading all system packages again (with Docker repo)..." 25 | sudo apt update -y && sudo apt upgrade -y 26 | 27 | echo "🐳 Installing Docker Engine, CLI, containerd, Buildx, and Compose plugin..." 28 | sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin 29 | 30 | # Except when logged in as root, configure ROOTLESS mode 31 | if [ "$(id -u)" -ne 0 ]; then 32 | echo "🧱 Enabling ROOTLESS Docker Mode..." 33 | sudo apt-get install -y docker-ce-rootless-extras 34 | sudo systemctl disable --now docker.service docker.socket 35 | sudo rm -f /var/run/docker.sock || true 36 | sudo apt-get install -y uidmap 37 | dockerd-rootless-setuptool.sh install 38 | # enable user service (best-effort) and allow running after logout 39 | sudo loginctl enable-linger "$USER" || true 40 | systemctl enable docker || true 41 | systemctl restart docker || true 42 | export XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$(id -d)}" 43 | # point docker CLI to rootless socket for this user 44 | # shellcheck disable=SC2016 45 | if ! grep -q 'DOCKER_HOST=unix://\$XDG_RUNTIME_DIR/docker.sock' "$HOME/.profile" 2>/dev/null; then 46 | # shellcheck disable=SC2016 47 | echo 'export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock' >> "$HOME/.profile" 48 | fi 49 | # shellcheck disable=SC2016 50 | if ! grep -q 'DOCKER_HOST=unix://\$XDG_RUNTIME_DIR/docker.sock' "$HOME/.bashrc" 2>/dev/null; then 51 | # shellcheck disable=SC2016 52 | echo 'export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock' >> "$HOME/.bashrc" 53 | fi 54 | fi 55 | 56 | if [ "$(id -u)" -eq 0 ]; then 57 | echo "🚦 Enabling and starting Docker service..." 58 | sudo systemctl enable --now docker 59 | else 60 | echo "ℹ️ Rootless Docker uses the per-user service. Skipping system docker.service." 61 | fi 62 | 63 | echo "🎉 Docker and Docker Compose are fully installed and up to date!" -------------------------------------------------------------------------------- /helpers/set_noatime.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Author: coincashew.eth | coincashew.com 3 | # License: GNU GPL 4 | # Source: https://github.com/coincashew/ethpillar 5 | # Description: EthPillar is a one-liner setup tool and node management TUI 6 | # 7 | # Made for home and solo stakers 🏠🥩 8 | 9 | # 🫶 Make improvements and suggestions on GitHub: 10 | # * https://github.com/coincashew/ethpillar 11 | # 🙌 Ask questions on Discord: 12 | # * https://discord.gg/dEpAVWgFNB 13 | 14 | # Check if the user is root 15 | if [[ $EUID -ne 0 ]]; then 16 | echo "This script must be run as root. Please use sudo." 17 | exit 1 18 | fi 19 | 20 | clear 21 | echo "########################################################################################" 22 | echo "noatime: Reduced Disk I/O and Improved Performance" 23 | echo "########################################################################################" 24 | echo "Key Points:" 25 | echo "* Purpose: Filesystem does not update the access time (atime) every time a file is read" 26 | echo "* Reduced Disk I/O: Fewer writes means less wear on your storage and faster read operations" 27 | echo "* Improved Performance: Speed improvements, especially for file-intensive workloads" 28 | echo "" 29 | echo "Do you wish to continue? [y|n]" 30 | read -rsn1 yn 31 | if [[ ! ${yn} = [Yy]* ]]; then 32 | exit 0 33 | fi 34 | _CHANGED="" 35 | cp /etc/fstab /etc/fstab.bak 36 | echo "INFO: Backup of /etc/fstab created at /etc/fstab.bak" 37 | 38 | # Function to append noatime to mount options 39 | append_noatime() { 40 | local label=$1 41 | local mount_point=$2 42 | local options=$(grep -w "$label" /etc/fstab | awk '{print $4}') 43 | local options_after="" 44 | if [[ $options != *"noatime"* ]]; then 45 | if [[ $options == *","* ]]; then 46 | options_after="$options,noatime" 47 | else 48 | options_after="noatime,$options" 49 | fi 50 | sed -i "s|$options|$options_after|" /etc/fstab 51 | echo "SUCCESS: Appended noatime to $mount_point mount point" 52 | _CHANGED="1" 53 | else 54 | echo "INFO: noatime already present in $mount_point mount point" 55 | fi 56 | } 57 | 58 | # Loop through mount points in /etc/fstab and append noatime 59 | while IFS= read -r line; do 60 | if [[ ! "$line" =~ "#" ]]; then #skip comments 61 | mount_point=$(echo $line | awk '{print $2}') 62 | label=$(echo $line | awk '{print $1}') 63 | if [[ ! "$mount_point" = /boot ]] && 64 | [[ ! "$mount_point" = /boot/efi ]] && 65 | [[ ! "$mount_point" = none ]] && 66 | [[ -n $mount_point ]]; then 67 | append_noatime $label $mount_point 68 | else 69 | echo "INFO: SKIPPING $label $mount_point" 70 | fi 71 | fi 72 | done < /etc/fstab 73 | if [[ $_CHANGED == "1" ]]; then 74 | echo "DONE: Please reboot your system to apply the changes" 75 | else 76 | echo "DONE: No mount point changes made." 77 | fi 78 | echo "Press ENTER to return to menu" 79 | read -------------------------------------------------------------------------------- /plugins/sentinel/plugin_csm_sentinel.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Author: coincashew.eth | coincashew.com 4 | # License: GNU GPL 5 | # Source: https://github.com/coincashew/ethpillar 6 | # Description: csm-sentinel helper script 7 | # 8 | # Made for home and solo stakers 🏠🥩 9 | 10 | # Base directory with scripts 11 | BASE_DIR=$HOME/git/ethpillar 12 | 13 | # Variables 14 | #GITHUB_URL=https://api.github.com/repos/skhomuti/csm-sentinel/releases/latest 15 | #GITHUB_RELEASE_NODES=https://github.com/skhomuti/csm-sentinel/releases 16 | DESCRIPTION="CSM Sentinel is a telegram bot that sends you notifications for your CSM Node Operator events. Self-hosted. Uses docker." 17 | DOCUMENTATION="https://github.com/skhomuti/csm-sentinel" 18 | SOURCE_CODE="https://github.com/skhomuti/csm-sentinel" 19 | APP_NAME="csm-sentinel" 20 | PLUGIN_NAME="CSM Sentinel Plugin" 21 | PLUGIN_SOURCE_PATH="$BASE_DIR/plugins/sentinel" 22 | PLUGIN_INSTALL_PATH="/opt/ethpillar/plugin-sentinel" 23 | 24 | #Asks to update 25 | function upgrade(){ 26 | if whiptail --title "Update $PLUGIN_NAME" --yesno "Reminder: Always read the release notes for breaking changes: $SOURCE_CODE\n\nDo you want to update?" 10 78; then 27 | cd $PLUGIN_INSTALL_PATH/$APP_NAME 28 | sudo git pull 29 | sudo docker stop $APP_NAME 30 | sudo docker build -t csm-sentinel . 31 | __start 32 | fi 33 | } 34 | 35 | # Installs latest release 36 | function install(){ 37 | exec sudo $PLUGIN_SOURCE_PATH/sentinel-installer.sh 38 | } 39 | 40 | # Starts the docker container 41 | function __start(){ 42 | cd $PLUGIN_INSTALL_PATH/$APP_NAME 43 | if docker ps -aq --filter=name=$APP_NAME > /dev/null 2>&1; then 44 | sudo docker rm -f $APP_NAME 45 | fi 46 | sudo docker run --net=host -d --env-file=.env --name csm-sentinel -v csm-sentinel-persistent:/app/.storage csm-sentinel 47 | } 48 | 49 | # Uninstall 50 | function removeAll() { 51 | if whiptail --title "Uninstall $APP_NAME" --defaultno --yesno "Are you sure you want to remove $APP_NAME" 9 78; then 52 | sudo docker stop "$APP_NAME" 53 | sudo docker rm -f $APP_NAME 54 | sudo docker rmi -f "$APP_NAME" 55 | sudo docker volume rm csm-sentinel-persistent 56 | sudo rm -rf "$PLUGIN_INSTALL_PATH" 57 | whiptail --title "Uninstall finished" --msgbox "You have uninstalled $APP_NAME." 8 78 58 | fi 59 | } 60 | 61 | # Displays usage info 62 | function usage() { 63 | cat << EOF 64 | Usage: $(basename "$0") [-i] [-u] [-r] [-s] 65 | 66 | $APP_NAME Helper Script 67 | 68 | Options) 69 | -i Install $APP_NAME 70 | -u Upgrade $APP_NAME 71 | -r Remove $APP_NAME 72 | -s Start $APP_NAME 73 | -h Display help 74 | 75 | About $APP_NAME) 76 | - $DESCRIPTION 77 | - Source code: $SOURCE_CODE 78 | - Documentation: $DOCUMENTATION 79 | EOF 80 | } 81 | 82 | # Process command line options 83 | while getopts :iurhs opt; do 84 | case ${opt} in 85 | i ) install ;; 86 | u ) upgrade ;; 87 | r ) removeAll ;; 88 | s ) __start ;; 89 | h) 90 | usage 91 | exit 0 92 | ;; 93 | \?) 94 | echo "Invalid option: -${OPTARG}" >&2 95 | usage 96 | exit 1 97 | ;; 98 | :) 99 | echo "Option -${OPTARG} requires an argument." >&2 100 | usage 101 | exit 1 102 | ;; 103 | esac 104 | done 105 | -------------------------------------------------------------------------------- /ethdo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Author: coincashew.eth | coincashew.com 4 | # License: GNU GPL 5 | # Source: https://github.com/coincashew/ethpillar 6 | # Description: ethdo helper script 7 | # 8 | # Made for home and solo stakers 🏠🥩 9 | 10 | # Base directory with scripts 11 | BASE_DIR=$HOME/git/ethpillar 12 | 13 | # Load functions 14 | source $BASE_DIR/functions.sh 15 | 16 | # Get machine info 17 | _platform=$(get_platform) 18 | _arch=$(get_arch) 19 | 20 | # Variables 21 | GITHUB_URL=https://api.github.com/repos/wealdtech/ethdo/releases/latest 22 | GITHUB_RELEASE_NODES=https://github.com/wealdtech/ethdo/releases 23 | RELEASE_SUFFIX="${_platform}-${_arch}.tar.gz$" 24 | DESCRIPTION="A command-line tool for managing common tasks in Ethereum" 25 | DOCUMENTATION=https://github.com/wealdtech/ethdo/blob/master/docs/howto.md 26 | SOURCE_CODE=https://github.com/wealdtech/ethdo 27 | APP_NAME=ethdo 28 | APP_INSTALL_PATH="/usr/local/bin" 29 | VERSION=v$([[ -e ${APP_INSTALL_PATH}/ethdo ]] && ethdo version) 30 | 31 | # Asks to update 32 | function upgradeBinaries(){ 33 | getLatestVersion 34 | if whiptail --title "Update $APP_NAME" --yesno "Installed Version is: $VERSION\nLatest Version of $APP_NAME is: $TAG\n\nReminder: Always read the release notes for breaking changes: $GITHUB_RELEASE_NODES\n\nDo you want to update to $TAG?" 10 78; then 35 | downloadClient 36 | fi 37 | } 38 | 39 | # Gets latest tag 40 | function getLatestVersion(){ 41 | TAG=$(curl -s $GITHUB_URL | jq -r .tag_name ) 42 | # Exit in case of null tag 43 | [[ -z $TAG ]] || [[ $TAG == "null" ]] && echo "ERROR: Couldn't find the latest version tag" && exit 1 44 | } 45 | 46 | # Downloads latest release 47 | function downloadClient(){ 48 | BINARIES_URL="$(curl -s $GITHUB_URL | jq -r ".assets[] | select(.name) | .browser_download_url" | grep --ignore-case ${RELEASE_SUFFIX})" 49 | echo Downloading URL: $BINARIES_URL 50 | cd $HOME 51 | # Download 52 | wget -O $APP_NAME.tar.gz $BINARIES_URL 53 | # Untar 54 | tar -xzvf $APP_NAME.tar.gz -C $HOME 55 | # Cleanup 56 | rm $APP_NAME.tar.gz 57 | # Install binary 58 | sudo mv $HOME/$APP_NAME $APP_INSTALL_PATH 59 | } 60 | 61 | # Uninstall 62 | function removeAll() { 63 | if whiptail --title "Uninstall $APP_NAME" --defaultno --yesno "Are you sure you want to remove $APP_NAME" 9 78; then 64 | sudo rm $APP_INSTALL_PATH/$APP_NAME 65 | whiptail --title "Uninstall finished" --msgbox "You have uninstalled $APP_NAME." 8 78 66 | fi 67 | } 68 | 69 | # Displays usage info 70 | function usage() { 71 | cat << EOF 72 | Usage: $(basename "$0") [-i] [-u] [-r] 73 | 74 | $APP_NAME Helper Script 75 | 76 | Options) 77 | -i Install $APP_NAME binary 78 | -u Upgrade $APP_NAME 79 | -r Remove $APP_NAME 80 | -h Display help 81 | 82 | About $APP_NAME) 83 | - $DESCRIPTION 84 | - Source code: $SOURCE_CODE 85 | - Documentation: $DOCUMENTATION 86 | EOF 87 | } 88 | 89 | # Process command line options 90 | while getopts :iurh opt; do 91 | case ${opt} in 92 | i ) downloadClient ;; 93 | u ) upgradeBinaries ;; 94 | r ) removeAll ;; 95 | h) 96 | usage 97 | exit 0 98 | ;; 99 | \?) 100 | echo "Invalid option: -${OPTARG}" >&2 101 | usage 102 | exit 1 103 | ;; 104 | :) 105 | echo "Option -${OPTARG} requires an argument." >&2 106 | usage 107 | exit 1 108 | ;; 109 | esac 110 | done 111 | -------------------------------------------------------------------------------- /eth-duties.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Author: coincashew.eth | coincashew.com 4 | # License: GNU GPL 5 | # Source: https://github.com/coincashew/ethpillar 6 | # Description: eth-duties helper script 7 | # 8 | # Made for home and solo stakers 🏠🥩 9 | 10 | # Base directory with scripts 11 | BASE_DIR=$HOME/git/ethpillar 12 | 13 | # Load functions 14 | source $BASE_DIR/functions.sh 15 | 16 | # Get machine info 17 | _arch=$(get_arch) 18 | 19 | # Translate achitecture file name 20 | if [[ "${_arch}" == "arm64" ]]; then 21 | _arch=arm 22 | fi 23 | 24 | # Variables 25 | GITHUB_URL=https://api.github.com/repos/TobiWo/eth-duties/releases/latest 26 | GITHUB_RELEASE_NODES=https://github.com/TobiWo/eth-duties/releases 27 | RELEASE_SUFFIX="ubuntu24.04-${_arch}.tar.gz$" 28 | DESCRIPTION="eth-duties logs upcoming validator duties to the console. Developed mainly for home stakers." 29 | DOCUMENTATION=https://tobiwo.github.io/eth-duties 30 | SOURCE_CODE=https://github.com/TobiWo/eth-duties 31 | APP_NAME=eth-duties 32 | APP_INSTALL_PATH="/usr/local/bin" 33 | 34 | # Asks to update 35 | function upgradeBinaries(){ 36 | getLatestVersion 37 | if whiptail --title "Update $APP_NAME" --yesno "Latest Version of $APP_NAME is: $TAG\n\nReminder: Always read the release notes for breaking changes: $GITHUB_RELEASE_NODES\n\nDo you want to update to $TAG?" 10 78; then 38 | downloadClient 39 | fi 40 | } 41 | 42 | # Gets latest tag 43 | function getLatestVersion(){ 44 | TAG=$(curl -s $GITHUB_URL | jq -r .tag_name ) 45 | # Exit in case of null tag 46 | [[ -z $TAG ]] || [[ $TAG == "null" ]] && echo "ERROR: Couldn't find the latest version tag" && exit 1 47 | } 48 | 49 | # Downloads latest release 50 | function downloadClient(){ 51 | BINARIES_URL="$(curl -s $GITHUB_URL | jq -r ".assets[] | select(.name) | .browser_download_url" | grep ${RELEASE_SUFFIX})" 52 | echo Downloading URL: $BINARIES_URL 53 | cd $HOME 54 | # Download 55 | wget -O $APP_NAME.tar.gz $BINARIES_URL 56 | if [ ! -f $APP_NAME.tar.gz ]; then 57 | echo "Error: Downloading $APP_NAME.tar.gz failed!" 58 | exit 1 59 | fi 60 | # Untar 61 | tar -xzvf $APP_NAME.tar.gz -C $HOME --strip-components=2 62 | # Cleanup 63 | rm $APP_NAME.tar.gz 64 | # Install binary 65 | sudo mv $HOME/$APP_NAME $APP_INSTALL_PATH 66 | } 67 | 68 | # Uninstall 69 | function removeAll() { 70 | if whiptail --title "Uninstall $APP_NAME" --defaultno --yesno "Are you sure you want to remove $APP_NAME" 9 78; then 71 | sudo rm $APP_INSTALL_PATH/$APP_NAME 72 | whiptail --title "Uninstall finished" --msgbox "You have uninstalled $APP_NAME." 8 78 73 | fi 74 | } 75 | 76 | # Displays usage info 77 | function usage() { 78 | cat << EOF 79 | Usage: $(basename "$0") [-i] [-u] [-r] 80 | 81 | $APP_NAME Helper Script 82 | 83 | Options) 84 | -i Install $APP_NAME binary 85 | -u Upgrade $APP_NAME 86 | -r Remove $APP_NAME 87 | -h Display help 88 | 89 | About $APP_NAME) 90 | - $DESCRIPTION 91 | - Source code: $SOURCE_CODE 92 | - Documentation: $DOCUMENTATION 93 | EOF 94 | } 95 | 96 | # Process command line options 97 | while getopts :iurh opt; do 98 | case ${opt} in 99 | i ) downloadClient ;; 100 | u ) upgradeBinaries ;; 101 | r ) removeAll ;; 102 | h) 103 | usage 104 | exit 0 105 | ;; 106 | \?) 107 | echo "Invalid option: -${OPTARG}" >&2 108 | usage 109 | exit 1 110 | ;; 111 | :) 112 | echo "Option -${OPTARG} requires an argument." >&2 113 | usage 114 | exit 1 115 | ;; 116 | esac 117 | done 118 | -------------------------------------------------------------------------------- /helpers/install_unattendedupgrades.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Author: coincashew.eth | coincashew.com 3 | # License: GNU GPL 4 | # Source: https://github.com/coincashew/ethpillar 5 | # Description: EthPillar is a one-liner setup tool and node management TUI 6 | # 7 | # Made for home and solo stakers 🏠🥩 8 | 9 | # 🫶 Make improvements and suggestions on GitHub: 10 | # * https://github.com/coincashew/ethpillar 11 | # 🙌 Ask questions on Discord: 12 | # * https://discord.gg/dEpAVWgFNB 13 | 14 | set -e 15 | 16 | # Check if the user is root 17 | if [[ $EUID -ne 0 ]]; then 18 | echo "This script must be run as root. Please use sudo." 19 | exit 1 20 | fi 21 | 22 | clear 23 | echo "########################################################################################" 24 | echo "Unattended-upgrades: Automatically install security updates" 25 | echo "########################################################################################" 26 | echo "Key Points:" 27 | echo "* Automatic Updates: Installs security updates and important updates without requiring user input." 28 | echo "* Convenience: This is particularly helpful for managing nodes, eliminating the need for manual update processes." 29 | echo "* Security: By keeping systems up to date, this helps maintain system security and protect against vulnerabilities." 30 | echo "" 31 | echo "Do you wish to install? [y|n]" 32 | read -rsn1 yn 33 | if [[ ! ${yn} = [Yy]* ]]; then 34 | exit 0 35 | fi 36 | 37 | # Install required package 38 | apt-get update && apt-get install -y unattended-upgrades 39 | 40 | # Configure automatic security updates 41 | CONFIG_FILE="/etc/apt/apt.conf.d/50unattended-upgrades" 42 | BACKUP_FILE="${CONFIG_FILE}.bak" 43 | 44 | # Create backup of original config 45 | cp "${CONFIG_FILE}" "${BACKUP_FILE}" 46 | 47 | # Modify configuration using sed 48 | sed -i \ 49 | -e 's/^\/\/\s*"\${distro_id}:\${distro_codename}-security";/ "\${distro_id}:\${distro_codename}-security";/' \ 50 | -e 's/^\/\/\s*"origin=Debian";/ "origin=Ubuntu";/' \ 51 | -e 's/^\/\/Unattended-Upgrade::AutoFixInterruptedDpkg/Unattended-Upgrade::AutoFixInterruptedDpkg/' \ 52 | -e 's/^\/\/Unattended-Upgrade::Remove-Unused-Dependencies/Unattended-Upgrade::Remove-Unused-Dependencies/' \ 53 | "${CONFIG_FILE}" 54 | 55 | # Configure update intervals 56 | cat > /etc/apt/apt.conf.d/20auto-upgrades < -L 8545:localhost:8545 # or use a publicRPC 39 | rainbowkitProjectId: "***" # Get one from https://cloud.reown.com/, it's free, but still works without? 40 | 41 | beaconapi: 42 | # beacon node rpc endpoints 43 | endpoints: 44 | - name: "local" 45 | url: "http://127.0.0.1:5052" 46 | 47 | # local cache for page models 48 | localCacheSize: 100 # 100MB 49 | 50 | # remote cache for page models 51 | redisCacheAddr: "" 52 | redisCachePrefix: "" 53 | 54 | executionapi: 55 | # execution node rpc endpoints 56 | endpoints: 57 | - name: "local" 58 | url: "http://127.0.0.1:8545" 59 | 60 | logBatchSize: 1000 61 | depositDeployBlock: 0 # el block number from where to crawl the deposit contract (should be <=, but close to the deposit contract deployment block) 62 | electraDeployBlock: 0 # el block number from where to crawl the electra system contracts (should be <=, but close to electra fork activation block) 63 | 64 | # indexer keeps track of the latest epochs in memory. 65 | indexer: 66 | # max number of epochs to keep in memory 67 | inMemoryEpochs: 1 68 | 69 | # number of epochs to keep validator activity history for (high memory usage for large validator sets) 70 | activityHistoryLength: 0 71 | 72 | # disable synchronizing historic data 73 | disableSynchronizer: true 74 | 75 | # reset synchronization state to this epoch on startup - only use to resync database, comment out afterwards 76 | #resyncFromEpoch: 0 77 | 78 | # force re-synchronization of epochs that are already present in DB - only use to fix missing data after schema upgrades 79 | #resyncForceUpdate: true 80 | 81 | # number of seconds to pause the synchronization between each epoch (don't overload CL client) 82 | syncEpochCooldown: 2 83 | 84 | # maximum number of parallel beacon state requests (might cause high memory usage) 85 | maxParallelValidatorSetRequests: 1 86 | 87 | # database configuration 88 | database: 89 | engine: "sqlite" # sqlite / pgsql 90 | 91 | # sqlite settings (only used if engine is sqlite) 92 | sqlite: 93 | file: "./explorer-db.sqlite" 94 | 95 | # pgsql settings (only used if engine is pgsql) 96 | pgsql: 97 | host: "127.0.0.1" 98 | port: 5432 99 | user: "" 100 | password: "" 101 | name: "" 102 | pgsqlWriter: # optional separate writer connection (used for replication setups) 103 | host: "" 104 | port: 5432 105 | user: "" 106 | password: "" 107 | name: "" 108 | 109 | # separate block db for storing block bodies (no archive beacon node required) 110 | blockDb: 111 | engine: "none" # pebble / s3 / none (disable block db) 112 | 113 | # pebble settings (only used if engine is set to pebble) 114 | pebble: 115 | path: "./tmp-blockdb.peb" 116 | cacheSize: 100 # 100MB 117 | 118 | # s3 settings (only used if engine is set to s3) 119 | s3: 120 | bucket: "" 121 | endpoint: "" 122 | secure: false 123 | region: "" 124 | accessKey: "" 125 | secretKey: "" 126 | path: "" # path prefix -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | # MEV Relay Data 2 | mainnet_relay_options = [ 3 | {'name': 'Aestus', 'url': 'https://0xa15b52576bcbf1072f4a011c0f99f9fb6c66f3e1ff321f11f461d15e31b1cb359caa092c71bbded0bae5b5ea401aab7e@aestus.live'}, 4 | {'name': 'Agnostic Gnosis', 'url': 'https://0xa7ab7a996c8584251c8f925da3170bdfd6ebc75d50f5ddc4050a6fdc77f2a3b5fce2cc750d0865e05d7228af97d69561@agnostic-relay.net'}, 5 | {'name': 'bloXroute Max Profit', 'url': 'https://0x8b5d2e73e2a3a55c6c87b8b6eb92e0149a125c852751db1422fa951e42a09b82c142c3ea98d0d9930b056a3bc9896b8f@bloxroute.max-profit.blxrbdn.com'}, 6 | {'name': 'bloXroute Regulated', 'url': 'https://0xb0b07cd0abef743db4260b0ed50619cf6ad4d82064cb4fbec9d3ec530f7c5e6793d9f286c4e082c0244ffb9f2658fe88@bloxroute.regulated.blxrbdn.com'}, 7 | {'name': 'Flashbots', 'url': 'https://0xac6e77dfe25ecd6110b8e780608cce0dab71fdd5ebea22a16c0205200f2f8e2e3ad3b71d3499c54ad14d6c21b41a37ae@boost-relay.flashbots.net'}, 8 | {'name': 'Ultra Sound', 'url': 'https://0xa1559ace749633b997cb3fdacffb890aeebdb0f5a3b6aaa7eeeaf1a38af0a8fe88b9e4b1f61f236d2e64d95733327a62@relay.ultrasound.money'} 9 | ] 10 | 11 | holesky_relay_options = [ 12 | {'name': 'Aestus', 'url': 'https://0xab78bf8c781c58078c3beb5710c57940874dd96aef2835e7742c866b4c7c0406754376c2c8285a36c630346aa5c5f833@holesky.aestus.live'}, 13 | {'name': 'Ultra Sound', 'url': 'https://0xb1559beef7b5ba3127485bbbb090362d9f497ba64e177ee2c8e7db74746306efad687f2cf8574e38d70067d40ef136dc@relay-stag.ultrasound.money'}, 14 | {'name': 'Flashbots', 'url': 'https://0xafa4c6985aa049fb79dd37010438cfebeb0f2bd42b115b89dd678dab0670c1de38da0c4e9138c9290a398ecd9a0b3110@boost-relay-holesky.flashbots.net'}, 15 | {'name': 'bloXroute', 'url': 'https://0x821f2a65afb70e7f2e820a925a9b4c80a159620582c1766b1b09729fec178b11ea22abb3a51f07b288be815a1a2ff516@bloxroute.holesky.blxrbdn.com'}, 16 | {'name': 'Eden Network', 'url': 'https://0xb1d229d9c21298a87846c7022ebeef277dfc321fe674fa45312e20b5b6c400bfde9383f801848d7837ed5fc449083a12@relay-holesky.edennetwork.io'}, 17 | {'name': 'Titan Relay', 'url': 'https://0xaa58208899c6105603b74396734a6263cc7d947f444f396a90f7b7d3e65d102aec7e5e5291b27e08d02c50a050825c2f@holesky.titanrelay.xyz'} 18 | ] 19 | 20 | sepolia_relay_options = [ 21 | {'name': 'Flashbots', 'url': 'https://0x845bd072b7cd566f02faeb0a4033ce9399e42839ced64e8b2adcfc859ed1e8e1a5a293336a49feac6d9a5edb779be53a@boost-relay-sepolia.flashbots.net'} 22 | ] 23 | 24 | hoodi_relay_options = [ 25 | {'name': 'Titan Relay', 'url': 'https://0xaa58208899c6105603b74396734a6263cc7d947f444f396a90f7b7d3e65d102aec7e5e5291b27e08d02c50a050825c2f@hoodi.titanrelay.xyz'}, 26 | {'name': 'Flashbots', 'url': 'https://0xafa4c6985aa049fb79dd37010438cfebeb0f2bd42b115b89dd678dab0670c1de38da0c4e9138c9290a398ecd9a0b3110@boost-relay-hoodi.flashbots.net'}, 27 | {'name': 'bloXroute', 'url': 'https://0x821f2a65afb70e7f2e820a925a9b4c80a159620582c1766b1b09729fec178b11ea22abb3a51f07b288be815a1a2ff516@bloxroute.hoodi.blxrbdn.com'}, 28 | {'name': 'Aestus', 'url': 'https://0x98f0ef62f00780cf8eb06701a7d22725b9437d4768bb19b363e882ae87129945ec206ec2dc16933f31d983f8225772b6@hoodi.aestus.live'} 29 | ] 30 | 31 | # Checkpoint-Sync Data 32 | mainnet_sync_urls = [ 33 | ("ETHSTAKER", "https://beaconstate.ethstaker.cc"), 34 | ("BEACONCHA.IN", "https://sync-mainnet.beaconcha.in"), 35 | ("ATTESTANT", "https://mainnet-checkpoint-sync.attestant.io"), 36 | ("SIGMA PRIME", "https://mainnet.checkpoint.sigp.io"), 37 | ("Lodestar", "https://beaconstate-mainnet.chainsafe.io"), 38 | ("BeaconState.info", "https://beaconstate.info"), 39 | ("PietjePuk", "https://checkpointz.pietjepuk.net"), 40 | ("invistools", "https://sync.invis.tools"), 41 | ("Nimbus", "http://testing.mainnet.beacon-api.nimbus.team"), 42 | ] 43 | 44 | holesky_sync_urls = [ 45 | ("BEACONSTATE", "https://holesky.beaconstate.info"), 46 | ("EF DevOps", "https://checkpoint-sync.holesky.ethpandaops.io"), 47 | ("Lodestar", "https://beaconstate-holesky.chainsafe.io"), 48 | ] 49 | 50 | sepolia_sync_urls = [ 51 | ("Beaconstate", "https://sepolia.beaconstate.info"), 52 | ("Lodestar", "https://beaconstate-sepolia.chainsafe.io"), 53 | ("EF DevOps", "https://checkpoint-sync.sepolia.ethpandaops.io"), 54 | ] 55 | 56 | ephemery_sync_urls = [ 57 | ("ETHSTAKER", "https://ephemery.beaconstate.ethstaker.cc"), 58 | ("EF DevOps", "https://checkpoint-sync.ephemery.ethpandaops.io"), 59 | ] 60 | 61 | hoodi_sync_urls = [ 62 | ("EF DevOps", "https://checkpoint-sync.hoodi.ethpandaops.io"), 63 | ("ATTESTANT", "https://hoodi-checkpoint-sync.attestant.io"), 64 | ] -------------------------------------------------------------------------------- /update_mevboost.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Author: coincashew.eth | coincashew.com 4 | # License: GNU GPL 5 | # Source: https://github.com/coincashew/ethpillar 6 | # Description: EthPillar is a one-liner setup tool and node management TUI 7 | # 8 | # Made for home and solo stakers 🏠🥩 9 | 10 | BASE_DIR=$HOME/git/ethpillar 11 | 12 | # Load functions 13 | # shellcheck disable=SC1091 14 | source "$BASE_DIR"/functions.sh 15 | 16 | # Get machine info 17 | _platform=$(get_platform) 18 | _arch=$(get_arch) 19 | 20 | function getCurrentVersion(){ 21 | INSTALLED=$(mev-boost --version 2>&1) 22 | #Find version in format #.#.# 23 | if [[ $INSTALLED ]] ; then 24 | # shellcheck disable=SC2001 25 | VERSION=$(echo "$INSTALLED" | sed 's/.*\s\([0-9]*\.[0-9]*\).*/\1/') 26 | else 27 | VERSION="Client not installed." 28 | fi 29 | } 30 | 31 | function selectCustomTag(){ 32 | local _listTags _tag 33 | _listTags=$(curl -fsSL https://api.github.com/repos/flashbots/mev-boost/tags | jq -r '.[].name' | sort -hr) 34 | if [ -z "$_listTags" ]; then 35 | error "❌ Could not retrieve tags. Try again later." 36 | fi 37 | info "ℹ️ Select the Version: Type the number to use. For example, 2 (for the 2nd most recent release)" 38 | select _tag in $_listTags; do 39 | if [ -n "$_tag" ]; then 40 | __OTHERTAG=$_tag 41 | break 42 | else 43 | error "❌ Invalid input. Enter the line # corresponding to a tag." 44 | fi 45 | done 46 | } 47 | 48 | function promptYesNo(){ 49 | # Remove front v if present 50 | if [[ "${VERSION#v}" == "${TAG#v}" ]]; then 51 | whiptail --title "Already updated" --msgbox "You are already on the latest version: ${VERSION#v}" 10 78 52 | if whiptail --title "Different Version of mevboost" --defaultno --yesno "Would you like to install a different version?" 8 78; then 53 | selectCustomTag 54 | updateClient "$__OTHERTAG" 55 | promptViewLogs 56 | fi 57 | return 58 | fi 59 | __MSG="Installed Version is: ${VERSION#v}\nLatest Version is: ${TAG#v}\n\nReminder: Always read the release notes for breaking changes: $CHANGES_URL\n\nDo you want to update mevboost to ${TAG#v}?" 60 | __SELECTTAG=$(whiptail --title "🔧 Update mevboost" --menu \ 61 | "$__MSG" 18 78 2 \ 62 | "LATEST" "| Installs ${TAG#v}, the latest release" \ 63 | "OTHER " "| I will select a different version" \ 64 | 3>&1 1>&2 2>&3) 65 | if [ -z "$__SELECTTAG" ]; then exit; fi # pressed cancel 66 | if [[ $__SELECTTAG == "LATEST" ]]; then 67 | updateClient "LATEST" 68 | promptViewLogs 69 | else 70 | selectCustomTag 71 | updateClient "$__OTHERTAG" 72 | promptViewLogs 73 | fi 74 | } 75 | 76 | function promptViewLogs(){ 77 | if whiptail --title "Update complete" --yesno "Would you like to view logs and confirm everything is running properly?" 8 78; then 78 | sudo bash -c 'journalctl -fu mevboost | ccze -A' 79 | fi 80 | } 81 | 82 | function getLatestVersion(){ 83 | TAG_URL="https://api.github.com/repos/flashbots/mev-boost/releases/latest" 84 | #Get tag name and remove leading 'v' 85 | TAG=$(curl -s $TAG_URL | jq -r .tag_name | sed 's/.*v\([0-9]*\.[0-9]*\).*/\1/') 86 | # Exit in case of null tag 87 | [[ -z $TAG ]] || [[ $TAG == "null" ]] && echo "ERROR: Couldn't find the latest version tag" && exit 1 88 | CHANGES_URL="https://github.com/flashbots/mev-boost/releases" 89 | } 90 | 91 | function updateClient(){ 92 | if [[ "$1" == "LATEST" ]]; then 93 | _URL_SUFFIX="releases/latest" 94 | else 95 | _URL_SUFFIX="releases/tags/$1" 96 | fi 97 | RELEASE_URL="https://api.github.com/repos/flashbots/mev-boost/${_URL_SUFFIX}" 98 | BINARIES_URL="$(curl -s "$RELEASE_URL" | jq -r ".assets[] | select(.name) | .browser_download_url" | grep --ignore-case "${_platform}"_"${_arch}"\.tar\.gz$)" 99 | [[ -z "$BINARIES_URL" ]] && error "❌ Could not determine download URL for ${_platform}_${_arch}." 100 | info "ℹ️ Downloading URL: $BINARIES_URL" 101 | cd "$HOME" || true 102 | # Download 103 | wget -O mev-boost.tar.gz "$BINARIES_URL" || error "❌ Failed to download mev-boost binary." 104 | # Untar 105 | tar -xzvf mev-boost.tar.gz -C "$HOME" || error "❌ Failed to extract mev-boost archive." 106 | # Cleanup 107 | rm mev-boost.tar.gz LICENSE README.md 108 | sudo systemctl stop mevboost 109 | sudo mv "$HOME"/mev-boost /usr/local/bin || error "❌ Failed to move mev-boost binary to /usr/local/bin." 110 | sudo systemctl start mevboost 111 | } 112 | 113 | getCurrentVersion 114 | getLatestVersion 115 | promptYesNo -------------------------------------------------------------------------------- /client_requirements.py: -------------------------------------------------------------------------------- 1 | """ 2 | Client version requirements for Ethereum networks. 3 | 4 | This module defines minimum client versions required for specific fork activations 5 | and provides validation utilities to ensure compatibility. 6 | 7 | Networks requiring Fusaka fork (PeerDAS support): 8 | - Ephemery: Activates Fusaka at epoch 10 (resets every 28 days) 9 | - Hoodi: Fusaka active since epoch 50688 10 | """ 11 | 12 | # Minimum client versions for Fusaka fork (PeerDAS support) 13 | # Required for: Ephemery (active at epoch 10), Hoodi (active since epoch 50688) 14 | FUSAKA_MIN_VERSIONS = { 15 | # Consensus clients 16 | 'lighthouse': 'v8.0.0', 17 | 'teku': '25.9.3', 18 | 'nimbus': 'v25.9.2', 19 | 'lodestar': 'v1.35.0', 20 | 'prysm': 'v6.1.0', 21 | # Execution clients 22 | 'reth': 'v1.7.0', 23 | 'besu': '25.7.0', 24 | 'nethermind': 'v1.34.0', 25 | 'erigon': 'v3.2.1' 26 | } 27 | 28 | 29 | def parse_version(version_string): 30 | """ 31 | Pure function: Parse semantic version string into comparable parts. 32 | 33 | Args: 34 | version_string: Version string (e.g., 'v8.0.0', '25.9.3-rc.0') 35 | 36 | Returns: 37 | Tuple of (major, minor, patch, prerelease) 38 | 39 | Examples: 40 | >>> parse_version('v8.0.0') 41 | (8, 0, 0, None) 42 | >>> parse_version('25.9.3-rc.0') 43 | (25, 9, 3, 'rc.0') 44 | """ 45 | clean_version = version_string.lstrip('v') 46 | parts = clean_version.split('-') 47 | version_nums = parts[0].split('.') 48 | 49 | nums = [int(n) if n.isdigit() else 0 for n in version_nums] 50 | nums.extend([0] * (3 - len(nums))) # Pad to 3 elements 51 | 52 | prerelease = parts[1] if len(parts) > 1 else None 53 | return (*nums[:3], prerelease) 54 | 55 | 56 | def compare_versions(v1, v2): 57 | """ 58 | Pure function: Compare two semantic version strings. 59 | 60 | Args: 61 | v1: First version string 62 | v2: Second version string 63 | 64 | Returns: 65 | -1 if v1 < v2, 0 if equal, 1 if v1 > v2 66 | 67 | Examples: 68 | >>> compare_versions('v8.0.0', 'v7.1.0') 69 | 1 70 | >>> compare_versions('v8.0.0-rc.0', 'v8.0.0') 71 | -1 72 | """ 73 | v1_parts = parse_version(v1) 74 | v2_parts = parse_version(v2) 75 | 76 | # Compare major, minor, patch 77 | for a, b in zip(v1_parts[:3], v2_parts[:3]): 78 | if a < b: return -1 79 | if a > b: return 1 80 | 81 | # Compare prerelease (no prerelease > has prerelease) 82 | v1_pre, v2_pre = v1_parts[3], v2_parts[3] 83 | if v1_pre is None and v2_pre is not None: return 1 84 | if v1_pre is not None and v2_pre is None: return -1 85 | if v1_pre == v2_pre: return 0 86 | return -1 if v1_pre < v2_pre else 1 87 | 88 | 89 | def validate_version_for_network(client_name, version, network): 90 | """ 91 | Pure function: Validate if version meets network requirements. 92 | 93 | Args: 94 | client_name: Name of the client (e.g., 'lighthouse', 'reth') 95 | version: Version string to validate 96 | network: Network name (e.g., 'ephemery', 'hoodi', 'mainnet') 97 | 98 | Returns: 99 | Tuple of (is_valid: bool, error_message: str | None) 100 | 101 | Networks requiring Fusaka (PeerDAS): 102 | - Ephemery: Active at epoch 10 (resets every 28 days) 103 | - Hoodi: Active since epoch 50688 104 | 105 | Examples: 106 | >>> validate_version_for_network('lighthouse', 'v8.0.0', 'ephemery') 107 | (True, None) 108 | >>> validate_version_for_network('lighthouse', 'v7.1.0', 'ephemery') 109 | (False, 'ERROR: ...') 110 | >>> validate_version_for_network('lighthouse', 'v7.1.0', 'mainnet') 111 | (True, None) # Mainnet doesn't require Fusaka yet 112 | """ 113 | # Only validate for networks running Fusaka fork 114 | if network not in ["ephemery", "hoodi"]: 115 | return (True, None) 116 | 117 | min_version = FUSAKA_MIN_VERSIONS.get(client_name) 118 | if not min_version: 119 | return (True, None) 120 | 121 | if compare_versions(version, min_version) >= 0: 122 | return (True, None) 123 | 124 | error_msg = ( 125 | f"\nERROR: {client_name.capitalize()} {version} is not compatible with {network.capitalize()}\n" 126 | f"{network.capitalize()} requires Fusaka fork support (minimum version: {min_version})\n" 127 | f"The latest {client_name.capitalize()} release ({version}) does not meet this requirement.\n" 128 | f"\nPlease wait for a newer {client_name.capitalize()} release or choose a different network." 129 | ) 130 | return (False, error_msg) 131 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Author: coincashew.eth | coincashew.com 4 | # License: GNU GPL 5 | # Source: https://github.com/coincashew/ethpillar 6 | # 7 | # Made for home and solo stakers 🏠🥩 8 | 9 | # 🫶 Make improvements and suggestions on GitHub: 10 | # * https://github.com/coincashew/ethpillar 11 | # 🙌 Ask questions on Discord: 12 | # * https://discord.gg/dEpAVWgFNB 13 | 14 | set -u 15 | 16 | # enable command completion 17 | set -o history -o histexpand 18 | 19 | abort() { 20 | printf "%s\n" "$1" 21 | exit 1 22 | } 23 | 24 | getc() { 25 | local save_state 26 | save_state=$(/bin/stty -g) 27 | /bin/stty raw -echo 28 | IFS= read -r -n 1 -d '' "$@" 29 | /bin/stty "$save_state" 30 | } 31 | 32 | exit_on_error() { 33 | exit_code=$1 34 | last_command="${@:2}" 35 | if [ $exit_code -ne 0 ]; then 36 | >&2 echo "\"${last_command}\" command failed with exit code ${exit_code}." 37 | exit $exit_code 38 | fi 39 | } 40 | 41 | wait_for_user() { 42 | local c 43 | echo 44 | echo "Press RETURN to continue or any other key to abort" 45 | getc c 46 | # we test for \r and \n because some stuff does \r instead 47 | if ! [[ "$c" == $'\r' || "$c" == $'\n' ]]; then 48 | exit 1 49 | fi 50 | } 51 | 52 | shell_join() { 53 | local arg 54 | printf "%s" "$1" 55 | shift 56 | for arg in "$@"; do 57 | printf " " 58 | printf "%s" "${arg// /\ }" 59 | done 60 | } 61 | 62 | # string formatters 63 | if [[ -t 1 ]]; then 64 | tty_escape() { printf "\033[%sm" "$1"; } 65 | else 66 | tty_escape() { :; } 67 | fi 68 | tty_mkbold() { tty_escape "1;$1"; } 69 | tty_underline="$(tty_escape "4;39")" 70 | tty_blue="$(tty_mkbold 34)" 71 | tty_red="$(tty_mkbold 31)" 72 | tty_bold="$(tty_mkbold 39)" 73 | tty_reset="$(tty_escape 0)" 74 | 75 | ohai() { 76 | printf "${tty_blue}==>${tty_bold} %s${tty_reset}\n" "$(shell_join "$@")" 77 | } 78 | 79 | requirements_check() { 80 | # Check CPU architecture 81 | if ! [[ $(lscpu | grep -oE 'x86') || $(lscpu | grep -oE 'aarch64') ]]; then 82 | echo "This machine's CPU architecture is not yet supported." 83 | echo "Recommend using Intel-AMD x86 or arm64 systems for best experience." 84 | exit 1 85 | fi 86 | 87 | # Check operating system 88 | if ! [[ "$(uname)" == "Linux" ]]; then 89 | echo "This operating system is not yet supported." 90 | echo "Recommend installing Ubuntu Desktop 24.04+ LTS or Ubuntu Server 24.04+ LTS for best experience." 91 | exit 1 92 | fi 93 | } 94 | 95 | linux_install_pre() { 96 | sudo apt-get update 97 | sudo apt-get install --no-install-recommends --no-install-suggests -y curl git ccze bc tmux jq nano btop whiptail ufw 98 | exit_on_error $? 99 | } 100 | 101 | linux_install_installer() { 102 | ohai "Cloning ethpillar into ~/git/ethpillar" 103 | mkdir -p ~/git/ethpillar 104 | git clone https://github.com/coincashew/ethpillar.git ~/git/ethpillar/ 2> /dev/null || (cd ~/git/ethpillar ; git fetch origin main ; git checkout main ; git pull) 105 | chmod +x ~/git/ethpillar/*.sh 106 | ohai "Installing ethpillar" 107 | if [ -f /usr/local/bin/ethpillar ]; then 108 | sudo rm /usr/local/bin/ethpillar 109 | fi 110 | sudo ln -s ~/git/ethpillar/ethpillar.sh /usr/local/bin/ethpillar 111 | exit_on_error $? 112 | } 113 | 114 | # Check OS and CPU requirements 115 | requirements_check 116 | 117 | # Do install. 118 | OS="$(uname)" 119 | if [[ "$OS" == "Linux" ]]; then 120 | echo """ 121 | ███████╗████████╗██╗ ██╗██████╗ ██╗██╗ ██╗ █████╗ ██████╗ 122 | ██╔════╝╚══██╔══╝██║ ██║██╔══██╗██║██║ ██║ ██╔══██╗██╔══██╗ 123 | █████╗ ██║ ███████║██████╔╝██║██║ ██║ ███████║██████╔╝ 124 | ██╔══╝ ██║ ██╔══██║██╔═══╝ ██║██║ ██║ ██╔══██║██╔══██╗ 125 | ███████╗ ██║ ██║ ██║██║ ██║███████╗███████╗██║ ██║██║ ██║ 126 | ╚══════╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ 127 | 128 | - This is my node. There are many like it, but this one is mine. 129 | - coincashew 130 | """ 131 | ohai "This script will install a node management tool called 'ethpillar'" 132 | 133 | wait_for_user 134 | linux_install_pre 135 | linux_install_installer 136 | 137 | echo "" 138 | echo "" 139 | echo "######################################################################" 140 | echo "## ##" 141 | echo "## INSTALL COMPLETE - To run, type \"ethpillar\" ##" 142 | echo "## ##" 143 | echo "######################################################################" 144 | echo "" 145 | echo "" 146 | fi 147 | -------------------------------------------------------------------------------- /plugins/aztec/helper_root.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Author: coincashew.eth | coincashew.com 4 | # License: GNU GPL 5 | # Source: https://github.com/coincashew/ethpillar 6 | # Description: Aztec plugin inspired by https://github.com/cryptocattelugu/Aztec-Network 7 | # 8 | # Made for home and solo stakers 🏠🥩 9 | 10 | # Colors 11 | g="\033[32m" # Green 12 | r="\033[31m" # Red 13 | nc="\033[0m" # No-color 14 | bold="\033[1m" 15 | 16 | function info { 17 | echo -e "${g}INFO: $1${nc}" 18 | } 19 | 20 | function error { 21 | echo -e "${r}${bold}ERROR: $1${nc}" 22 | exit 1 23 | } 24 | 25 | PLUGIN_INSTALL_PATH=/opt/ethpillar/aztec 26 | source "$PLUGIN_INSTALL_PATH"/.env 27 | 28 | function installAztecCli() { 29 | info "🔧 Installing Aztec CLI, please wait ..." 30 | yes | bash -i <(curl -s https://install.aztec.network) 31 | # shellcheck disable=SC2016 32 | echo 'export PATH="$HOME/.aztec/bin:$PATH"' >> ~/.bashrc 33 | export PATH="$HOME/.aztec/bin:$PATH" 34 | # shellcheck disable=SC1090 35 | source ~/.bashrc >/dev/null 2>&1 36 | aztec-up >/dev/null 2>&1 37 | } 38 | 39 | # Runs as root. Workaround for aztec issue: Due to how we containerize our applications, we require your working directory to be somewhere within /root. 40 | info "🚦 Enabling and starting Docker service..." 41 | systemctl enable --now docker || error "Failed to enable/start Docker service" 42 | 43 | # Generate new keys 44 | rm -f /root/.aztec/keystore/key1.json 45 | cd /root || true 46 | 47 | if ! command -v aztec &>/dev/null; then 48 | installAztecCli 49 | if ! command -v aztec &>/dev/null; then 50 | error "Aztec CLI installation failed. Try again." 51 | fi 52 | info "✅ Aztec CLI installed." 53 | fi 54 | 55 | aztec validator-keys new --fee-recipient 0x0000000000000000000000000000000000000000000000000000000000000000 && echo " " 56 | KEYSTORE_FILE=~/.aztec/keystore/key1.json 57 | KEYSTORE_FILE_TARGET=/opt/ethpillar/aztec/keystore/key1.json 58 | 59 | echo "🔧 Update keystore location and permissions" 60 | mkdir -p /opt/ethpillar/aztec/keystore 61 | mv "$KEYSTORE_FILE" "$KEYSTORE_FILE_TARGET" || echo "Unable to move key1.json" 62 | chmod 644 "$KEYSTORE_FILE_TARGET" || echo "Unable update keystore permissions" 63 | info "⚠️ Backup this keystore file: /opt/ethpillar/aztec/keystore/key1.json" 64 | info "🔐 Contains private keys:" 65 | echo "======START OF FILE=====" 66 | cat /opt/ethpillar/aztec/keystore/key1.json 67 | echo "======END OF FILE=======" 68 | 69 | KEYSTORE_FILE_TARGET=/opt/ethpillar/aztec/keystore/key1.json 70 | NEW_ETH_PRIVATE_KEY=$(jq -r '.validators[0].attester.eth' "$KEYSTORE_FILE_TARGET") 71 | NEW_BLS_PRIVATE_KEY=$(jq -r '.validators[0].attester.bls' "$KEYSTORE_FILE_TARGET") 72 | NEW_PUBLIC_ADDRESS=$(cast wallet address "$NEW_ETH_PRIVATE_KEY") 73 | 74 | info "🔧 Update ETH address values in .env..." 75 | # shellcheck disable=SC2015 76 | [[ -n $NEW_ETH_PRIVATE_KEY ]] && sed -i "s/^VALIDATOR_PRIVATE_KEYS.*$/VALIDATOR_PRIVATE_KEYS=${NEW_ETH_PRIVATE_KEY}/" $PLUGIN_INSTALL_PATH/.env || error "Unable to set VALIDATOR_PRIVATE_KEYS" 77 | # shellcheck disable=SC2015 78 | [[ -n $NEW_PUBLIC_ADDRESS ]] && sed -i "s/^VALIDATOR_ADDRESS.*$/VALIDATOR_ADDRESS=${NEW_PUBLIC_ADDRESS}/" $PLUGIN_INSTALL_PATH/.env || error "Unable to set VALIDATOR_ADDRESS" 79 | # shellcheck disable=SC2015 80 | # COINBASE is block reward recipient. On mainnet, use a unique hardware wallet secured ETH address. 81 | [[ -n $NEW_PUBLIC_ADDRESS ]] && sed -i "s/^COINBASE.*$/COINBASE=${NEW_PUBLIC_ADDRESS}/" $PLUGIN_INSTALL_PATH/.env || error "Unable to set COINBASE" 82 | 83 | if [[ $__SELECT == "EXISTING" ]]; then 84 | echo "⚠️ Please provide your old validator info." 85 | # shellcheck disable=SC2162 86 | read -sp " Enter your OLD Sequencer Private Key (will not be shown): " PRIVATE_KEY 87 | else 88 | echo "⚠️ You need to send 200k STAKES to your new address:" 89 | echo " $NEW_PUBLIC_ADDRESS" 90 | # shellcheck disable=SC2162 91 | read -p " After the funding transaction is confirmed, press [Enter] to continue.." && echo " " 92 | PRIVATE_KEY="$NEW_ETH_PRIVATE_KEY" 93 | fi 94 | 95 | echo "⚠️ You need to send 0.2 to 0.5 Sepolia ETH to your new address:" 96 | echo " $NEW_PUBLIC_ADDRESS" 97 | # shellcheck disable=SC2162 98 | read -p " After the funding transaction is confirmed, press [Enter] to continue.." && echo " " 99 | 100 | ROLLUP_ADDRESS="0xebd99ff0ff6677205509ae73f93d0ca52ac85d67" 101 | STAKE_CONTRACT="0x139d2a7a0881e16332d7D1F8DB383A4507E1Ea7A" 102 | STAKE_AMOUNT="200000ether" 103 | 104 | echo "Approving STAKE spending..." 105 | cast send "$STAKE_CONTRACT" "approve(address,uint256)" "$ROLLUP_ADDRESS" "$STAKE_AMOUNT" --private-key "$PRIVATE_KEY" --rpc-url "$ETHEREUM_HOSTS" 106 | 107 | aztec add-l1-validator \ 108 | --l1-rpc-urls "$ETHEREUM_HOSTS" \ 109 | --network testnet \ 110 | --private-key "$PRIVATE_KEY" \ 111 | --attester "$NEW_PUBLIC_ADDRESS" \ 112 | --withdrawer "$NEW_PUBLIC_ADDRESS" \ 113 | --bls-secret-key "$NEW_BLS_PRIVATE_KEY" \ 114 | --rollup "$ROLLUP_ADDRESS" 115 | 116 | # shellcheck disable=SC2181 117 | if [ $? -ne 0 ]; then 118 | error "aztec add-l1-validator failed. Unable to register validator $NEW_PUBLIC_ADDRESS. Try again later." 119 | fi 120 | 121 | exit 0 122 | -------------------------------------------------------------------------------- /plugins/sentinel/sentinel-installer.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Author: coincashew.eth | coincashew.com 4 | # License: GNU GPL 5 | # Source: https://github.com/coincashew/ethpillar 6 | # Description: csm-sentinel installer script 7 | # 8 | # Made for home and solo stakers 🏠🥩 9 | 10 | set -e 11 | 12 | MSG_ABOUT="About: CSM Sentinel is a telegram bot that sends you notifications for your CSM Node Operator events. 13 | \nMaintainer: This bot was developed and is maintained by @skhomuti, a member of the Lido Protocol community, to simplify the process of subscribing to the important events for CSM. 14 | \nLocally ran instance: This bot will run on your node, self-hosted for your own improved reliability and privacy. 15 | \nSource Code: https://github.com/skhomuti/csm-sentinel 16 | \nContinue to install?" 17 | 18 | # Intro screen 19 | if ! whiptail --title "CSM Sentinel: Running your own instance" --yesno "$MSG_ABOUT" 20 78; then exit; fi 20 | 21 | # Get network 22 | _NETWORK=$(whiptail --title "Set Network" --menu \ 23 | "For which network are you running CSM Sentinel?" 10 78 2 \ 24 | "mainnet" "ethereum" \ 25 | "hoodi" "testnet" \ 26 | 3>&1 1>&2 2>&3) 27 | 28 | MSG_TOKEN="First, you need to create a bot on Telegram. 29 | \nTo create a bot, initiate a conversation with @BotFather (https://t.me/botfather), select the 'New Bot' option, and follow the prompts to set up your bot's name, username, and initial settings. 30 | \nEnter the token you received from the BotFather. 31 | \nExample: 270485614:AAHfiqksKZ8WmR2zSjiQ7_v4TMAKdiHm9T0" 32 | 33 | # Get token 34 | while true; do 35 | _TOKEN=$(whiptail --title "Set Token from BotFather" --inputbox "$MSG_TOKEN" 17 78 --ok-button "Submit" 3>&1 1>&2 2>&3) 36 | if [ -z "$_TOKEN" ]; then exit; fi #pressed cancel 37 | if [[ "${_TOKEN}" =~ [0-9]+:.* ]]; then 38 | break 39 | else 40 | whiptail --title "Error" --msgbox "Invalid TOKEN. Try again." 8 78 41 | fi 42 | done 43 | 44 | MSG_WEB3_SOCKET_PROVIDER="The websocket provider for your node. 45 | \nPreferably, use your own local execution client node e.g. you already have for CSM validators. 46 | \nDefault example: ws://127.0.0.1:8545" 47 | 48 | # Get token 49 | _WEB3_SOCKET_PROVIDER=$(whiptail --title "Set WEB3_SOCKET_PROVIDER" --inputbox "$MSG_WEB3_SOCKET_PROVIDER" 15 78 "ws://127.0.0.1:8545" --ok-button "Submit" 3>&1 1>&2 2>&3) 50 | if [ -z "$_WEB3_SOCKET_PROVIDER" ]; then exit; fi #pressed cancel 51 | 52 | # Install packages 53 | apt-get update 54 | apt-get upgrade -y 55 | 56 | install_docker() { 57 | # Install Docker 58 | sudo apt-get install --yes docker.io 59 | # Verify that we can at least get version output 60 | if ! docker --version; then 61 | echo "ERROR: Is Docker installed?" 62 | exit 1 63 | fi 64 | } 65 | 66 | if ! command -v docker &> /dev/null; then 67 | install_docker 68 | fi 69 | 70 | # Setup files 71 | mkdir -p /opt/ethpillar/plugin-sentinel 72 | cd /opt/ethpillar/plugin-sentinel 73 | if [[ ! -d /opt/ethpillar/plugin-sentinel/csm-sentinel ]]; then 74 | git clone https://github.com/skhomuti/csm-sentinel 75 | cd csm-sentinel 76 | else 77 | cd csm-sentinel && git pull 78 | fi 79 | 80 | # Build image 81 | docker build -t csm-sentinel . 82 | docker volume create csm-sentinel-persistent 83 | 84 | # Create env file 85 | case $_NETWORK in 86 | mainnet) 87 | cat << EOF > /opt/ethpillar/plugin-sentinel/csm-sentinel/.env 88 | FILESTORAGE_PATH=.storage 89 | TOKEN=${_TOKEN} 90 | WEB3_SOCKET_PROVIDER=${_WEB3_SOCKET_PROVIDER} 91 | CSM_ADDRESS=0xdA7dE2ECdDfccC6c3AF10108Db212ACBBf9EA83F 92 | ACCOUNTING_ADDRESS=0x4d72BFF1BeaC69925F8Bd12526a39BAAb069e5Da 93 | FEE_DISTRIBUTOR_ADDRESS=0xD99CC66fEC647E68294C6477B40fC7E0F6F618D0 94 | EXIT_PENALTIES_ADDRESS=0x06cd61045f958A209a0f8D746e103eCc625f4193 95 | PARAMETERS_REGISTRY_ADDRESS=0x9D28ad303C90DF524BA960d7a2DAC56DcC31e428 96 | VEBO_ADDRESS=0x0De4Ea0184c2ad0BacA7183356Aea5B8d5Bf5c6e 97 | CSM_STAKING_MODULE_ID=3 98 | ETHERSCAN_URL=https://etherscan.io 99 | BEACONCHAIN_URL=https://beaconcha.in 100 | CSM_UI_URL=https://csm.lido.fi/?ref=ethpillar 101 | BLOCK_BATCH_SIZE=10000 102 | BLOCK_FROM= 103 | ADMIN_IDS= 104 | EOF 105 | ;; 106 | hoodi) 107 | cat << EOF > /opt/ethpillar/plugin-sentinel/csm-sentinel/.env 108 | FILESTORAGE_PATH=.storage 109 | TOKEN=${_TOKEN} 110 | WEB3_SOCKET_PROVIDER=${_WEB3_SOCKET_PROVIDER} 111 | CSM_ADDRESS=0x79CEf36D84743222f37765204Bec41E92a93E59d 112 | ACCOUNTING_ADDRESS=0xA54b90BA34C5f326BC1485054080994e38FB4C60 113 | FEE_DISTRIBUTOR_ADDRESS=0xaCd9820b0A2229a82dc1A0770307ce5522FF3582 114 | EXIT_PENALTIES_ADDRESS=0xD259b31083Be841E5C85b2D481Cfc17C14276800 115 | PARAMETERS_REGISTRY_ADDRESS=0xA4aD5236963f9Fe4229864712269D8d79B65C5Ad 116 | VEBO_ADDRESS=0x8664d394C2B3278F26A1B44B967aEf99707eeAB2 117 | CSM_STAKING_MODULE_ID=4 118 | ETHERSCAN_URL=https://hoodi.etherscan.io 119 | BEACONCHAIN_URL=https://hoodi.beaconcha.in 120 | CSM_UI_URL=https://csm.testnet.fi/?ref=ethpillar 121 | BLOCK_BATCH_SIZE=10000 122 | BLOCK_FROM= 123 | ADMIN_IDS= 124 | EOF 125 | ;; 126 | *) 127 | echo "Unsupported network" 128 | exit 1 129 | ;; 130 | esac 131 | 132 | # Start docker container 133 | cd /opt/ethpillar/plugin-sentinel/csm-sentinel 134 | sudo docker run -d --env-file=.env --name csm-sentinel -v csm-sentinel-persistent:/app/.storage csm-sentinel 135 | 136 | MSG_COMPLETE="Done! Congratulations on your new locally hosted CSM Sentinel bot. 137 | \nYou will find it at t.me/[YOUR-BOT-NAME]. 138 | Follow your Node Operator id." 139 | 140 | # Intro screen 141 | whiptail --title "CSM Sentinel: Install Complete" --msgbox "$MSG_COMPLETE" 10 78 142 | -------------------------------------------------------------------------------- /plugins/eth-validator-cli/plugin_eth-validator-cli.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Author: coincashew.eth | coincashew.com 4 | # License: GNU GPL 5 | # Source: https://github.com/coincashew/ethpillar 6 | # Description: Contributoor helper script 7 | # 8 | # Made for home and solo stakers 🏠🥩 9 | 10 | SOURCE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 11 | 12 | # Variables 13 | RELEASE_URL="https://api.github.com/repos/TobiWo/eth-valctl/releases/latest" 14 | GITHUB_RELEASE_NODES="https://github.com/TobiWo/eth-valctl/releases" 15 | DESCRIPTION="🔧 eth-validator-cli: CLI tool for managing Ethereum validators via execution layer requests." 16 | DOCUMENTATION="https://github.com/TobiWo/eth-valctl" 17 | SOURCE_CODE="https://github.com/TobiWo/eth-valctl" 18 | APP_NAME="eth-validator-cli" 19 | PLUGIN_INSTALL_PATH="/opt/ethpillar/plugin-eth-validator-cli" 20 | PLUGIN_SOURCE_PATH="$SOURCE_DIR" 21 | 22 | # Gets latest tag 23 | function getLatestVersion(){ 24 | TAG=$(curl -s $RELEASE_URL | jq -r .tag_name ) 25 | if [[ -z "$TAG" ]]; then echo "Failed to fetch latest version"; exit 1; fi 26 | } 27 | 28 | # Downloads latest release 29 | function downloadClient(){ 30 | local _custom 31 | # Handle custom naming convention 32 | [[ "$_arch" == "amd64" ]] && _custom="x64" 33 | if [[ -z "$_custom" ]]; then 34 | echo "Unsupported architecture: $_arch" 35 | exit 1 36 | fi 37 | json=$(curl -s $RELEASE_URL) || true 38 | BINARIES_URL=$(echo "$json" | jq -r ".assets[] | select(.name) | .browser_download_url" | grep --ignore-case "${_platform}"-"${_custom}") 39 | if [[ -z "$BINARIES_URL" ]]; then 40 | echo "Error: No download URL found for ${_platform}-${_custom}" 41 | exit 1 42 | fi 43 | echo Downloading URL: "$BINARIES_URL" 44 | # Make temporary directory 45 | TEMP_DIR=$(mktemp -d) 46 | # Download 47 | wget -O "$TEMP_DIR"/"$APP_NAME".tar.gz "$BINARIES_URL" 48 | # Untar 49 | tar -xzvf "$TEMP_DIR"/"$APP_NAME".tar.gz -C "$TEMP_DIR" 50 | # Install binary 51 | sudo mv "$TEMP_DIR"/"$APP_NAME" $PLUGIN_INSTALL_PATH/ && sudo chmod +x $PLUGIN_INSTALL_PATH/$APP_NAME 52 | # Store current version 53 | TAG=$(echo "$json" | jq -r .tag_name ) 54 | echo "$TAG" | sudo tee $PLUGIN_INSTALL_PATH/current_version 55 | # Cleanup 56 | rm -rf "$TEMP_DIR" 57 | } 58 | 59 | # Upgrade function 60 | function upgrade(){ 61 | getLatestVersion 62 | VERSION=$(cat $PLUGIN_INSTALL_PATH/current_version) 63 | [[ "${VERSION#v}" == "${TAG#v}" ]] && whiptail --title "Already updated" --msgbox "You are already on the latest version: $VERSION" 10 78 && return 64 | if whiptail --title "Update $APP_NAME" --yesno "Installed Version is: $VERSION\nLatest Version is: $TAG\n\nReminder: Always read the release notes for breaking changes: $GITHUB_RELEASE_NODES\n\nDo you want to update to $TAG?" 12 78; then 65 | downloadClient 66 | fi 67 | } 68 | 69 | # Installation function 70 | function install(){ 71 | MSG_ABOUT="🔧 eth-validator-cli by TobiWo: CLI tool for managing validators via execution layer requests 72 | \nFeatures: 73 | - Consolidate one or multiple source validators to one target validator 74 | - Switch withdrawal credentials from type 0x01 to 0x02 (compounding) for one or multiple validators 75 | - Partially withdraw ETH from one or many validators 76 | - Exit one or many validators 77 | - This cli currently only supports validator related features included in the Pectra hardfork. 78 | - The tool is especially useful if you need to manage multiple validators at once. 79 | - Currently it only supports private keys as secret. This will change soon with e.g. hardware ledger support. 80 | - ⚠️ Tool is very early. Use on Hoodi only. Not recommend to use it on mainnet yet! 81 | \nDocumentation: $DOCUMENTATION 82 | Source Code: $SOURCE_CODE 83 | \nContinue to install?" 84 | 85 | # Intro screen 86 | if ! whiptail --title "$APP_NAME: Installation" --yesno "$MSG_ABOUT" 28 78; then exit; fi 87 | 88 | # Setup installation directory 89 | sudo mkdir -p $PLUGIN_INSTALL_PATH 90 | 91 | # Install binaries 92 | downloadClient 93 | 94 | # Install env file 95 | sudo cp "$PLUGIN_SOURCE_PATH"/env.example $PLUGIN_INSTALL_PATH/env 96 | 97 | # Update permissions 98 | sudo chmod -R 755 "$PLUGIN_SOURCE_PATH" 99 | 100 | MSG_COMPLETE="Done! $APP_NAME is now installed. 101 | \nNext Steps: 102 | \n1. Study the documentation > $DOCUMENTATION 103 | \n2. Review env configuration, change if needed 104 | \n3. Practice on hoodi testnet before mainnet" 105 | 106 | # Installation complete screen 107 | whiptail --title "$APP_NAME: Install Complete" --msgbox "$MSG_COMPLETE" 17 78 108 | } 109 | 110 | # Uninstall 111 | function removeAll() { 112 | if whiptail --title "Uninstall $APP_NAME" --defaultno --yesno "Are you sure you want to remove $APP_NAME" 9 78; then 113 | sudo rm -rf "$PLUGIN_INSTALL_PATH" 114 | whiptail --title "Uninstall finished" --msgbox "You have uninstalled $APP_NAME." 8 78 115 | fi 116 | } 117 | 118 | # Displays usage info 119 | function usage() { 120 | cat << EOF 121 | Usage: $(basename "$0") [-i] [-u] [-r] 122 | 123 | $APP_NAME Helper Script 124 | 125 | Options: 126 | -i Install $APP_NAME 127 | -u Upgrade $APP_NAME 128 | -r Remove $APP_NAME 129 | -h Display help 130 | 131 | About $APP_NAME) 132 | - $DESCRIPTION 133 | - Source code: $SOURCE_CODE 134 | - Documentation: $DOCUMENTATION 135 | EOF 136 | } 137 | 138 | # Process command line options 139 | while getopts :iurh opt; do 140 | case ${opt} in 141 | i ) install ;; 142 | u ) upgrade ;; 143 | r ) removeAll ;; 144 | h) 145 | usage 146 | exit 0 147 | ;; 148 | \?) 149 | echo "Invalid option: -${OPTARG}" >&2 150 | usage 151 | exit 1 152 | ;; 153 | :) 154 | echo "Option -${OPTARG} requires an argument." >&2 155 | usage 156 | exit 1 157 | ;; 158 | esac 159 | done 160 | -------------------------------------------------------------------------------- /plugins/contributoor/plugin_contributoor.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Author: coincashew.eth | coincashew.com 4 | # License: GNU GPL 5 | # Source: https://github.com/coincashew/ethpillar 6 | # Description: Contributoor helper script 7 | # 8 | # Made for home and solo stakers 🏠🥩 9 | 10 | SOURCE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 11 | 12 | # Variables 13 | RELEASE_URL="https://api.github.com/repos/ethpandaops/contributoor/releases/latest" 14 | GITHUB_RELEASE_NODES="https://github.com/ethpandaops/contributoor/releases" 15 | DESCRIPTION="🐼 Contributoor: a monitoring and data-gathering tool. improve Ethereum’s network visibility. runs seamlessly alongside your beacon node" 16 | DOCUMENTATION="https://ethpandaops.io/posts/contribute-to-xatu-data" 17 | SOURCE_CODE="https://github.com/ethpandaops/contributoor" 18 | APP_NAME="contributoor" 19 | PLUGIN_INSTALL_PATH="/opt/ethpillar/plugin-contributoor" 20 | PLUGIN_SOURCE_PATH="$SOURCE_DIR" 21 | SERVICE_NAME="contributoor" 22 | SERVICE_ACCOUNT="contributoor" 23 | SERVICE_FILE="/etc/systemd/system/contributoor.service" 24 | 25 | # Gets latest tag 26 | function getLatestVersion(){ 27 | TAG=$(curl -s $RELEASE_URL | jq -r .tag_name ) 28 | } 29 | 30 | # Downloads latest release 31 | function downloadClient(){ 32 | json=$(curl -s $RELEASE_URL) || true 33 | BINARIES_URL=$(echo "$json" | jq -r ".assets[] | select(.name) | .browser_download_url" | grep --ignore-case "${_platform}"_"${_arch}") 34 | echo Downloading URL: "$BINARIES_URL" 35 | # Make temporary directory 36 | TEMP_DIR=$(mktemp -d) 37 | # Download 38 | wget -O "$TEMP_DIR"/$APP_NAME.tar.gz "$BINARIES_URL" 39 | # Untar 40 | tar -xzvf "$TEMP_DIR"/$APP_NAME.tar.gz -C "$TEMP_DIR" 41 | # Install binary 42 | sudo mv "$TEMP_DIR"/sentry $PLUGIN_INSTALL_PATH/ && sudo chmod +x $PLUGIN_INSTALL_PATH/sentry 43 | # Store current version 44 | TAG=$(echo "$json" | jq -r .tag_name ) 45 | echo "$TAG" | sudo tee $PLUGIN_INSTALL_PATH/current_version 46 | # Cleanup 47 | rm -rf "$TEMP_DIR" 48 | } 49 | 50 | # Upgrade function 51 | function upgrade(){ 52 | getLatestVersion 53 | VERSION=$(cat $PLUGIN_INSTALL_PATH/current_version) 54 | [[ "${VERSION#v}" == "${TAG#v}" ]] && whiptail --title "Already updated" --msgbox "You are already on the latest version: $VERSION" 10 78 && return 55 | if whiptail --title "Update $APP_NAME" --yesno "Installed Version is: $VERSION\nLatest Version is: $TAG\n\nReminder: Always read the release notes for breaking changes: $GITHUB_RELEASE_NODES\n\nDo you want to update to $TAG?" 12 78; then 56 | sudo systemctl stop $SERVICE_NAME 57 | downloadClient 58 | sudo systemctl start $SERVICE_NAME 59 | fi 60 | } 61 | 62 | # Installation function 63 | function install(){ 64 | MSG_ABOUT="🐼 Contributoor by ethpandaops.io is a powerful monitoring and data-gathering tool designed to enhance Ethereum's network transparency 65 | \nFeatures: 66 | \n- A lightweight service that operates with an Ethereum consensus client. 67 | \n- Runs seamlessly alongside your consensus node, gathering data through the client's APIs. 68 | \n- A simplified, user-friendly version of the sentry service from ethpandaops/xatu. 69 | \n- Data is published openly and privately for research and analysis. 70 | \nDocumentation: $DOCUMENTATION 71 | Source Code: $SOURCE_CODE 72 | \nContinue to install?" 73 | 74 | # Intro screen 75 | if ! whiptail --title "Contributoor: Installation" --yesno "$MSG_ABOUT" 26 78; then exit; fi 76 | 77 | # Create service user 78 | sudo useradd --no-create-home --shell /bin/false $SERVICE_ACCOUNT 79 | 80 | # Install service file 81 | sudo cp "$PLUGIN_SOURCE_PATH"/${SERVICE_NAME}.service.example $SERVICE_FILE 82 | sudo systemctl daemon-reload 83 | 84 | # Setup installation directory 85 | sudo mkdir -p $PLUGIN_INSTALL_PATH 86 | 87 | # Install binaries 88 | downloadClient 89 | 90 | # Install config file 91 | sudo cp "$PLUGIN_SOURCE_PATH"/config.yaml.example $PLUGIN_INSTALL_PATH/config.yaml 92 | 93 | # Update permissions 94 | sudo chown $SERVICE_ACCOUNT:$SERVICE_ACCOUNT -R $PLUGIN_INSTALL_PATH 95 | 96 | MSG_COMPLETE="Done! Contributoor is now installed. 97 | \nNext Steps: 98 | \n1. Signup for Contributoor credentials > $DOCUMENTATION 99 | \n2. Edit your config.yaml: Add credentials, metrics(optional), healthcheck(optional) 100 | \n3. Start contributoor service" 101 | 102 | # Installation complete screen 103 | whiptail --title "Contributoor: Install Complete" --msgbox "$MSG_COMPLETE" 17 78 104 | } 105 | 106 | # Uninstall 107 | function removeAll() { 108 | if whiptail --title "Uninstall $APP_NAME" --defaultno --yesno "Are you sure you want to remove $APP_NAME" 9 78; then 109 | sudo systemctl stop $SERVICE_NAME 110 | sudo systemctl disable $SERVICE_NAME 111 | sudo rm $SERVICE_FILE 112 | sudo userdel $SERVICE_ACCOUNT 113 | sudo rm -rf "$PLUGIN_INSTALL_PATH" 114 | whiptail --title "Uninstall finished" --msgbox "You have uninstalled $APP_NAME." 8 78 115 | fi 116 | } 117 | 118 | # Displays usage info 119 | function usage() { 120 | cat << EOF 121 | Usage: $(basename "$0") [-i] [-u] [-r] 122 | 123 | $APP_NAME Helper Script 124 | 125 | Options) 126 | -i Install $APP_NAME 127 | -u Upgrade $APP_NAME 128 | -r Remove $APP_NAME 129 | -h Display help 130 | 131 | About $APP_NAME) 132 | - $DESCRIPTION 133 | - Source code: $SOURCE_CODE 134 | - Documentation: $DOCUMENTATION 135 | EOF 136 | } 137 | 138 | # Process command line options 139 | while getopts :iurh opt; do 140 | case ${opt} in 141 | i ) install ;; 142 | u ) upgrade ;; 143 | r ) removeAll ;; 144 | h) 145 | usage 146 | exit 0 147 | ;; 148 | \?) 149 | echo "Invalid option: -${OPTARG}" >&2 150 | usage 151 | exit 1 152 | ;; 153 | :) 154 | echo "Option -${OPTARG} requires an argument." >&2 155 | usage 156 | exit 1 157 | ;; 158 | esac 159 | done 160 | -------------------------------------------------------------------------------- /plugins/eth-validator-cli/menu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Author: coincashew.eth | coincashew.com 4 | # License: GNU GPL 5 | # Source: https://github.com/coincashew/ethpillar 6 | # Description: Contributoor helper script 7 | # 8 | # Made for home and solo stakers 🏠🥩 9 | 10 | set -euo pipefail 11 | 12 | # Colors 13 | g="\033[32m" # Green 14 | r="\033[31m" # Red 15 | nc="\033[0m" # No-color 16 | bold="\033[1m" 17 | 18 | function info { 19 | echo -e "${g}INFO: $1${nc}" 20 | } 21 | 22 | function error { 23 | echo -e "${r}${bold}ERROR: $1${nc}" 24 | } 25 | 26 | function question { 27 | read -p "$1" -r 28 | echo "$REPLY" 29 | } 30 | 31 | # Load env variables 32 | PLUGIN_INSTALL_PATH=/opt/ethpillar/plugin-eth-validator-cli 33 | [[ -f $PLUGIN_INSTALL_PATH/env ]] && source $PLUGIN_INSTALL_PATH/env 34 | [[ -f $PLUGIN_INSTALL_PATH/current_version ]] && VERSION=$(cat $PLUGIN_INSTALL_PATH/current_version) 35 | 36 | # Validate that required env vars are set 37 | : "${JSON_RPC_URL:?JSON_RPC_URL must be set}" 38 | : "${BEACON_API_URL:?BEACON_API_URL must be set}" 39 | : "${MAX_REQUESTS_PER_BLOCK:?MAX_REQUESTS_PER_BLOCK must be set}" 40 | 41 | global_options=( 42 | --network="${NETWORK,,}" 43 | --beacon-api-url="$BEACON_API_URL" 44 | --json-rpc-url="$JSON_RPC_URL" 45 | --max-requests-per-block="$MAX_REQUESTS_PER_BLOCK" 46 | ) 47 | 48 | function consolidateCommand(){ 49 | local s t 50 | s=$(question "Space separated list of validator pubkeys which will be consolidated into the target validator: ") 51 | t=$(question "Target validator pubkey: ") 52 | cli_options=() 53 | cli_options+=(--target="$t") 54 | cli_options+=(--source="$s") 55 | local cmd=( "$PLUGIN_INSTALL_PATH/eth-validator-cli" "consolidate" "${global_options[@]}" "${cli_options[@]}" ) 56 | info "Executing command > ${cmd[*]}" 57 | yn=$(question "Please double-check your inputs before executing a command. Is the above correct? [y|n]") 58 | if [[ $yn = [Yy]* ]]; then 59 | "${cmd[@]}" || error "Error running command" 60 | fi 61 | read -p "Press ENTER to return to menu" -r 62 | } 63 | 64 | function exitCommand(){ 65 | local v 66 | v=$(question "Space separated list of validator pubkeys which will be exited: ") 67 | cli_options=() 68 | cli_options+=(--validator="$v") 69 | local cmd=( "$PLUGIN_INSTALL_PATH/eth-validator-cli" "exit" "${global_options[@]}" "${cli_options[@]}" ) 70 | info "Executing command > ${cmd[*]}" 71 | yn=$(question "Please double-check your inputs before executing a command. Is the above correct? [y|n]") 72 | if [[ $yn = [Yy]* ]]; then 73 | "${cmd[@]}" || error "Error running command" 74 | fi 75 | read -p "Press ENTER to return to menu" -r 76 | } 77 | 78 | function switchWithdrawalCredentialTypeCommand(){ 79 | local v 80 | v=$(question "Space separated list of validator pubkeys for which the withdrawal credential type will be changed to 0x02: ") 81 | cli_options=() 82 | cli_options+=(--validator="$v") 83 | local cmd=( "$PLUGIN_INSTALL_PATH/eth-validator-cli" "switch" "${global_options[@]}" "${cli_options[@]}" ) 84 | info "Executing command > ${cmd[*]}" 85 | yn=$(question "Please double-check your inputs before executing a command. Is the above correct? [y|n]") 86 | if [[ $yn = [Yy]* ]]; then 87 | "${cmd[@]}" || error "Error running command" 88 | fi 89 | read -p "Press ENTER to return to menu" -r 90 | } 91 | 92 | function withdrawCommand(){ 93 | local v a 94 | v=$(question "Space separated list of validator pubkeys for which the withdrawal will be executed: ") 95 | a=$(question "Amount of ETH which will be withdrawn from the validator(s) (in ETH notation e.g. 0.001): ") 96 | cli_options=() 97 | cli_options+=(--amount="$a") 98 | cli_options+=(--validator="$v") 99 | local cmd=( "$PLUGIN_INSTALL_PATH/eth-validator-cli" "withdraw" "${global_options[@]}" "${cli_options[@]}" ) 100 | info "Executing command > ${cmd[*]}" 101 | yn=$(question "Please double-check your inputs before executing a command. Is the above correct? [y|n]") 102 | if [[ $yn = [Yy]* ]]; then 103 | "${cmd[@]}" || error "Error running command" 104 | fi 105 | read -p "Press ENTER to return to menu" -r 106 | } 107 | 108 | while true; do 109 | # Define the options for the submenu 110 | SUBOPTIONS=( 111 | 1 "Edit env configuration" 112 | 2 "Switch: Switch withdrawal credential type from 0x01 to 0x02 for one or many validators" 113 | 3 "Consolidate: Consolidate one or many source validators into one target validator" 114 | 4 "Withdraw: Partially withdraw ETH from one or many validators" 115 | 5 "Exit: Exit one or many validators" 116 | 6 "Update to latest release" 117 | 7 "Uninstall plugin" 118 | - "" 119 | 10 "Back to main menu" 120 | ) 121 | 122 | # Display the submenu and get the user's choice 123 | SUBCHOICE=$(whiptail --clear --cancel-button "Back" \ 124 | --backtitle "$BACKTITLE" \ 125 | --title "Plugin - 🔧 eth-validator-cli $VERSION by TobiWo: managing validators via execution layer requests" \ 126 | --menu "\nChoose one of the following options:" \ 127 | 0 0 0 \ 128 | "${SUBOPTIONS[@]}" \ 129 | 3>&1 1>&2 2>&3) 130 | 131 | if [ $? -gt 0 ]; then # user pressed button 132 | break 133 | fi 134 | # Handle the user's choice from the submenu 135 | case $SUBCHOICE in 136 | 1) 137 | sudo "${EDITOR}" "$PLUGIN_INSTALL_PATH"/env 138 | ;; 139 | 2) 140 | switchWithdrawalCredentialTypeCommand 141 | ;; 142 | 3) 143 | consolidateCommand 144 | ;; 145 | 4) 146 | withdrawCommand 147 | ;; 148 | 5) 149 | exitCommand 150 | ;; 151 | 6) 152 | exec ./plugins/eth-validator-cli/plugin_eth-validator-cli.sh -u 153 | ;; 154 | 7) 155 | exec ./plugins/eth-validator-cli/plugin_eth-validator-cli.sh -r 156 | ;; 157 | 10) 158 | break 159 | ;; 160 | esac 161 | done 162 | -------------------------------------------------------------------------------- /helpers/install_2fa.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Author: coincashew.eth | coincashew.com 3 | # License: GNU GPL 4 | # Source: https://github.com/coincashew/ethpillar 5 | # Description: EthPillar is a one-liner setup tool and node management TUI 6 | # 7 | # Made for home and solo stakers 🏠🥩 8 | 9 | # 🫶 Make improvements and suggestions on GitHub: 10 | # * https://github.com/coincashew/ethpillar 11 | # 🙌 Ask questions on Discord: 12 | # * https://discord.gg/dEpAVWgFNB 13 | 14 | set -euo pipefail 15 | trap 'echo -e "\n❌ Operation aborted."; exit 1' INT 16 | 17 | clear 18 | echo "########################################################################################" 19 | echo "2FA: Secure your SSH access with two-factor authentication" 20 | echo "########################################################################################" 21 | echo "Key Points:" 22 | echo "* Enhanced Access Control: Requires two verification factors (e.g., SSH key + time-based code)." 23 | echo "* Mitigates Credential Theft: Renders stolen passwords/SSH keys useless without the second factor." 24 | echo "* Phishing/Keylogger Resistance: Time-sensitive codes prevent reuse, thwarting most phishing and keylogging attacks." 25 | echo "* ⚠️ Critical Note: Always test 2FA in a parallel session to avoid accidental lockouts." 26 | echo "" 27 | 28 | function check_ssh_config() { 29 | echo "🧪 Validating SSH configuration..." 30 | if ! sudo sshd -t; then 31 | echo "❌ SSH configuration is invalid. Aborting to avoid lockout." 32 | exit 1 33 | fi 34 | } 35 | 36 | function install() { 37 | echo "🔐 SSH 2FA Setup: Proceed with installation? [y/N]" 38 | read -rsn1 yn 39 | [[ ! ${yn:-n} =~ ^[Yy]$ ]] && exit 0 40 | 41 | echo "🔧 Installing required package..." 42 | sudo apt-get update -qq 43 | sudo apt-get install -y libpam-google-authenticator 44 | 45 | echo "🔐 Generating 2FA credentials..." 46 | if [[ -f ~/.google_authenticator ]]; then 47 | echo "⚠️ Existing 2FA config detected for this user." 48 | echo "Overwrite? This will invalidate your current setup. [y/N]" 49 | read -rsn1 ow 50 | [[ ! ${ow:-n} =~ ^[Yy]$ ]] && exit 0 51 | fi 52 | google-authenticator -t -d -f -r 3 -R 30 -w 3 53 | 54 | echo "📁 Ensuring SSH config directory exists..." 55 | sudo mkdir -p /etc/ssh/sshd_config.d 56 | 57 | echo "📝 Backing up PAM SSH config..." 58 | sudo cp /etc/pam.d/sshd /etc/pam.d/sshd.bak 59 | 60 | echo "🔧 Configuring PAM for 2FA..." 61 | if ! grep -q "pam_google_authenticator.so" /etc/pam.d/sshd; then 62 | echo "auth required pam_google_authenticator.so" | sudo tee -a /etc/pam.d/sshd 63 | fi 64 | sudo sed -i "s/^@include common-auth/#@include common-auth/" /etc/pam.d/sshd 65 | 66 | echo "🛡️ Creating SSH 2FA config..." 67 | sudo tee /etc/ssh/sshd_config.d/two-factor.conf > /dev/null </dev/null | grep -oP 'v\d+\.\d+\.\d+' || echo "unknown") 35 | else 36 | CURRENT_VERSION="client-stats not installed" 37 | fi 38 | } 39 | 40 | # Downloads latest release 41 | function downloadClient(){ 42 | cd $HOME 43 | getLatestVersion 44 | prysm_version=$TAG 45 | # Convert to lower case 46 | _platform=$(echo ${_platform} | tr '[:upper:]' '[:lower:]') 47 | # Format files 48 | file_client_stats=client-stats-${prysm_version}-${_platform}-${_arch} 49 | curl -f -L "https://prysmaticlabs.com/releases/${file_client_stats}" -o client-stats 50 | chmod +x client-stats 51 | sudo mv client-stats $PLUGIN_INSTALL_PATH 52 | } 53 | 54 | # Upgrade function 55 | function upgrade(){ 56 | getLatestVersion 57 | getCurrentVersion 58 | # Remove front v if present and compare versions 59 | [[ "${CURRENT_VERSION#v}" == "${TAG#v}" ]] && whiptail --title "Already updated" --msgbox "You are already on the latest version: $CURRENT_VERSION" 10 78 && return 60 | if whiptail --title "Update $APP_NAME" --yesno "Installed Version is: $CURRENT_VERSION\nLatest Version is: $TAG\n\nReminder: Always read the release notes for breaking changes: $GITHUB_RELEASE_NODES\n\nDo you want to update to $TAG?" 12 78; then 61 | sudo systemctl stop $SERVICE_NAME 62 | downloadClient 63 | sudo systemctl start $SERVICE_NAME 64 | fi 65 | } 66 | 67 | # Installation function 68 | function install(){ 69 | MSG_ABOUT="🌈 Prysm client-stats collects CL & VC metrics and publishes them to the beaconcha.in stats service 70 | \nFeatures: 71 | \n- Monitor your staking node on the beaconcha.in mobile app 72 | \n- Gathers data through the validator and consensus client's metrics APIs. 73 | \n- Free monitoring tool by beaconcha.in to enhance the solo staking experience 74 | \nSignup: https://beaconcha.in/register 75 | \nFind your API Key here: 76 | testnet > https://hoodi.beaconcha.in/user/settings#api 77 | mainnet > https://beaconcha.in/user/settings#api 78 | \nTip: Ensure your metrics are enabled! 79 | Consensus metrics on port 8008, validator metrics on port 8009 80 | \nDownload the mobile app: https://beaconcha.in/mobile 81 | \nContinue to install?" 82 | 83 | # Intro screen 84 | if ! whiptail --title "$APP_NAME: Installation" --yesno "$MSG_ABOUT" 30 78; then exit; fi 85 | 86 | # Get API_KEY 87 | APIKEY=$(whiptail --title "API-KEY" --inputbox "Enter your beaconcha.in API-KEY" 10 78 --ok-button "Submit" 3>&1 1>&2 2>&3) 88 | 89 | # Get MACHINE_NAME 90 | HOSTNAME=$(hostname) 91 | MACHINE_NAME=$(whiptail --title "Machine Name" --inputbox "Enter a name for this machine" 10 78 "$HOSTNAME" --ok-button "Submit" 3>&1 1>&2 2>&3) 92 | 93 | # Create service file 94 | sudo cp ${PLUGIN_SOURCE_PATH}/${SERVICE_NAME}.service.example ${PLUGIN_SOURCE_PATH}/${SERVICE_NAME}.service.tmp 95 | 96 | # Update values 97 | sed -i "s/__APIKEY/${APIKEY}/g" ${PLUGIN_SOURCE_PATH}/${SERVICE_NAME}.service.tmp || true 98 | sed -i "s/__MACHINE_NAME/${MACHINE_NAME}/g" ${PLUGIN_SOURCE_PATH}/${SERVICE_NAME}.service.tmp || true 99 | 100 | # Install service file 101 | sudo mv ${PLUGIN_SOURCE_PATH}/${SERVICE_NAME}.service.tmp $SERVICE_FILE 102 | 103 | # Create service user 104 | sudo useradd --no-create-home --shell /bin/false $SERVICE_ACCOUNT 105 | 106 | # Setup installation directory 107 | sudo mkdir -p $PLUGIN_INSTALL_PATH 108 | 109 | # Install binaries 110 | downloadClient 111 | 112 | # Update permissions 113 | sudo chown $SERVICE_ACCOUNT:$SERVICE_ACCOUNT -R $PLUGIN_INSTALL_PATH 114 | 115 | # Enable and start service 116 | sudo systemctl enable $SERVICE_NAME 117 | sudo systemctl start $SERVICE_NAME 118 | 119 | MSG_COMPLETE="Done! $APP_NAME is now running. View the logs for more details." 120 | 121 | # Installation complete screen 122 | whiptail --title "$APP_NAME: Install Complete" --msgbox "$MSG_COMPLETE" 8 78 123 | } 124 | 125 | # Uninstall 126 | function removeAll() { 127 | if whiptail --title "Uninstall $APP_NAME" --defaultno --yesno "Are you sure you want to remove $APP_NAME" 9 78; then 128 | sudo systemctl stop $SERVICE_NAME 129 | sudo systemctl disable $SERVICE_NAME 130 | sudo rm $SERVICE_FILE 131 | sudo userdel $SERVICE_ACCOUNT 132 | sudo rm -rf "$PLUGIN_INSTALL_PATH" 133 | whiptail --title "Uninstall finished" --msgbox "You have uninstalled $APP_NAME." 8 78 134 | fi 135 | } 136 | 137 | # Displays usage info 138 | function usage() { 139 | cat << EOF 140 | Usage: $(basename "$0") [-i] [-u] [-r] [-s] 141 | 142 | $APP_NAME Helper Script 143 | 144 | Options) 145 | -i Install $APP_NAME 146 | -u Upgrade $APP_NAME 147 | -r Remove $APP_NAME 148 | -h Display help 149 | 150 | About $APP_NAME) 151 | - $DESCRIPTION 152 | - Source code: $SOURCE_CODE 153 | - Documentation: $DOCUMENTATION 154 | EOF 155 | } 156 | 157 | # Process command line options 158 | while getopts :iurhs opt; do 159 | case ${opt} in 160 | i ) install ;; 161 | u ) upgrade ;; 162 | r ) removeAll ;; 163 | h) 164 | usage 165 | exit 0 166 | ;; 167 | \?) 168 | echo "Invalid option: -${OPTARG}" >&2 169 | usage 170 | exit 1 171 | ;; 172 | :) 173 | echo "Option -${OPTARG} requires an argument." >&2 174 | usage 175 | exit 1 176 | ;; 177 | esac 178 | done 179 | -------------------------------------------------------------------------------- /plugins/dora/plugin_dora.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Author: coincashew.eth | coincashew.com 4 | # License: GNU GPL 5 | # Source: https://github.com/coincashew/ethpillar 6 | # Description: Dora the Explorer helper script 7 | # 8 | # Made for home and solo stakers 🏠🥩 9 | 10 | SOURCE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 11 | 12 | # Variables 13 | RELEASE_URL="https://api.github.com/repos/ethpandaops/dora/releases/latest" 14 | GITHUB_RELEASE_NODES="https://github.com/ethpandaops/dora/releases" 15 | DESCRIPTION="Dora the Explorer is a tool for exploring ethereum execution and consensus clients. It provides insights into client behavior, performance, and network metrics." 16 | DOCUMENTATION="https://github.com/ethpandaops/dora/wiki" 17 | SOURCE_CODE="https://github.com/ethpandaops/dora" 18 | APP_NAME="dora" 19 | PLUGIN_NAME="Dora the Explorer Plugin" 20 | PLUGIN_INSTALL_PATH="/opt/ethpillar/plugin-dora" 21 | PLUGIN_SOURCE_PATH="$SOURCE_DIR" 22 | SERVICE_NAME="dora" 23 | SERVICE_ACCOUNT="dora" 24 | SERVICE_FILE="/etc/systemd/system/dora.service" 25 | 26 | # Load functions 27 | source $SOURCE_DIR/../../functions.sh 28 | 29 | # Get machine info 30 | _platform=$(get_platform) 31 | _arch=$(get_arch) 32 | 33 | # Gets latest tag 34 | function getLatestVersion(){ 35 | TAG=$(curl -s $RELEASE_URL | jq -r .tag_name ) 36 | # Exit in case of null tag 37 | [[ -z $TAG ]] || [[ $TAG == "null" ]] && echo "ERROR: Couldn't find the latest version tag" && exit 1 38 | } 39 | 40 | # Downloads latest release 41 | function downloadClient(){ 42 | BINARIES_URL="$(curl -s $RELEASE_URL | jq -r ".assets[] | select(.name) | .browser_download_url" | grep --ignore-case ${_platform}_${_arch})" 43 | echo Downloading URL: $BINARIES_URL 44 | cd $HOME 45 | # Download 46 | wget -O $APP_NAME.tar.gz $BINARIES_URL 47 | # Untar 48 | tar -xzvf $APP_NAME.tar.gz -C $HOME 49 | # Cleanup 50 | rm $APP_NAME.tar.gz 51 | # Install binary 52 | sudo mv $HOME/dora-explorer $PLUGIN_INSTALL_PATH 53 | sudo mv $HOME/dora-utils $PLUGIN_INSTALL_PATH 54 | sudo chmod +x $PLUGIN_INSTALL_PATH/dora* 55 | # Store current version 56 | getLatestVersion 57 | sudo echo "$TAG" > $PLUGIN_INSTALL_PATH/current_version 58 | } 59 | 60 | #Asks to update 61 | function upgrade(){ 62 | getLatestVersion 63 | if whiptail --title "Update $APP_NAME" --yesno "Installed Version is: $(cat $PLUGIN_INSTALL_PATH/current_version)\nLatest Version is: $TAG\n\nReminder: Always read the release notes for breaking changes: $GITHUB_RELEASE_NODES\n\nDo you want to update to $TAG?" 12 78; then 64 | sudo systemctl stop $SERVICE_NAME 65 | downloadClient 66 | sudo systemctl start $SERVICE_NAME 67 | fi 68 | } 69 | 70 | # Installs latest release and creates config file 71 | function install(){ 72 | MSG_ABOUT="Dora the Explorer is a lightweight beaconchain explorer 73 | \nFeatures: 74 | \n- Validator Activities: Submit deposits, consolidation, withdrawals, exits 75 | \n- Block Explorer: Block and transaction explorer 76 | \n- Data: Real-time metrics and statistics 77 | \n- Privacy: Dora will run locally using your node 78 | \nSource Code: https://github.com/ethpandaops/dora 79 | \nContinue to install?" 80 | 81 | # Intro screen 82 | if ! whiptail --title "Dora the Explorer: Installation" --yesno "$MSG_ABOUT" 20 78; then exit; fi 83 | 84 | # Get network 85 | _LISTENING_IP=$(whiptail --title "Set Dora's HTTP listening IP" --menu \ 86 | "For which IP should Dora run on?" 10 78 2 \ 87 | "localhost" "localhost only (local access only)" \ 88 | "${ip_current}" "machine's IP (allow external access)" \ 89 | 3>&1 1>&2 2>&3) 90 | 91 | # Open firewall port 8080 for local network 92 | [[ $_LISTENING_IP = ${ip_current} ]] && sudo ufw allow from ${network_current} to any port 8080 comment 'Allow local network to access dora' 93 | 94 | # Create service user 95 | sudo useradd --no-create-home --shell /bin/false $SERVICE_ACCOUNT 96 | 97 | # Install service file 98 | sudo cp ${PLUGIN_SOURCE_PATH}/${SERVICE_NAME}.service.example $SERVICE_FILE 99 | 100 | # Setup installation directory 101 | sudo mkdir -p $PLUGIN_INSTALL_PATH 102 | 103 | # Create config 104 | sudo cp $PLUGIN_SOURCE_PATH/explorer-config.yaml.example $PLUGIN_SOURCE_PATH/explorer-config.yaml.tmp 105 | 106 | # Update config values 107 | sed -i "s/__NETWORK/${NETWORK}/g" $PLUGIN_SOURCE_PATH/explorer-config.yaml.tmp || true 108 | sed -i "s/__IP/${_LISTENING_IP}/g" $PLUGIN_SOURCE_PATH/explorer-config.yaml.tmp || true 109 | sudo mv $PLUGIN_SOURCE_PATH/explorer-config.yaml.tmp $PLUGIN_INSTALL_PATH/explorer-config.yaml 110 | 111 | # Install binaries 112 | downloadClient 113 | 114 | # Update permissions 115 | sudo chown $SERVICE_ACCOUNT:$SERVICE_ACCOUNT -R $PLUGIN_INSTALL_PATH 116 | 117 | # Enable and start service 118 | sudo systemctl enable $SERVICE_NAME 119 | sudo systemctl start $SERVICE_NAME 120 | 121 | MSG_COMPLETE="Done! Dora the Explorer is now running. 122 | \nAccess the dashboard at: http://localhost:8080 or http://$ip_current:8080 123 | \nNote: It may take a few minutes for data to appear as Dora indexes your node." 124 | 125 | # Installation complete screen 126 | whiptail --title "Dora the Explorer: Install Complete" --msgbox "$MSG_COMPLETE" 15 78 127 | } 128 | 129 | # Uninstall 130 | function removeAll() { 131 | if whiptail --title "Uninstall $APP_NAME" --defaultno --yesno "Are you sure you want to remove $APP_NAME" 9 78; then 132 | sudo systemctl stop $SERVICE_NAME 133 | sudo systemctl disable $SERVICE_NAME 134 | sudo rm $SERVICE_FILE 135 | sudo userdel $SERVICE_ACCOUNT 136 | sudo rm -rf "$PLUGIN_INSTALL_PATH" 137 | whiptail --title "Uninstall finished" --msgbox "You have uninstalled $APP_NAME." 8 78 138 | fi 139 | } 140 | 141 | # Displays usage info 142 | function usage() { 143 | cat << EOF 144 | Usage: $(basename "$0") [-i] [-u] [-r] [-s] 145 | 146 | $APP_NAME Helper Script 147 | 148 | Options) 149 | -i Install $APP_NAME 150 | -u Upgrade $APP_NAME 151 | -r Remove $APP_NAME 152 | -h Display help 153 | 154 | About $APP_NAME) 155 | - $DESCRIPTION 156 | - Source code: $SOURCE_CODE 157 | - Documentation: $DOCUMENTATION 158 | EOF 159 | } 160 | 161 | # Process command line options 162 | while getopts :iurhs opt; do 163 | case ${opt} in 164 | i ) install ;; 165 | u ) upgrade ;; 166 | r ) removeAll ;; 167 | h) 168 | usage 169 | exit 0 170 | ;; 171 | \?) 172 | echo "Invalid option: -${OPTARG}" >&2 173 | usage 174 | exit 1 175 | ;; 176 | :) 177 | echo "Option -${OPTARG} requires an argument." >&2 178 | usage 179 | exit 1 180 | ;; 181 | esac 182 | done 183 | -------------------------------------------------------------------------------- /uninstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Author: coincashew.eth | coincashew.com 4 | # License: GNU GPL 5 | # Source: https://github.com/coincashew 6 | # 7 | # Made for home and solo stakers 🏠🥩 8 | 9 | function promptYesNo(){ 10 | if whiptail --title "Uninstall Staking Node" --defaultno --yesno "This will remove all data and files related to this staking node.\nAre you sure you want to remove all files?\n(consensus/execution/validator/mevboost)" 9 78; then 11 | uninstallCL 12 | uninstallEL 13 | uninstallVC 14 | uninstallMevboost 15 | cleanupMisc 16 | uninstallPlugins 17 | whiptail --title "Uninstall finished" --msgbox "You have uninstalled this staking node and all validator keys." 8 78 18 | else 19 | echo "Cancelled uninstall." && return 1 20 | fi 21 | } 22 | 23 | function uninstallPlugins(){ 24 | if [[ -d /opt/ethpillar/plugin-csm ]]; then 25 | sudo systemctl stop csm_nimbusvalidator 26 | sudo systemctl disable csm_nimbusvalidator 27 | sudo rm /etc/systemd/system/csm_nimbusvalidator.service 28 | sudo userdel csm_nimbus_validator 29 | sudo rm -rf /opt/ethpillar/plugin-csm 30 | fi 31 | if [[ -d /opt/ethpillar/plugin-sentinel ]]; then 32 | sudo docker stop csm-sentinel 33 | sudo docker rm csm-sentinel 34 | sudo docker rmi csm-sentinel 35 | sudo docker volume rm csm-sentinel-persistent 36 | sudo rm -rf /opt/ethpillar/plugin-sentinel 37 | fi 38 | if [[ -d /opt/ethpillar/plugin-dora ]]; then 39 | sudo systemctl stop dora 40 | sudo systemctl disable dora 41 | sudo rm /etc/systemd/system/dora.service 42 | sudo userdel dora 43 | sudo rm -rf /opt/ethpillar/plugin-dora 44 | fi 45 | if [[ -d /opt/ethpillar/plugin-client-stats ]]; then 46 | sudo systemctl stop client-stats 47 | sudo systemctl disable client-stats 48 | sudo rm /etc/systemd/system/client-stats.service 49 | sudo userdel client-stats 50 | sudo rm -rf /opt/ethpillar/plugin-client-stats 51 | fi 52 | if [[ -d /opt/ethpillar/plugin-contributoor ]]; then 53 | sudo systemctl stop contributoor 54 | sudo systemctl disable contributoor 55 | sudo rm /etc/systemd/system/contributoor.service 56 | sudo userdel contributoor 57 | sudo rm -rf /opt/ethpillar/plugin-contributoor 58 | fi 59 | if [[ -d /opt/ethpillar/aztec ]]; then 60 | cd /opt/ethpillar/aztec 2>/dev/null && docker compose down || true 61 | sudo docker rm -f aztec-sequencer 62 | TAG=$(grep "DOCKER_TAG" /opt/ethpillar/aztec/.env | sed "s/^DOCKER_TAG=\(.*\)/\1/") 63 | sudo docker rmi -f aztecprotocol/aztec:"$TAG" 64 | if [[ -f /opt/ethpillar/aztec/.cast_installed_by_plugin && -f /usr/local/bin/cast ]]; then 65 | sudo rm /usr/local/bin/cast 66 | fi 67 | sudo rm -rf /opt/ethpillar/aztec 68 | fi 69 | } 70 | 71 | function cleanupMisc(){ 72 | if [[ -f /etc/systemd/system/ethereum-metrics-exporter.service ]]; then 73 | sudo rm /etc/apt/sources.list.d/grafana.list 74 | sudo systemctl disable ethereum-metrics-exporter 75 | sudo systemctl stop ethereum-metrics-exporter 76 | sudo rm /etc/systemd/system/ethereum-metrics-exporter.service 77 | sudo rm /usr/local/bin/ethereum-metrics-exporter 78 | sudo systemctl disable grafana-server prometheus prometheus-node-exporter 79 | sudo systemctl stop grafana-server prometheus prometheus-node-exporter 80 | sudo apt remove -y grafana prometheus prometheus-node-exporter 81 | fi 82 | if [[ -f /usr/local/bin/eth-duties ]]; then sudo rm /usr/local/bin/eth-duties; fi 83 | if [[ -f /usr/local/bin/ethdo ]]; then sudo rm /usr/local/bin/ethdo; fi 84 | if [[ -f $BASE_DIR/.env.overrides ]]; then sudo rm $BASE_DIR/.env.overrides; fi 85 | if [[ -d /opt/ethpillar/testnet ]]; then sudo rm -rf /opt/ethpillar/testnet; fi 86 | if [[ -d /opt/ethpillar/patches ]]; then sudo rm -rf /opt/ethpillar/patches; fi 87 | } 88 | 89 | function uninstallCL(){ 90 | if [[ -f /etc/systemd/system/consensus.service ]]; then 91 | sudo systemctl stop consensus 92 | sudo systemctl disable consensus 93 | sudo rm /etc/systemd/system/consensus.service 94 | 95 | #Lighthouse 96 | sudo rm -rf /usr/local/bin/lighthouse 97 | sudo rm -rf /var/lib/lighthouse 98 | 99 | #Lodestar 100 | sudo rm -rf /usr/local/bin/lodestar 101 | sudo rm -rf /var/lib/lodestar 102 | 103 | #Teku 104 | sudo rm -rf /usr/local/bin/teku 105 | sudo rm -rf /var/lib/teku 106 | 107 | #Nimbus 108 | sudo rm -rf /usr/local/bin/nimbus_beacon_node 109 | sudo rm -rf /var/lib/nimbus 110 | 111 | #Prysm from Binaries 112 | sudo rm -rf /usr/local/bin/beacon-chain 113 | #Prysm from Build from Source 114 | sudo rm -rf /usr/local/bin/prysm 115 | sudo rm -rf /var/lib/prysm 116 | 117 | sudo userdel consensus 118 | fi 119 | } 120 | 121 | function uninstallEL(){ 122 | if [[ -f /etc/systemd/system/execution.service ]]; then 123 | sudo systemctl stop execution 124 | sudo systemctl disable execution 125 | sudo rm /etc/systemd/system/execution.service 126 | 127 | #Nethermind 128 | sudo rm -rf /usr/local/bin/nethermind 129 | sudo rm -rf /var/lib/nethermind 130 | 131 | #Besu 132 | sudo rm -rf /usr/local/bin/besu 133 | sudo rm -rf /var/lib/besu 134 | 135 | #Geth 136 | sudo rm -rf /usr/local/bin/geth 137 | sudo rm -rf /var/lib/geth 138 | 139 | #Erigon 140 | sudo rm -rf /usr/local/bin/erigon 141 | sudo rm -rf /var/lib/erigon 142 | 143 | #Reth 144 | sudo rm -rf /usr/local/bin/reth 145 | sudo rm -rf /var/lib/reth 146 | 147 | sudo userdel execution 148 | fi 149 | } 150 | 151 | function uninstallVC(){ 152 | if [[ -f /etc/systemd/system/validator.service ]]; then 153 | sudo systemctl stop validator 154 | sudo systemctl disable validator 155 | sudo rm /etc/systemd/system/validator.service 156 | 157 | #Lighthouse 158 | sudo rm -rf /var/lib/lighthouse/validators 159 | sudo rm -rf /var/lib/lighthouse_validator 160 | 161 | #Lodestar 162 | sudo rm -rf /var/lib/lodestar/validators 163 | sudo rm -rf /var/lib/lodestar_validator 164 | 165 | #Teku, if running Standalone Teku Validator 166 | sudo rm -rf /var/lib/teku_validator 167 | 168 | #Nimbus, if running standalone Nimbus Validator 169 | sudo rm -rf /var/lib/nimbus_validator 170 | sudo rm -rf /usr/local/bin/nimbus_validator_client 171 | 172 | #Prysm from Binaries 173 | sudo rm -rf /usr/local/bin/validator 174 | sudo rm -rf /var/lib/prysm/validators 175 | 176 | sudo userdel validator 177 | fi 178 | } 179 | 180 | function uninstallMevboost(){ 181 | if [[ -f /etc/systemd/system/mevboost.service ]]; then 182 | sudo systemctl stop mevboost 183 | sudo systemctl disable mevboost 184 | sudo rm /etc/systemd/system/mevboost.service 185 | sudo rm /usr/local/bin/mev-boost 186 | sudo userdel mevboost 187 | fi 188 | } 189 | 190 | promptYesNo -------------------------------------------------------------------------------- /install-node.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Author: coincashew.eth | coincashew.com 4 | # License: GNU GPL 5 | # https: //github.com/coincashew/ethpillar 6 | # 7 | # Acknowledgments 8 | # validator-install is branched from validator-install written by Accidental-green: https: //github.com/accidental-green/validator-install 9 | 10 | set -u 11 | 12 | # enable command completion 13 | set -o history -o histexpand 14 | 15 | python="python3" 16 | skip_prompt="" 17 | 18 | if [[ ${#} -eq 0 ]]; then 19 | echo "ERROR: Missing deploy file. Example ./install-node.sh deploy-nimbus-nethermind.py" 20 | exit 1 21 | elif [[ ${#} -eq 2 ]]; then 22 | skip_prompt="$2" 23 | fi 24 | install_file="$1" 25 | # Accept only deploy-*.py and disallow slashes 26 | if [[ "$install_file" != deploy-*.py || "$install_file" == */* ]]; then 27 | echo "ERROR: Invalid deploy file: $install_file" 28 | exit 1 29 | fi 30 | 31 | abort() { 32 | printf "%s\n" "$1" 33 | exit 1 34 | } 35 | 36 | getc() { 37 | local save_state 38 | save_state=$(/bin/stty -g) 39 | /bin/stty raw -echo 40 | IFS= read -r -n 1 -d '' "$@" 41 | /bin/stty "$save_state" 42 | } 43 | 44 | exit_on_error() { 45 | exit_code=$1 46 | last_command="${@:2}" 47 | if [ $exit_code -ne 0 ]; then 48 | >&2 echo "\"${last_command}\" command failed with exit code ${exit_code}." 49 | exit $exit_code 50 | fi 51 | } 52 | 53 | wait_for_user() { 54 | local c 55 | echo 56 | echo "Press RETURN to continue or any other key to abort" 57 | getc c 58 | # we test for \r and \n because some stuff does \r instead 59 | if ! [[ "$c" == $'\r' || "$c" == $'\n' ]]; then 60 | exit 1 61 | fi 62 | } 63 | 64 | shell_join() { 65 | local arg 66 | printf "%s" "$1" 67 | shift 68 | for arg in "$@"; do 69 | printf " " 70 | printf "%s" "${arg// /\ }" 71 | done 72 | } 73 | 74 | # string formatters 75 | if [[ -t 1 ]]; then 76 | tty_escape() { printf "\033[%sm" "$1"; } 77 | else 78 | tty_escape() { :; } 79 | fi 80 | tty_mkbold() { tty_escape "1;$1"; } 81 | tty_underline="$(tty_escape "4;39")" 82 | tty_blue="$(tty_mkbold 34)" 83 | tty_red="$(tty_mkbold 31)" 84 | tty_bold="$(tty_mkbold 39)" 85 | tty_reset="$(tty_escape 0)" 86 | 87 | ohai() { 88 | printf "${tty_blue}==>${tty_bold} %s${tty_reset}\n" "$(shell_join "$@")" 89 | } 90 | 91 | requirements_check() { 92 | # Check CPU architecture 93 | if ! [[ $(lscpu | grep -oE 'x86') || $(lscpu | grep -oE 'aarch64') ]]; then 94 | echo "This machine's CPU architecture is not yet supported." 95 | echo "Recommend using Intel-AMD x86 or arm64 systems for best experience." 96 | exit 1 97 | fi 98 | 99 | # Check operating system 100 | if ! [[ "$(uname)" == "Linux" ]]; then 101 | echo "This operating system is not yet supported." 102 | echo "Recommend installing Ubuntu Desktop 24.04+ LTS or Ubuntu Server 24.04+ LTS for best experience." 103 | exit 1 104 | fi 105 | } 106 | 107 | linux_install_pre() { 108 | sudo apt-get update 109 | sudo apt-get install --no-install-recommends --no-install-suggests -y curl git ccze jq tmux bc 110 | exit_on_error $? 111 | } 112 | 113 | linux_install_python() { 114 | which $python 115 | if [[ $? != 0 ]] ; then 116 | ohai "Installing python" 117 | sudo apt-get install --no-install-recommends --no-install-suggests -y $python 118 | else 119 | ohai "Updating python" 120 | sudo apt-get install --only-upgrade -y $python 121 | fi 122 | exit_on_error $? 123 | ohai "Installing python tools" 124 | sudo apt-get install --no-install-recommends --no-install-suggests -y $python-pip $python-tk $python-venv 125 | ohai "Creating venv" 126 | $python -m venv ~/.local --system-site-packages 127 | ohai "Installing pip requirements" 128 | ~/.local/bin/pip install requests console-menu python-dotenv tqdm 129 | exit_on_error $? 130 | } 131 | 132 | linux_install_validator-install() { 133 | ohai "Cloning ethpillar into ~/git/ethpillar" 134 | mkdir -p ~/git/ethpillar 135 | git clone https://github.com/coincashew/ethpillar.git ~/git/ethpillar 2> /dev/null || (cd ~/git/ethpillar ; git fetch origin main ; git checkout main ; git pull) 136 | ohai "Installing validator-install" 137 | $python ~/git/ethpillar/${install_file} 138 | ohai "Allowing user to view journalctl logs" 139 | sudo usermod -a -G systemd-journal $USER 140 | ohai "Install complete!" 141 | exit_on_error $? 142 | } 143 | 144 | # Check OS and CPU requirements 145 | requirements_check 146 | 147 | # Do install. 148 | OS="$(uname)" 149 | if [[ "$OS" == "Linux" ]]; then 150 | echo """ 151 | ██╗ ██╗ █████╗ ██╗ ██╗██████╗ █████╗ ████████╗ ██████╗ ██████╗ 152 | ██║ ██║██╔══██╗██║ ██║██╔══██╗██╔══██╗╚══██╔══╝██╔═══██╗██╔══██╗ 153 | ██║ ██║███████║██║ ██║██║ ██║███████║ ██║ ██║ ██║██████╔╝ 154 | ╚██╗ ██╔╝██╔══██║██║ ██║██║ ██║██╔══██║ ██║ ██║ ██║██╔══██╗ 155 | ╚████╔╝ ██║ ██║███████╗██║██████╔╝██║ ██║ ██║ ╚██████╔╝██║ ██║ 156 | ╚═══╝ ╚═╝ ╚═╝╚══════╝╚═╝╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ 157 | 158 | ██╗███╗ ██╗███████╗████████╗ █████╗ ██╗ ██╗ 159 | ██║████╗ ██║██╔════╝╚══██╔══╝██╔══██╗██║ ██║ 160 | ██║██╔██╗ ██║███████╗ ██║ ███████║██║ ██║ 161 | ██║██║╚██╗██║╚════██║ ██║ ██╔══██║██║ ██║ 162 | ██║██║ ╚████║███████║ ██║ ██║ ██║███████╗███████╗ 163 | ╚═╝╚═╝ ╚═══╝╚══════╝ ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝ 164 | 165 | - This is my node. There are many like it, but this one is mine. 166 | - coincashew 167 | """ 168 | ohai "This script will install your Ethereum node:" 169 | echo "git jq curl ccze tmux bc" 170 | echo "python3-tk python3-pip python3-venv" 171 | echo "validator-install" 172 | 173 | if [[ -z $skip_prompt ]]; then wait_for_user; fi 174 | linux_install_pre 175 | linux_install_python 176 | linux_install_validator-install 177 | echo "" 178 | echo "" 179 | echo "######################################################################" 180 | echo "## ##" 181 | echo "## INSTALL COMPLETE! To start, type \"ethpillar\" ##" 182 | echo "## ##" 183 | echo "######################################################################" 184 | echo "" 185 | echo "" 186 | fi 187 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Do you like this software? Star the project and become a [⭐ Stargazer](https://github.com/coincashew/ethpillar/stargazers) 2 | 3 | # 🛡️ EthPillar 4 | 5 | ## Your Friendly Ethereum Node Installer & Manager 6 | 7 | [![Github release](https://img.shields.io/github/v/release/coincashew/ethpillar)](https://github.com/coincashew/ethpillar/releases) 8 | [![License](https://img.shields.io/github/license/coincashew/EthPillar)](https://github.com/coincashew/EthPillar/blob/main/LICENSE) 9 | [![GitHub Repo stars](https://img.shields.io/github/stars/coincashew/EthPillar?logo=github&color=yellow)](https://github.com/coincashew/EthPillar/stargazers) 10 | [![GitHub forks](https://img.shields.io/github/forks/coincashew/EthPillar?logo=github&color=blue)](https://github.com/coincashew/EthPillar/network/members) 11 | [![GitHub last commit](https://img.shields.io/github/last-commit/coincashew/EthPillar?logo=git)](https://github.com/coincashew/EthPillar/commits/main) 12 | [![Discord](https://img.shields.io/badge/discord-join%20chat-5B5EA6)](https://t.co/lnlom4iImq) 13 | [![Twitter](https://img.shields.io/twitter/follow/coincashew_)](https://x.com/coincashew_) 14 | ![CodeRabbit Pull Request Reviews](https://img.shields.io/coderabbit/prs/github/coincashew/EthPillar?utm_source=oss&utm_medium=github&utm_campaign=coincashew%2FEthPillar&labelColor=171717&color=FF570A) 15 | 16 | --- 17 | 18 | ## 🚀 What is EthPillar? 19 | 20 | EthPillar is a free, open-source tool to set up and manage your Ethereum node with just a few commands. Whether you’re home solo staking, using Lido CSM, defending cypherpunk ethos with Aztec L2 sequencer node, or running your own RPC node, EthPillar makes everything easy—from installing clients to monitoring your system—all via a friendly text user interface (TUI). 21 | 22 | **Highlights:** 23 | - Supports ARM64 & AMD64 hardware 24 | - Native [Lido CSM Integration](https://docs.lido.fi/run-on-lido/csm/node-setup/intermediate/ethpillar) 25 | - Native [Aztec L2 Integration](https://docs.coincashew.com/ethpillar/aztec) 26 | - Solo staking, full node, and testnet configurations 27 | - Fast updates and troubleshooting 28 | - Plugins for monitoring and performance 29 | 30 | ![EthPillar UI Preview](https://github.com/coincashew/coincashew/raw/master/.gitbook/assets/EthPillar.final.png) 31 | 32 | --- 33 | 34 | ## 🏁 Quickstart: One-line Ubuntu Install 35 | 36 | Open a terminal and run: 37 | 38 | ```bash 39 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/coincashew/EthPillar/main/install.sh)" 40 | ``` 41 | 42 | --- 43 | 44 | ## 🤔 Why use EthPillar? 45 | 46 | - **Beginner Friendly**: No need to memorize complex commands 47 | - **Fast Setup**: Deploy minority consensus/execution clients in minutes (Nimbus-Nethermind, Teku-Besu, Lodestar-Besu, Lighthouse-Reth, MEVboost included) 48 | - **Easy Updates**: Find and install the latest releases quickly 49 | - **Compatibility**: Works with Coincashew’s V2 staking setups 50 | 51 | Already running a validator? EthPillar works with [Coincashew’s Staking Guide](https://docs.coincashew.com/guides/mainnet). 52 | 53 | --- 54 | 55 | ## 🌟 Features 56 | 57 | - **Testnet Support**: Ephemery & Hoodi testnets for risk-free practice 58 | - **Lido CSM Integration**: Stake with as little as 2.4 ETH via Lido CSM ([Learn more](https://csm.testnet.fi/?ref=ethpillar)) 59 | - **Plugins**: Aztec, Lido CSM, Node-checker, validator tools, monitoring, stats, and more 60 | - **Grafana Dashboards**: Built-in Ethereum node monitoring 61 | - **Troubleshooting Tools**: Built-in checks for common node issues with Node Checker 62 | - **Flexible Deployment Configurations**: Solo staking node, Full Node, CSM, Validator-only, or Failover setups 63 | 64 | --- 65 | 66 | ## 👀 Screenshots 67 | 68 | _Main Menu_ 69 | ![Main Menu](https://docs.coincashew.com/img/preview02.png) 70 | 71 | --- 72 | 73 | ## 🎬 Demo 74 | 75 | [![Watch the demo](https://img.youtube.com/vi/aZLPACj2oPI/maxresdefault.jpg)](https://www.youtube.com/watch?v=aZLPACj2oPI) 76 | 77 | --- 78 | 79 | ## 📝 Prerequisites 80 | 81 | - Review [Staking for Beginners](https://www.reddit.com/r/ethstaker/wiki/staking_for_beginners/) 82 | - [Learn staking basics & hardware requirements](https://docs.coincashew.com/guides/mainnet/step-1-prerequisites) 83 | - Linux (Ubuntu recommended, tested on 24.04 LTS, also compatible with Armbian, Linux Mint, Debian) 84 | - AMD64 or ARM64 hardware (16GB RAM recommended for ARM64 single-board computers) 85 | 86 | --- 87 | 88 | ## 🛠️ Installation 89 | 90 | ### Option 1: Automated One-Liner 91 | 92 | ```bash 93 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/coincashew/EthPillar/main/install.sh)" 94 | ``` 95 | 96 | ### Option 2: Manual Install 97 | 98 | ```bash 99 | sudo apt-get update && sudo apt-get install git curl ccze bc tmux 100 | mkdir -p ~/git/ethpillar 101 | git clone https://github.com/coincashew/ethpillar.git ~/git/ethpillar 102 | sudo ln -s ~/git/ethpillar/ethpillar.sh /usr/local/bin/ethpillar 103 | ethpillar 104 | ``` 105 | 106 | --- 107 | 108 | ## 🏃 Next Steps 109 | 110 | Congrats! You’ve installed EthPillar and are ready to set up your node. 111 | 112 | Recommended next key steps: 113 | 114 | - Configure network, port forwarding, and firewall (Security & Node Checks > UFW Firewall) 115 | - Enable monitoring (Logging & Monitoring > Monitoring) 116 | - Benchmark your node (Toolbox > Yet-Another-Bench-Script) 117 | - Set up validator keys (Validator Client > Generate / Import Validator Keys) 118 | - Finally, run the automated Node Checker to verify everything is up to spec (Security & Node Checks > Node Checker) 119 | 120 | --- 121 | 122 | ## ❓ FAQ 123 | 124 | - Visit the [FAQs](https://docs.coincashew.com/ethpillar/faq) 125 | 126 | --- 127 | 128 | ## 📞 Support & Community 129 | 130 | - Join [Discord](https://discord.gg/dEpAVWgFNB) 131 | - Open issues or pull requests on [GitHub](https://github.com/coincashew/EthPillar) 132 | 133 | --- 134 | 135 | ## ❤️ Donate 136 | 137 | Support public goods! Find us on [Giveth || Gitcoin Grants](https://giveth.io/project/ethpillar-streamlining-ethereum-staking-for-everyone) or donate to [0xCF83d0c22dd54475cC0C52721B0ef07d9756E8C0](https://etherscan.io/address/0xCF83d0c22dd54475cC0C52721B0ef07d9756E8C0) (coincashew.eth) 138 | 139 | --- 140 | 141 | ## 🔄 Update EthPillar 142 | 143 | **TUI Update:** 144 | System Administration > Update EthPillar, then restart. 145 | 146 | **Manual Update:** 147 | ```bash 148 | cd ~/git/ethpillar 149 | git pull 150 | ``` 151 | 152 | --- 153 | 154 | ## 🌠 Contribute 155 | 156 | - Star the project on [GitHub](https://github.com/coincashew/EthPillar) 157 | - Share your experience on X or Reddit 158 | - Give feedback ([GitHub Issues](https://github.com/coincashew/EthPillar/issues)) 159 | - Submit PRs to improve EthPillar! 160 | 161 | --- 162 | 163 | ## 🙌 Credits 164 | 165 | Thanks to [accidental-green](https://github.com/accidental-green/validator-install) for inspiring this tooling! 166 | 167 | --- 168 | 169 | ## ⭐ Stargazers over time 170 | 171 | [![Stargazers over time](https://starchart.cc/coincashew/EthPillar.svg?variant=adaptive)](https://starchart.cc/coincashew/EthPillar) 172 | -------------------------------------------------------------------------------- /view_logs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Author: coincashew.eth | coincashew.com 4 | # License: GNU GPL 5 | # Source: https://github.com/coincashew/ethpillar 6 | # 7 | # Made for home and solo stakers 🏠🥩 8 | 9 | # Install btop process monitoring 10 | if ! command -v btop &> /dev/null; then 11 | sudo apt-get install btop -y 12 | fi 13 | 14 | # Install tmux 15 | if ! command -v tmux &> /dev/null; then 16 | sudo apt-get install tmux -y 17 | fi 18 | 19 | # Install ccze 20 | if ! command -v ccze &> /dev/null; then 21 | sudo apt-get install ccze -y 22 | fi 23 | 24 | # Check if the current user belongs to the systemd-journal group 25 | current_user=$(whoami) 26 | group_members=$(getent group systemd-journal | cut -d: -f2-) 27 | 28 | if ! echo "$group_members" | grep -qE -o -- "$current_user"; then 29 | clear 30 | # Add the user to the systemd-journal group if they're not already a member 31 | sudo usermod -aG systemd-journal $current_user 32 | echo -e "\033[1m########## New Terminal Session Required ############" 33 | echo "To view logs, $current_user has been added to systemd-journal group." 34 | echo "Open a new terminal, run 'ethpillar', then check logs again." 35 | echo "Press ENTER to continue" 36 | read 37 | exit 0 38 | fi 39 | 40 | # Enable truecolor logs for btop 41 | if [[ ! -f ~/.tmux.conf ]]; then 42 | cat << EOF > ~/.tmux.conf 43 | set-option -g terminal-overrides ",*:Tc" 44 | EOF 45 | fi 46 | 47 | # Kill prior session 48 | tmux kill-session -t logs 2>/dev/null || true 49 | 50 | # Get terminal width 51 | cols=$(tput cols) 52 | 53 | # Aztec node with remote rpc 54 | if [[ -d /opt/ethpillar/aztec ]] && [[ ! -f /etc/systemd/system/consensus.service ]]; then 55 | tmux new-session -d -s logs \; \ 56 | send-keys 'cd /opt/ethpillar/aztec && docker compose logs -f --tail=233' C-m \; \ 57 | split-window -h \; \ 58 | select-pane -t 1 \; \ 59 | send-keys 'btop --utf-force' C-m \; \ 60 | select-layout even-vertical \; 61 | exec tmux attach-session -t logs 62 | exit 0 63 | elif [[ -d /opt/ethpillar/aztec ]] && [[ -f /etc/systemd/system/consensus.service ]] && [[ ! -f /etc/systemd/system/validator.service ]]; then 64 | # Aztec node with local rpc 65 | tmux new-session -d -s logs \; \ 66 | send-keys 'journalctl -fu consensus --no-hostname | ccze -A' C-m \; \ 67 | split-window -h \; \ 68 | send-keys 'btop --utf-force' C-m \; \ 69 | split-window -v \; \ 70 | send-keys 'cd /opt/ethpillar/aztec && docker compose logs -f --tail=233' C-m \; \ 71 | select-pane -t 0 \; \ 72 | split-window -v \; \ 73 | send-keys 'journalctl -fu execution --no-hostname | ccze -A' C-m \; 74 | exec tmux attach-session -t logs 75 | exit 0 76 | fi 77 | 78 | # Check if integrated EL/CL client 79 | if grep --ignore-case -q "Integrated Execution-Consensus Client" /etc/systemd/system/execution.service; then isIntegrated=true; fi 80 | 81 | # Portrait view for narrow terminals <= 80 col 82 | if [[ $cols -lt 81 ]]; then 83 | if [[ -f /etc/systemd/system/execution.service ]] && [[ -f /etc/systemd/system/consensus.service ]] && [[ -f /etc/systemd/system/validator.service ]]; then 84 | # Solo Staking Node 85 | tmux new-session -d -s logs \; \ 86 | send-keys 'journalctl -fu consensus --no-hostname | ccze -A' C-m \; \ 87 | split-window -v \; \ 88 | send-keys 'journalctl -fu validator --no-hostname | ccze -A' C-m \; \ 89 | select-pane -t 0 \; \ 90 | split-window -v \; \ 91 | send-keys 'journalctl -fu execution --no-hostname | ccze -A' C-m \; \ 92 | select-layout even-vertical \; 93 | elif [[ -f /etc/systemd/system/execution.service || ${isIntegrated:-false} == "true" ]] && [[ -f /etc/systemd/system/validator.service ]]; then 94 | # Integrated EL-CL Node i.e. Caplin-Erigon 95 | tmux new-session -d -s logs \; \ 96 | send-keys 'journalctl -fu execution --no-hostname | ccze -A' C-m \; \ 97 | split-window -h \; \ 98 | select-pane -t 1 \; \ 99 | send-keys 'journalctl -fu validator --no-hostname | ccze -A' C-m \; \ 100 | select-layout even-vertical \; 101 | elif [[ -f /etc/systemd/system/execution.service ]] && [[ -f /etc/systemd/system/consensus.service ]]; then 102 | # Full Node Only 103 | tmux new-session -d -s logs \; \ 104 | send-keys 'journalctl -fu consensus --no-hostname | ccze -A' C-m \; \ 105 | split-window -h \; \ 106 | select-pane -t 1 \; \ 107 | send-keys 'journalctl -fu execution --no-hostname | ccze -A' C-m \; \ 108 | select-layout even-vertical \; 109 | elif [[ -f /etc/systemd/system/execution.service ]] && [[ ${isIntegrated:-false} == "true" ]]; then 110 | # Full Node Only for Integrated EL-CL 111 | tmux new-session -d -s logs \; \ 112 | send-keys 'journalctl -fu execution --no-hostname | ccze -A' C-m \; \ 113 | split-window -h \; \ 114 | select-pane -t 1 \; \ 115 | send-keys 'btop --utf-force' C-m \; \ 116 | select-layout even-vertical \; 117 | elif [[ -f /etc/systemd/system/validator.service ]]; then 118 | # Validator Client Only 119 | tmux new-session -d -s logs \; \ 120 | send-keys 'journalctl -fu validator --no-hostname | ccze -A' C-m \; \ 121 | split-window -h \; \ 122 | select-pane -t 1 \; \ 123 | send-keys 'btop --utf-force' C-m \; \ 124 | select-layout even-vertical \; 125 | fi 126 | else 127 | # Create full screen panes for validator node or non-staking node 128 | if [[ -f /etc/systemd/system/execution.service ]] && [[ -f /etc/systemd/system/consensus.service ]] && [[ -f /etc/systemd/system/validator.service ]]; then 129 | # Solo Staking Node 130 | tmux new-session -d -s logs \; \ 131 | send-keys 'journalctl -fu consensus --no-hostname | ccze -A' C-m \; \ 132 | split-window -h \; \ 133 | send-keys 'btop --utf-force' C-m \; \ 134 | split-window -v \; \ 135 | send-keys 'journalctl -fu validator --no-hostname | ccze -A' C-m \; \ 136 | select-pane -t 0 \; \ 137 | split-window -v \; \ 138 | send-keys 'journalctl -fu execution --no-hostname | ccze -A' C-m \; 139 | elif [[ -f /etc/systemd/system/execution.service || ${isIntegrated:-false} == "true" ]] && [[ -f /etc/systemd/system/validator.service ]]; then 140 | # Integrated EL-CL Node i.e. Caplin-Erigon 141 | tmux new-session -d -s logs \; \ 142 | send-keys 'journalctl -fu execution --no-hostname | ccze -A' C-m \; \ 143 | split-window -v \; \ 144 | split-window -h \; \ 145 | send-keys 'btop --utf-force' C-m \; \ 146 | select-pane -t 1 \; \ 147 | send-keys 'journalctl -fu validator --no-hostname | ccze -A' C-m \; 148 | elif [[ -f /etc/systemd/system/execution.service ]] && [[ -f /etc/systemd/system/consensus.service ]]; then 149 | # Full Node Only 150 | tmux new-session -d -s logs \; \ 151 | send-keys 'journalctl -fu consensus --no-hostname | ccze -A' C-m \; \ 152 | split-window -v \; \ 153 | split-window -h \; \ 154 | send-keys 'btop --utf-force' C-m \; \ 155 | select-pane -t 1 \; \ 156 | send-keys 'journalctl -fu execution --no-hostname | ccze -A' C-m \; 157 | elif [[ -f /etc/systemd/system/execution.service ]] && [[ ${isIntegrated:-false} == "true" ]]; then 158 | # Full Node Only for Integrated EL-CL 159 | tmux new-session -d -s logs \; \ 160 | send-keys 'journalctl -fu execution --no-hostname | ccze -A' C-m \; \ 161 | split-window -h \; \ 162 | select-pane -t 1 \; \ 163 | send-keys 'btop --utf-force' C-m \; \ 164 | select-layout even-vertical \; 165 | elif [[ -f /etc/systemd/system/validator.service ]]; then 166 | # Validator Client Only 167 | tmux new-session -d -s logs \; \ 168 | send-keys 'journalctl -fu validator --no-hostname | ccze -A' C-m \; \ 169 | split-window -h \; \ 170 | select-pane -t 1 \; \ 171 | send-keys 'btop --utf-force' C-m \; \ 172 | select-layout even-vertical \; 173 | fi 174 | fi 175 | 176 | # Attach to the tmux session 177 | tmux attach-session -t logs -------------------------------------------------------------------------------- /plugins/csm/plugin_csm_validator.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Author: coincashew.eth | coincashew.com 4 | # License: GNU GPL 5 | # Source: https://github.com/coincashew/ethpillar 6 | # Description: eth-duties helper script 7 | # 8 | # Made for home and solo stakers 🏠🥩 9 | 10 | # Base directory with scripts 11 | BASE_DIR=$HOME/git/ethpillar 12 | 13 | # Load functions 14 | source $BASE_DIR/functions.sh 15 | 16 | # Load environment variables, Lido CSM withdrawal address and fee recipient 17 | source $BASE_DIR/env 18 | 19 | # Get machine info 20 | _platform=$(get_platform) 21 | _arch=$(get_arch) 22 | 23 | # Variables 24 | DESCRIPTION="Lido CSM Validator Plugin: Activate an extra NIMBUS validator client to join Lido's CSM. Reuses existing EL/CL and installs a separate systemd validator service file." 25 | DOCUMENTATION="http://eth.coincashew.com" 26 | SOURCE_CODE="https://github.com/coincashew/ethpillar" 27 | PLUGIN_NAME="Lido CSM Validator Plugin" 28 | PLUGIN_SOURCE_PATH="$BASE_DIR/plugins/csm" 29 | export PLUGIN_INSTALL_PATH="/opt/ethpillar/plugin-csm" 30 | PLUGIN_ENV_VARS_FILE="csm_env_vars" 31 | PLUGIN_BINARY="nimbus_validator_client" 32 | PLUGIN_BINARY2="nimbus_beacon_node" 33 | export SERVICE_NAME="csm_nimbusvalidator" 34 | export SERVICE_ACCOUNT="csm_nimbus_validator" 35 | SERVICE_FILE="/etc/systemd/system/$SERVICE_NAME.service" 36 | 37 | # Load environment variables overrides 38 | [[ -f "$PLUGIN_INSTALL_PATH"/"$PLUGIN_ENV_VARS_FILE" ]] && source "$PLUGIN_INSTALL_PATH"/"$PLUGIN_ENV_VARS_FILE" 39 | 40 | # Gets software version from binary 41 | function _getCurrentVersion(){ 42 | VERSION="" 43 | test -f $PLUGIN_INSTALL_PATH/$PLUGIN_BINARY && VERSION=$($PLUGIN_INSTALL_PATH/$PLUGIN_BINARY --version | head -1 | grep -oE "v[0-9]+.[0-9]+.[0-9]+") 44 | } 45 | 46 | function _getLatestVersion(){ 47 | TAG_URL="https://api.github.com/repos/status-im/nimbus-eth2/releases/latest" 48 | CHANGES_URL="https://github.com/status-im/nimbus-eth2/releases" 49 | #Get tag name and remove leading 'v' 50 | TAG=$(curl -s $TAG_URL | jq -r .tag_name | sed 's/.*\(v[0-9]*\.[0-9]*\.[0-9]*\).*/\1/') 51 | # Exit in case of null tag 52 | [[ -z $TAG ]] || [[ $TAG == "null" ]] && echo "ERROR: Couldn't find the latest version tag" && exit 1 53 | } 54 | 55 | function _promptYesNo(){ 56 | if whiptail --title "Update ${CLIENT}" --yesno "Installed Version is: $VERSION\nLatest Version is: $TAG\n\nReminder: Always read the release notes for breaking changes: $CHANGES_URL\n\nDo you want to update $CLIENT to $TAG?" 15 78; then 57 | _downloadBinaries 58 | _promptViewLogs 59 | fi 60 | } 61 | 62 | function _promptViewLogs(){ 63 | if whiptail --title "Update complete" --yesno "Would you like to view logs and confirm everything is running properly?" 8 78; then 64 | sudo bash -c "journalctl -fu $SERVICE_NAME | ccze -A" 65 | fi 66 | } 67 | 68 | # Asks to update 69 | function _upgradeBinaries(){ 70 | CLIENT="Nimbus" 71 | _getCurrentVersion 72 | _getLatestVersion 73 | _promptYesNo 74 | } 75 | 76 | # Uninstall 77 | function _removeAll() { 78 | if whiptail --title "Uninstall $PLUGIN_NAME" --defaultno --yesno "Are you sure you want to remove $PLUGIN_NAME" 9 78; then 79 | sudo rm -rf "$PLUGIN_INSTALL_PATH" 80 | sudo systemctl stop $SERVICE_NAME 81 | sudo systemctl disable $SERVICE_NAME 82 | sudo rm $SERVICE_FILE 83 | sudo userdel $SERVICE_ACCOUNT 84 | whiptail --title "Uninstall finished" --msgbox "You have uninstalled $PLUGIN_NAME." 8 78 85 | fi 86 | } 87 | function _downloadBinaries(){ 88 | #Download and installbinaries 89 | RELEASE_URL="https://api.github.com/repos/status-im/nimbus-eth2/releases/latest" 90 | BINARIES_URL="$(curl -s $RELEASE_URL | jq -r ".assets[] | select(.name) | .browser_download_url" | grep --ignore-case "_${_platform}_${_arch}.*.tar.gz$")" 91 | echo Downloading URL: $BINARIES_URL 92 | cd $HOME 93 | wget -O nimbus.tar.gz $BINARIES_URL 94 | if [ ! -f nimbus.tar.gz ]; then 95 | echo "Error: Downloading nimbus archive failed!" 96 | exit 1 97 | fi 98 | tar -xzvf nimbus.tar.gz -C $HOME 99 | mv nimbus-eth2_${_platform}_${_arch}* nimbus 100 | test -f "$SERVICE_FILE" && sudo service $SERVICE_NAME stop 101 | test -f ${PLUGIN_INSTALL_PATH}/${PLUGIN_BINARY} && sudo rm ${PLUGIN_INSTALL_PATH}/${PLUGIN_BINARY} 102 | test -f ${PLUGIN_INSTALL_PATH}/${PLUGIN_BINARY2} && sudo rm ${PLUGIN_INSTALL_PATH}/${PLUGIN_BINARY2} 103 | sudo mv nimbus/build/${PLUGIN_BINARY} ${PLUGIN_INSTALL_PATH} 104 | sudo mv nimbus/build/${PLUGIN_BINARY2} ${PLUGIN_INSTALL_PATH} 105 | test -f "$SERVICE_FILE" && sudo service $SERVICE_NAME start 106 | rm -r nimbus 107 | rm nimbus.tar.gz 108 | } 109 | 110 | # Install logic 111 | function _installPlugin(){ 112 | function _doInstall(){ 113 | # Create service user 114 | sudo useradd --no-create-home --shell /bin/false csm_nimbus_validator 115 | 116 | # Install env file and service file 117 | sudo mkdir -p $PLUGIN_INSTALL_PATH 118 | sudo cp "$PLUGIN_SOURCE_PATH/$PLUGIN_ENV_VARS_FILE".example $PLUGIN_INSTALL_PATH/$PLUGIN_ENV_VARS_FILE 119 | sudo cp "$PLUGIN_SOURCE_PATH/$SERVICE_NAME".service.example $SERVICE_FILE 120 | sudo chown $USER:$USER -R $PLUGIN_INSTALL_PATH 121 | sudo systemctl enable $SERVICE_NAME 122 | 123 | # Setup validator 124 | # Load values 125 | source $PLUGIN_INSTALL_PATH/$PLUGIN_ENV_VARS_FILE 126 | sudo mkdir -p $DATA_DIR 127 | sudo chown -R $SERVICE_ACCOUNT:$SERVICE_ACCOUNT $DATA_DIR 128 | sudo chmod 700 $DATA_DIR 129 | 130 | # Download binaries 131 | _downloadBinaries 132 | 133 | # Update ENV values 134 | sed -i "s/FEE_RECIPIENT=\"\"/FEE_RECIPIENT=${CSM_FEE_RECIPIENT_ADDRESS}/g" "$PLUGIN_INSTALL_PATH"/"$PLUGIN_ENV_VARS_FILE" || true 135 | whiptail --msgbox "✅ Success: Lido CSM Validator Plugin Installed.\nYou can now generate or load validator keys from the menu." 8 78 136 | } 137 | 138 | # Prompt user for config values 139 | NETWORK=$(whiptail --title "Network" --menu \ 140 | "For which network are running CSM Validators?" 10 78 4 \ 141 | "mainnet" "Ethereum - Real ETH. Real staking rewards." \ 142 | "hoodi" "Long term Testnet - Ideal for CSM experimentation" \ 143 | "ephemery" "Short term Testnet - Good for testing setups. Monthly resets." \ 144 | "holesky" "deprecated Testnet" \ 145 | 3>&1 1>&2 2>&3) 146 | 147 | case $NETWORK in 148 | mainnet) 149 | CSM_FEE_RECIPIENT_ADDRESS=${CSM_FEE_RECIPIENT_ADDRESS_MAINNET} 150 | CSM_WITHDRAWAL_ADDRESS=${CSM_WITHDRAWAL_ADDRESS_MAINNET} 151 | ;; 152 | hoodi) 153 | CSM_FEE_RECIPIENT_ADDRESS=${CSM_FEE_RECIPIENT_ADDRESS_HOODI} 154 | CSM_WITHDRAWAL_ADDRESS=${CSM_WITHDRAWAL_ADDRESS_HOODI} 155 | ;; 156 | holesky) 157 | CSM_FEE_RECIPIENT_ADDRESS=${CSM_FEE_RECIPIENT_ADDRESS_HOLESKY} 158 | CSM_WITHDRAWAL_ADDRESS=${CSM_WITHDRAWAL_ADDRESS_HOLESKY} 159 | ;; 160 | ephemery) 161 | CSM_FEE_RECIPIENT_ADDRESS=${CSM_FEE_RECIPIENT_ADDRESS_HOLESKY} 162 | CSM_WITHDRAWAL_ADDRESS=${CSM_WITHDRAWAL_ADDRESS_HOLESKY} 163 | ;; 164 | esac 165 | 166 | MSG_ETHADDRESS="\nSet this to Lido's CSM Fee Recipient Address. 167 | \n${NETWORK}: ${CSM_FEE_RECIPIENT_ADDRESS} 168 | \nIn checksum format, ether the fee recipient address:" 169 | 170 | while true; do 171 | ETHADDRESS=$(whiptail --title "Fee Recipient Address" --inputbox "$MSG_ETHADDRESS" 15 78 --ok-button "Submit" 3>&1 1>&2 2>&3) 172 | if [ -z "$ETHADDRESS" ]; then exit; fi #pressed cancel 173 | if [[ "${ETHADDRESS}" =~ ^0x[a-fA-F0-9]{40}$ ]]; then 174 | break 175 | else 176 | whiptail --title "Error" --msgbox "Invalid ETH address. Try again." 8 78 177 | fi 178 | done 179 | 180 | MSG_CONFIRM="\nFor Lido CSM, I am installing a separate NIMBUS VALIDATOR client. 181 | \nThis validator client re-uses my current node's EL/CL and MEV relays. 182 | \n✅ ${NETWORK} fee recipient: ${CSM_FEE_RECIPIENT_ADDRESS} 183 | ✅ Service file: ${SERVICE_FILE} 184 | ✅ Config file: $PLUGIN_INSTALL_PATH/$PLUGIN_ENV_VARS_FILE" 185 | 186 | if whiptail --title "Confirm Install" --defaultno --yesno "$MSG_CONFIRM" 15 78; then 187 | _doInstall 188 | fi 189 | } 190 | 191 | function __viewPubkeyAndIndices(){ 192 | VC="Nimbus" 193 | #source ${BASE_DIR}/functions.sh 194 | getPubKeys "plugin_csm_validator" 195 | getIndices 196 | viewPubkeyAndIndices 197 | } 198 | 199 | function __generateKeys(){ 200 | source $BASE_DIR/manage_validator_keys.sh true 201 | export DATA_DIR 202 | generateNewValidatorKeys "plugin_csm_validator" 203 | } 204 | 205 | function __importKeys(){ 206 | source $BASE_DIR/manage_validator_keys.sh true 207 | export DATA_DIR 208 | importValidatorKeys "plugin_csm_validator" 209 | } 210 | 211 | function __addRestoreKeys(){ 212 | source $BASE_DIR/manage_validator_keys.sh true 213 | export DATA_DIR 214 | addRestoreValidatorKeys "plugin_csm_validator" 215 | } 216 | 217 | # Displays usage info 218 | function usage() { 219 | cat << EOF 220 | Usage: $(basename "$0") [-i] [-u] [-r] 221 | 222 | $PLUGIN_NAME Helper Script 223 | 224 | Options) 225 | -i Install $PLUGIN_NAME 226 | -u Upgrade $PLUGIN_NAME 227 | -r Remove $PLUGIN_NAME 228 | -h Display help 229 | 230 | About $PLUGIN_NAME) 231 | - $DESCRIPTION 232 | - Source code: $SOURCE_CODE 233 | - Documentation: $DOCUMENTATION 234 | EOF 235 | } 236 | 237 | setWhiptailColors 238 | 239 | # Process command line options 240 | while getopts :iurgmdhp opt; do 241 | case ${opt} in 242 | i ) _installPlugin ;; 243 | u ) _upgradeBinaries ;; 244 | r ) _removeAll ;; 245 | g ) __generateKeys ;; 246 | m ) __importKeys ;; 247 | d ) __addRestoreKeys ;; 248 | p ) __viewPubkeyAndIndices ;; 249 | h ) 250 | usage 251 | exit 0 252 | ;; 253 | \?) 254 | echo "Invalid option: -${OPTARG}" >&2 255 | usage 256 | exit 1 257 | ;; 258 | :) 259 | echo "Option -${OPTARG} requires an argument." >&2 260 | usage 261 | exit 1 262 | ;; 263 | esac 264 | done 265 | -------------------------------------------------------------------------------- /update_execution.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Author: coincashew.eth | coincashew.com 4 | # License: GNU GPL 5 | # Source: https://github.com/coincashew/ethpillar 6 | # Description: EthPillar is a one-liner setup tool and node management TUI 7 | # 8 | # Made for home and solo stakers 🏠🥩 9 | 10 | BASE_DIR=$HOME/git/ethpillar 11 | __OTHERTAG="" 12 | 13 | # Load functions 14 | # shellcheck disable=SC1091 15 | source "$BASE_DIR"/functions.sh 16 | 17 | # Get machine info 18 | _platform=$(get_platform) 19 | _arch=$(get_arch) 20 | 21 | function getCurrentVersion(){ 22 | EL_INSTALLED=$(curl -s -X POST -H "Content-Type: application/json" \ 23 | --data '{"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":2}' \ 24 | "${EL_RPC_ENDPOINT}" | jq -r '.result // empty') 25 | if [[ -z "$EL_INSTALLED" ]]; then 26 | VERSION="Client not running or still starting up. Unable to query version." 27 | return 28 | fi 29 | VERSION=$(sed -E 's/.*[v/]([0-9]+\.[0-9]+\.[0-9]+).*/\1/' <<< "$EL_INSTALLED") 30 | } 31 | 32 | function getClient(){ 33 | EL=$(cat /etc/systemd/system/execution.service | grep Description= | awk -F'=' '{print $2}' | awk '{print $1}') 34 | # Handle integrated ELs i.e. Erigon-Caplin 35 | EL=${EL%-*} 36 | } 37 | 38 | function selectCustomTag(){ 39 | case $EL in 40 | Nethermind) 41 | _repo="NethermindEth/nethermind" 42 | ;; 43 | Besu) 44 | _repo="hyperledger/besu" 45 | ;; 46 | Erigon) 47 | _repo="erigontech/erigon" 48 | ;; 49 | Geth) 50 | _repo="ethereum/go-ethereum" 51 | ;; 52 | Reth) 53 | _repo="paradigmxyz/reth" 54 | ;; 55 | *) 56 | error "❌ Unsupported or unknown client '$EL'." 57 | ;; 58 | esac 59 | local _listTags _tag 60 | _listTags=$(curl -fsSL https://api.github.com/repos/"${_repo}"/tags | jq -r '.[].name' | sort -hr) 61 | if [ -z "$_listTags" ]; then 62 | error "❌ Could not retrieve tags for ${_repo}. Try again later." 63 | fi 64 | info "ℹ️ Select the Version: Type the number to use. For example, 2 (for the 2nd most recent release)" 65 | select _tag in $_listTags; do 66 | if [ -n "$_tag" ]; then 67 | __OTHERTAG=$_tag 68 | break 69 | else 70 | error "❌ Invalid input. Enter the line # corresponding to a tag." 71 | fi 72 | done 73 | } 74 | 75 | function promptYesNo(){ 76 | # Remove front v if present 77 | if [[ "${VERSION#v}" == "${TAG#v}" ]]; then 78 | whiptail --title "Already updated" --msgbox "You are already on the latest version: ${VERSION#v}" 10 78 79 | if whiptail --title "Different Version of $EL" --defaultno --yesno "Would you like to install a different version?" 8 78; then 80 | selectCustomTag 81 | updateClient "$__OTHERTAG" 82 | promptViewLogs 83 | fi 84 | return 85 | fi 86 | __MSG="Installed Version is: ${VERSION#v}\nLatest Version is: ${TAG#v}\n\nReminder: Always read the release notes for breaking changes: $CHANGES_URL\n\nDo you want to update $EL to ${TAG#v}?" 87 | __SELECTTAG=$(whiptail --title "🔧 Update Execution Client" --menu \ 88 | "$__MSG" 18 78 2 \ 89 | "LATEST" "| Installs ${TAG#v}, the latest release" \ 90 | "OTHER " "| I will select a different version" \ 91 | 3>&1 1>&2 2>&3) 92 | if [ -z "$__SELECTTAG" ]; then exit; fi # pressed cancel 93 | if [[ $__SELECTTAG == "LATEST" ]]; then 94 | updateClient "LATEST" 95 | promptViewLogs 96 | else 97 | selectCustomTag 98 | updateClient "$__OTHERTAG" 99 | promptViewLogs 100 | fi 101 | } 102 | 103 | function promptViewLogs(){ 104 | if whiptail --title "Update complete - $EL" --yesno "Would you like to view logs and confirm everything is running properly?" 8 78; then 105 | sudo bash -c 'journalctl -fu execution | ccze -A' 106 | fi 107 | } 108 | 109 | function getLatestVersion(){ 110 | case $EL in 111 | Nethermind) 112 | TAG_URL="https://api.github.com/repos/NethermindEth/nethermind/releases/latest" 113 | CHANGES_URL="https://github.com/NethermindEth/nethermind/releases" 114 | ;; 115 | Besu) 116 | TAG_URL="https://api.github.com/repos/hyperledger/besu/releases/latest" 117 | CHANGES_URL="https://github.com/hyperledger/besu/releases" 118 | ;; 119 | Erigon) 120 | TAG_URL="https://api.github.com/repos/erigontech/erigon/releases/latest" 121 | CHANGES_URL="https://github.com/erigontech/erigon/releases" 122 | ;; 123 | Geth) 124 | TAG_URL="https://api.github.com/repos/ethereum/go-ethereum/releases/latest" 125 | CHANGES_URL="https://github.com/ethereum/go-ethereum/releases" 126 | ;; 127 | Reth) 128 | TAG_URL="https://api.github.com/repos/paradigmxyz/reth/releases/latest" 129 | CHANGES_URL="https://github.com/paradigmxyz/reth/releases" 130 | ;; 131 | *) 132 | error "❌ Unsupported or unknown client '$EL'." 133 | ;; 134 | esac 135 | #Get tag name 136 | TAG=$(curl -s "$TAG_URL" | jq -r .tag_name) 137 | # Exit in case of null tag 138 | if [[ -z $TAG ]] || [[ $TAG == "null" ]]; then 139 | error "❌ Couldn't find the latest version tag" 140 | fi 141 | } 142 | 143 | function updateClient(){ 144 | if [[ "$1" == "LATEST" ]]; then 145 | _URL_SUFFIX="releases/latest" 146 | else 147 | _URL_SUFFIX="releases/tags/$1" 148 | fi 149 | case $EL in 150 | Nethermind) 151 | [[ "${_arch}" == "amd64" ]] && _architecture="x64" || _architecture="arm64" 152 | RELEASE_URL="https://api.github.com/repos/NethermindEth/nethermind/$_URL_SUFFIX" 153 | BINARIES_URL=$(curl -s "$RELEASE_URL" | jq -r ".assets[] | select(.name) | .browser_download_url" | grep --ignore-case "${_platform}"-"${_architecture}") 154 | info "✅ Downloading URL: $BINARIES_URL" 155 | cd "$HOME" || true 156 | wget -O nethermind.zip "$BINARIES_URL" || error "❌ Unable to wget file" 157 | unzip -o nethermind.zip -d "$HOME"/nethermind || error "❌ Unable to unzip file" 158 | rm nethermind.zip 159 | sudo systemctl stop execution 160 | sudo rm -rf /usr/local/bin/nethermind 161 | sudo mv "$HOME"/nethermind /usr/local/bin/nethermind || error "❌ Unable to move file" 162 | sudo systemctl start execution 163 | ;; 164 | Besu) 165 | updateJRE 166 | RELEASE_URL="https://api.github.com/repos/hyperledger/besu/$_URL_SUFFIX" 167 | TAG=$(curl -s "$RELEASE_URL" | jq -r .tag_name) 168 | BINARIES_URL="https://github.com/hyperledger/besu/releases/download/$TAG/besu-$TAG.tar.gz" 169 | info "✅ Downloading URL: $BINARIES_URL" 170 | cd "$HOME" || true 171 | wget -O besu.tar.gz "$BINARIES_URL" || error "❌ Unable to wget file" 172 | tar -xzvf besu.tar.gz -C "$HOME" || error "❌ Unable to untar file" 173 | sudo mv besu-"${TAG}" besu 174 | sudo systemctl stop execution 175 | sudo rm -rf /usr/local/bin/besu 176 | sudo mv "$HOME"/besu /usr/local/bin/besu || error "❌ Unable to move file" 177 | sudo systemctl start execution 178 | rm besu.tar.gz 179 | ;; 180 | Erigon) 181 | RELEASE_URL="https://api.github.com/repos/erigontech/erigon/$_URL_SUFFIX" 182 | BINARIES_URL=$(curl -s "$RELEASE_URL" | jq -r ".assets[] | select(.name) | .browser_download_url" | grep --ignore-case "${_platform}"_"${_arch}".tar.gz) 183 | info "✅ Downloading URL: $BINARIES_URL" 184 | cd "$HOME" || true 185 | wget -O erigon.tar.gz "$BINARIES_URL" || error "❌ Unable to wget file" 186 | tar -xzvf erigon.tar.gz -C "$HOME" || error "❌ Unable to untar file" 187 | mv erigon_*_"${_arch}" erigon 188 | sudo systemctl stop execution 189 | sudo mv "$HOME"/erigon/erigon /usr/local/bin || error "❌ Unable to move file" 190 | sudo systemctl start execution 191 | rm -rf erigon erigon.tar.gz 192 | ;; 193 | Geth) 194 | # Convert to lower case 195 | _platform=${_platform,,} 196 | RELEASE_URL="https://geth.ethereum.org/downloads" 197 | #https://gethstore.blob.core.windows.net/builds/geth-linux-386-1.16.3-09786041.tar.gz 198 | # Remove front v if present 199 | if [[ "$1" == "LATEST" ]]; then 200 | _URL_SUFFIX="" 201 | else 202 | _URL_SUFFIX="-${1#v}-" 203 | fi 204 | FILE="https://gethstore.blob.core.windows.net/builds/geth-${_platform}-${_arch}${_URL_SUFFIX}[a-zA-Z0-9./?=_%:-]*.tar.gz" 205 | BINARIES_URL=$(curl -s $RELEASE_URL | grep -Eo "$FILE" | head -1) 206 | info "✅ Downloading URL: $BINARIES_URL" 207 | cd "$HOME" || true 208 | wget -O geth.tar.gz "$BINARIES_URL" || error "❌ Unable to wget file" 209 | tar -xzvf geth.tar.gz -C "$HOME" --strip-components=1 || error "❌ Unable to untar file" 210 | sudo systemctl stop execution 211 | sudo mv "$HOME"/geth /usr/local/bin || error "❌ Unable to move file" 212 | sudo systemctl start execution 213 | rm geth.tar.gz COPYING 214 | ;; 215 | Reth) 216 | # Convert to lower case 217 | _platform=${_platform,,} 218 | [[ "${_arch}" == "amd64" ]] && _architecture="x86_64" || _architecture="aarch64" 219 | RELEASE_URL="https://api.github.com/repos/paradigmxyz/reth/$_URL_SUFFIX" 220 | TAG=$(curl -s "$RELEASE_URL" | jq -r .tag_name) 221 | BINARIES_URL="https://github.com/paradigmxyz/reth/releases/download/$TAG/reth-$TAG-${_architecture}-unknown-${_platform}-gnu.tar.gz" 222 | info "✅ Downloading URL: $BINARIES_URL" 223 | cd "$HOME" || true 224 | wget -O reth.tar.gz "$BINARIES_URL" || error "❌ Unable to wget file" 225 | tar -xzvf reth.tar.gz -C "$HOME" || error "❌ Unable to untar file" 226 | rm reth.tar.gz 227 | sudo systemctl stop execution 228 | sudo mv "$HOME"/reth /usr/local/bin || error "❌ Unable to move file" 229 | sudo systemctl start execution 230 | ;; 231 | esac 232 | } 233 | 234 | function updateJRE(){ 235 | # Check if OpenJDK-21-JRE or OpenJDK-21-JDK is already installed 236 | if dpkg --list | grep -q -E "openjdk-21-jre|openjdk-21-jdk"; then 237 | info "✅ OpenJDK-21-JRE or OpenJDK-21-JDK is already installed. Skipping installation." 238 | else 239 | # Install OpenJDK-21-JRE 240 | sudo apt-get update 241 | sudo apt-get install -y openjdk-21-jre 242 | 243 | # Check if the installation was successful 244 | # shellcheck disable=SC2181 245 | if [ $? -eq 0 ]; then 246 | info "✅ OpenJDK-21-JRE installed successfully!" 247 | else 248 | error "❌ Error installing OpenJDK-21-JRE. Please check the error log." 249 | fi 250 | fi 251 | } 252 | 253 | getClient 254 | getCurrentVersion 255 | getLatestVersion 256 | promptYesNo -------------------------------------------------------------------------------- /ethereum-metrics-exporter.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Author: coincashew.eth | coincashew.com 4 | # License: GNU GPL 5 | # Source: https://github.com/coincashew/ethpillar 6 | # Description: Installs ethereum-metrics-exporter, and supporting tools: grafana, prometheus 7 | # 8 | # Made for home and solo stakers 🏠🥩 9 | 10 | BASE_DIR="$HOME"/git/ethpillar 11 | 12 | # Load functions 13 | source "$BASE_DIR"/functions.sh 14 | 15 | # Get machine info 16 | _platform=$(get_platform) 17 | _arch=$(get_arch) 18 | 19 | # Variables 20 | GITHUB_URL=https://api.github.com/repos/ethpandaops/ethereum-metrics-exporter/releases/latest 21 | GITHUB_RELEASE_NODES=https://github.com/ethpandaops/ethereum-metrics-exporter/releases 22 | ETHEREUM_METRICS_EXPORTER_OPTIONS=( 23 | --metrics-port 9099 24 | --consensus-url=http://localhost:5052 25 | --execution-url=http://localhost:8545 26 | ) 27 | GRAFANA_DIR=/etc/grafana 28 | PROMETHEUS_DIR=/etc/prometheus 29 | 30 | # Asks to update 31 | function upgradeBinaries(){ 32 | getLatestVersion 33 | if whiptail --title "Update ethereum-metrics-exporter, grafana, prometheus, node-exporter" --yesno "Latest Version of ethereum-metrics-exporter is: $TAG\n\nReminder: Always read the release notes for breaking changes: $GITHUB_RELEASE_NODES\n\nDo you want to update to $TAG?" 10 78; then 34 | downloadClient 35 | upgradeGrafanaPrometheus 36 | promptViewLogs 37 | fi 38 | } 39 | 40 | # Asks to view logs 41 | function promptViewLogs(){ 42 | if whiptail --title "View Logs" --yesno "Would you like to view logs and confirm everything is running properly?" 8 78; then 43 | sudo bash -c 'journalctl -fu ethereum-metrics-exporter | ccze -A' 44 | fi 45 | } 46 | 47 | # Gets latest tag of ethereum-metrics-exporter 48 | function getLatestVersion(){ 49 | TAG=$(curl -s $GITHUB_URL | jq -r .tag_name ) 50 | # Exit in case of null tag 51 | [[ -z $TAG ]] || [[ $TAG == "null" ]] && echo "ERROR: Couldn't find the latest version tag" && exit 1 52 | } 53 | 54 | # Downloads latest release of ethereum-metrics-exporter 55 | function downloadClient(){ 56 | BINARIES_URL="$(curl -s "$GITHUB_URL" | jq -r ".assets[] | select(.name) | .browser_download_url" | grep --ignore-case "${_platform}"_"${_arch}".tar.gz$)" 57 | echo Downloading URL: "$BINARIES_URL" 58 | cd "$HOME" || true 59 | # Download 60 | wget -O ethereum-metrics-exporter.tar.gz "$BINARIES_URL" 61 | # Untar 62 | tar -xzvf ethereum-metrics-exporter.tar.gz -C "$HOME" 63 | # Cleanup 64 | rm ethereum-metrics-exporter.tar.gz README.md 65 | # Install binary 66 | if systemctl is-active --quiet ethereum-metrics-exporter ; then sudo systemctl stop ethereum-metrics-exporter; fi 67 | sudo mv "$HOME"/ethereum-metrics-exporter-* /usr/local/bin/ethereum-metrics-exporter 68 | sudo systemctl start ethereum-metrics-exporter 69 | } 70 | 71 | # Removes ethereum-metrics-exporter, Grafana and Prometheus 72 | function removeAll() { 73 | if whiptail --title "Uninstall Monitoring" --defaultno --yesno "Are you sure you want to remove all monitoring tools?\n(grafana/prometheus/ethereum-metrics-exporter/node-exporter)" 9 78; then 74 | sudo systemctl disable ethereum-metrics-exporter 75 | sudo systemctl stop ethereum-metrics-exporter 76 | 77 | sudo rm /etc/systemd/system/ethereum-metrics-exporter.service 78 | sudo rm /usr/local/bin/ethereum-metrics-exporter 79 | 80 | sudo systemctl disable grafana-server prometheus prometheus-node-exporter 81 | sudo systemctl stop grafana-server prometheus prometheus-node-exporter 82 | sudo apt remove -y grafana prometheus prometheus-node-exporter 83 | sudo rm /etc/apt/sources.list.d/grafana.list 84 | whiptail --title "Uninstall finished" --msgbox "You have uninstalled all monitoring tools." 8 78 85 | fi 86 | } 87 | 88 | # Installs Grafana, Prometheus, Node-Exporter 89 | function installGrafanaPrometheus(){ 90 | sudo apt-get install -y software-properties-common wget apt-transport-https 91 | sudo wget -q -O /usr/share/keyrings/grafana.key https://apt.grafana.com/gpg.key 92 | echo "deb [signed-by=/usr/share/keyrings/grafana.key] https://apt.grafana.com stable main" | sudo tee -a /etc/apt/sources.list.d/grafana.list 93 | sudo apt-get update && sudo apt-get install -y grafana prometheus prometheus-node-exporter 94 | sudo systemctl enable grafana-server prometheus prometheus-node-exporter 95 | sudo systemctl restart grafana-server prometheus prometheus-node-exporter 96 | 97 | # Setup prometheus.yml config file 98 | sudo bash -c "cat << 'EOF' > ${PROMETHEUS_DIR}/prometheus.yml 99 | rule_files: 100 | - alert.rules.yml 101 | 102 | global: 103 | scrape_interval: 15s # By default, scrape targets every 15 seconds. 104 | 105 | # A scrape configuration containing exactly one endpoint to scrape: 106 | # Here it's Prometheus itself. 107 | scrape_configs: 108 | - job_name: 'ethereum-metrics-exporter' 109 | static_configs: 110 | - targets: ['localhost:9099'] 111 | - job_name: 'node_exporter' 112 | static_configs: 113 | - targets: ['localhost:9100'] 114 | EOF" 115 | } 116 | 117 | # Upgrade Grafana, Prometheus, Node-Exporter 118 | function upgradeGrafanaPrometheus(){ 119 | sudo apt-get update && sudo apt-get install --only-upgrade -y grafana prometheus prometheus-node-exporter 120 | sudo systemctl restart grafana-server prometheus prometheus-node-exporter 121 | } 122 | 123 | # Installs ethereum-metrics-exporter as a systemd service 124 | function installSystemd(){ 125 | # Create service user 126 | sudo adduser --system --no-create-home --group ethereum-metrics-exporter 127 | # Create systemd service 128 | sudo bash -c "cat << 'EOF' > /etc/systemd/system/ethereum-metrics-exporter.service 129 | [Unit] 130 | Description=Ethereum Metrics Exporter Service 131 | Wants=network-online.target 132 | After=network-online.target 133 | After=consensus.service 134 | Documentation=https://docs.coincashew.com 135 | 136 | [Service] 137 | Type=simple 138 | User=ethereum-metrics-exporter 139 | Group=ethereum-metrics-exporter 140 | Restart=on-failure 141 | RestartSec=3 142 | KillSignal=SIGINT 143 | TimeoutStopSec=900 144 | ExecStart=/usr/local/bin/ethereum-metrics-exporter $(echo "${ETHEREUM_METRICS_EXPORTER_OPTIONS[@]}") 145 | 146 | [Install] 147 | WantedBy=multi-user.target 148 | EOF" 149 | sudo systemctl daemon-reload 150 | sudo systemctl enable ethereum-metrics-exporter 151 | } 152 | 153 | # Asks whether to open grafana access to local network 154 | function allowLocalAccessToGrafana(){ 155 | echo -e "\e[32m:: Open firewall to Grafana for local access ::\e[0m" 156 | echo "Allow access to Grafana from within your local network? [y|n]" 157 | read -rsn1 yn 158 | if [[ ${yn} = [Yy]* ]]; then 159 | sudo ufw allow from "$network_current" to any port 3000 proto tcp comment 'Allow local LAN access to Grafana Port' 160 | fi 161 | } 162 | 163 | # Sets the default Prometheus datasource to http://localhost:9090 164 | function configureDataSource(){ 165 | sudo bash -c "cat << 'EOF' > $GRAFANA_DIR/provisioning/datasources/datasources.yml 166 | apiVersion: 1 167 | datasources: 168 | - name: Prometheus 169 | type: prometheus 170 | url: http://localhost:9090 171 | access: proxy 172 | isDefault: true 173 | EOF" 174 | } 175 | 176 | function showNextSteps(){ 177 | cat << EOF 178 | 179 | Congrats! 180 | Successfully installed monitoring tools: ethereum-metrics-exporter, grafana, prometheus, node-exporter 181 | 182 | Access Grafana at: 183 | http://127.0.0.1:3000 184 | or 185 | http://${ip_current}:3000 186 | 187 | Login to Grafana with: 188 | Username: admin 189 | Password: admin 190 | 191 | To view dashboards, 192 | 1) Click Dashboards in the primary menu. 193 | 194 | EOF 195 | echo "Press ENTER to continue" 196 | read 197 | } 198 | 199 | function provisionDashboards(){ 200 | # Install jq if not installed 201 | if ! command -v jq >/dev/null 2>&1 ; then sudo apt-get install jq; fi 202 | 203 | # Create yml file to configure provisioning 204 | sudo bash -c "cat << 'EOF' > $GRAFANA_DIR/provisioning/dashboards/dashboard.yml 205 | apiVersion: 1 206 | 207 | providers: 208 | - name: 'Prometheus' 209 | orgId: 1 210 | folder: '' 211 | folderUid: '' 212 | type: file 213 | disableDeletion: false 214 | updateIntervalSeconds: 10 215 | allowUiUpdates: true 216 | options: 217 | path: $GRAFANA_DIR/provisioning/dashboards 218 | EOF" 219 | 220 | # Download dashboards into provision directory 221 | # Ethereum-Metrics-Exporter Dashboard 222 | ID=16277 223 | REVISION=$(wget -qO - https://grafana.com/api/dashboards/$ID | jq .revision) 224 | URL=https://grafana.com/api/dashboards/$ID/revisions/$REVISION/download 225 | JSON_FILE=$GRAFANA_DIR/provisioning/dashboards/ethereum-metrics-exporter.json 226 | sudo bash -c "wget -qO - $URL | jq 'walk(if . == \"\${DS_PROMETHEUS}\" then \"Prometheus\" else . end)' > $JSON_FILE" 227 | 228 | # Node Exporter Dashboard by StarsL 229 | ID=11074 230 | REVISION=$(wget -qO - https://grafana.com/api/dashboards/$ID | jq .revision) 231 | URL=https://grafana.com/api/dashboards/$ID/revisions/$REVISION/download 232 | JSON_FILE=$GRAFANA_DIR/provisioning/dashboards/node-exporter-for-prometheus-dashboard.json 233 | sudo bash -c "wget -qO - $URL | jq 'walk(if . == \"\${DS__VICTORIAMETRICS}\" then \"Prometheus\" else . end)' > $JSON_FILE" 234 | 235 | # Delete any failed 0 size dashboards 236 | find $GRAFANA_DIR/provisioning/dashboards -type f -size 0 -delete 237 | 238 | # Install default alert rules and restart prometheus 239 | sudo cp "$(dirname "$(realpath "${BASH_SOURCE[0]}")")"/alert.rules.yml $PROMETHEUS_DIR 240 | sudo systemctl restart prometheus 241 | } 242 | 243 | # Displays usage info 244 | function usage() { 245 | cat << EOF 246 | Usage: $(basename "$0") [-i] [-u] [-r] 247 | 248 | Ethereum-Metrics-Exporter Monitoring Helper Script 249 | 250 | Options) 251 | -i Install ethereum-metrics-exporter, grafana, prometheus, node-exporter as a systemd service 252 | -u Upgrade ethereum-metrics-exporter, grafana, prometheus, node-exporter 253 | -r Remove all monitoring tools 254 | -h Display help 255 | EOF 256 | } 257 | 258 | function installMonitoring(){ 259 | MSG_ABOUT="🚨 Monitoring with Ethereum Metrics Exporter & Grafana & Prometheus & node-exporter 260 | \nFeatures: 261 | \n- Dashboards: Simplify observation across your node's resources and performance 262 | \n- Alerts: Customize alerts to notify you of any issues 263 | \n- Data: Real-time metrics and statistics 264 | \n- Privacy: Run locally using your node 265 | \nSource Code: https://github.com/ethpandaops/ethereum-metrics-exporter 266 | \nContinue to install?" 267 | 268 | if ! whiptail --title "Monitoring: Installation" --yesno "$MSG_ABOUT" 22 78; then exit; fi 269 | installGrafanaPrometheus 270 | installSystemd 271 | configureDataSource 272 | provisionDashboards 273 | downloadClient 274 | getNetworkConfig 275 | allowLocalAccessToGrafana 276 | showNextSteps 277 | promptViewLogs 278 | } 279 | 280 | # Process command line options 281 | while getopts :iurh opt; do 282 | case ${opt} in 283 | i ) installMonitoring ;; 284 | u ) upgradeBinaries ;; 285 | r ) removeAll ;; 286 | h) 287 | usage 288 | exit 0 289 | ;; 290 | \?) 291 | echo "Invalid option: -${OPTARG}" >&2 292 | usage 293 | exit 1 294 | ;; 295 | :) 296 | echo "Option -${OPTARG} requires an argument." >&2 297 | usage 298 | exit 1 299 | ;; 300 | esac 301 | done 302 | -------------------------------------------------------------------------------- /update_consensus.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Author: coincashew.eth | coincashew.com 4 | # License: GNU GPL 5 | # Source: https://github.com/coincashew/ethpillar 6 | # Description: EthPillar is a one-liner setup tool and node management TUI 7 | # 8 | # Made for home and solo stakers 🏠🥩 9 | 10 | BASE_DIR=$HOME/git/ethpillar 11 | __OTHERTAG="" 12 | 13 | # Load functions 14 | # shellcheck disable=SC1091 15 | source "$BASE_DIR"/functions.sh 16 | 17 | # Get machine info 18 | _platform=$(get_platform) 19 | _arch=$(get_arch) 20 | 21 | function selectCustomTag(){ 22 | case $CLIENT in 23 | Lighthouse) 24 | _repo="sigp/lighthouse" 25 | ;; 26 | Lodestar) 27 | _repo="ChainSafe/lodestar" 28 | ;; 29 | Teku) 30 | _repo="ConsenSys/teku" 31 | ;; 32 | Nimbus) 33 | _repo="status-im/nimbus-eth2" 34 | ;; 35 | Prysm) 36 | _repo="OffchainLabs/prysm" 37 | ;; 38 | *) 39 | error "❌ Unsupported or unknown client '$CLIENT'." 40 | ;; 41 | esac 42 | local _listTags _tag 43 | _listTags=$(curl -fsSL https://api.github.com/repos/"${_repo}"/tags | jq -r '.[].name' | sort -hr) 44 | if [ -z "$_listTags" ]; then 45 | error "❌ Could not retrieve tags for ${_repo}. Try again later." 46 | fi 47 | info "ℹ️ Select the Version: Type the number to use. For example, 2 (for the 2nd most recent release)" 48 | select _tag in $_listTags; do 49 | if [ -n "$_tag" ]; then 50 | __OTHERTAG=$_tag 51 | break 52 | else 53 | error "❌ Invalid input. Enter the line # corresponding to a tag." 54 | fi 55 | done 56 | } 57 | 58 | function promptYesNo(){ 59 | # Remove front v if present 60 | if [[ "${VERSION#v}" == "${TAG#v}" ]]; then 61 | whiptail --title "Already updated" --msgbox "You are already on the latest version: ${VERSION#v}" 10 78 62 | if whiptail --title "Different Version of ${CLIENT}" --defaultno --yesno "Would you like to install a different version?" 8 78; then 63 | selectCustomTag 64 | updateClient "$__OTHERTAG" 65 | promptViewLogs 66 | fi 67 | return 68 | fi 69 | __MSG="Installed Version is: ${VERSION#v}\nLatest Version is: ${TAG#v}\n\nReminder: Always read the release notes for breaking changes: $CHANGES_URL\n\nDo you want to update $CLIENT to ${TAG#v}?" 70 | __SELECTTAG=$(whiptail --title "🔧 Update ${CLIENT}" --menu \ 71 | "$__MSG" 18 78 2 \ 72 | "LATEST" "| Installs ${TAG#v}, the latest release" \ 73 | "OTHER " "| I will select a different version" \ 74 | 3>&1 1>&2 2>&3) 75 | if [ -z "$__SELECTTAG" ]; then exit; fi # pressed cancel 76 | if [[ $__SELECTTAG == "LATEST" ]]; then 77 | updateClient "LATEST" 78 | promptViewLogs 79 | else 80 | selectCustomTag 81 | updateClient "$__OTHERTAG" 82 | promptViewLogs 83 | fi 84 | } 85 | 86 | function promptViewLogs(){ 87 | if whiptail --title "Update complete" --yesno "Would you like to view logs and confirm everything is running properly?" 8 78; then 88 | if [[ ${NODE_MODE} =~ "Validator Client Only" ]]; then 89 | sudo bash -c 'journalctl -fu validator | ccze -A' 90 | else 91 | sudo bash -c 'journalctl -fu consensus | ccze -A' 92 | fi 93 | fi 94 | } 95 | 96 | function getLatestVersion(){ 97 | case "$CLIENT" in 98 | Lighthouse) 99 | TAG_URL="https://api.github.com/repos/sigp/lighthouse/releases/latest" 100 | CHANGES_URL="https://github.com/sigp/lighthouse/releases" 101 | ;; 102 | Lodestar) 103 | TAG_URL="https://api.github.com/repos/ChainSafe/lodestar/releases/latest" 104 | CHANGES_URL="https://github.com/ChainSafe/lodestar/releases" 105 | ;; 106 | Teku) 107 | TAG_URL="https://api.github.com/repos/ConsenSys/teku/releases/latest" 108 | CHANGES_URL="https://github.com/ConsenSys/teku/releases" 109 | ;; 110 | Nimbus) 111 | TAG_URL="https://api.github.com/repos/status-im/nimbus-eth2/releases/latest" 112 | CHANGES_URL="https://github.com/status-im/nimbus-eth2/releases" 113 | ;; 114 | Prysm) 115 | TAG_URL="https://api.github.com/repos/OffchainLabs/prysm/releases/latest" 116 | CHANGES_URL="https://github.com/OffchainLabs/prysm/releases" 117 | ;; 118 | *) 119 | error "❌ Unsupported or unknown client '$CLIENT'." 120 | ;; 121 | esac 122 | #Get tag name and remove leading 'v' 123 | TAG=$(curl -s $TAG_URL | jq -r .tag_name | sed 's/.*\(v[0-9]*\.[0-9]*\.[0-9]*\).*/\1/') 124 | # Exit in case of null tag 125 | if [[ -z $TAG ]] || [[ $TAG == "null" ]]; then 126 | error "❌ Couldn't find the latest version tag" 127 | fi 128 | } 129 | 130 | function updateClient(){ 131 | if [[ "$1" == "LATEST" ]]; then 132 | _URL_SUFFIX="releases/latest" 133 | else 134 | _URL_SUFFIX="releases/tags/$1" 135 | fi 136 | case "$CLIENT" in 137 | Lighthouse) 138 | [[ "${_arch}" == "amd64" ]] && _architecture="x86_64" || _architecture="aarch64" 139 | RELEASE_URL="https://api.github.com/repos/sigp/lighthouse/$_URL_SUFFIX" 140 | BINARIES_URL=$(curl -s "$RELEASE_URL" | jq -r ".assets[] | select(.name) | .browser_download_url" | grep --ignore-case "${_architecture}"-unknown-"${_platform}"-gnu.tar.gz$) 141 | info "✅ Downloading URL: $BINARIES_URL" 142 | cd "$HOME" || true 143 | wget -O lighthouse.tar.gz "$BINARIES_URL" || error "❌ Unable to wget file" 144 | tar -xzvf lighthouse.tar.gz -C "$HOME" || error "❌ Unable to untar file" 145 | rm lighthouse.tar.gz 146 | test -f /etc/systemd/system/consensus.service && sudo systemctl stop consensus 147 | test -f /etc/systemd/system/validator.service && sudo service validator stop 148 | sudo rm /usr/local/bin/lighthouse 149 | sudo mv "$HOME"/lighthouse /usr/local/bin/lighthouse || error "❌ Unable to move file" 150 | test -f /etc/systemd/system/consensus.service && sudo systemctl start consensus 151 | test -f /etc/systemd/system/validator.service && sudo service validator start 152 | ;; 153 | Lodestar) 154 | RELEASE_URL="https://api.github.com/repos/ChainSafe/lodestar/$_URL_SUFFIX" 155 | LATEST_TAG=$(curl -s "$RELEASE_URL" | jq -r ".tag_name") 156 | BINARIES_URL="https://github.com/ChainSafe/lodestar/releases/download/${LATEST_TAG}/lodestar-${LATEST_TAG}-${_platform}-${_arch}.tar.gz" 157 | info "✅ Downloading URL: $BINARIES_URL" 158 | cd "$HOME" || true 159 | wget -O lodestar.tar.gz "$BINARIES_URL" || error "❌ Unable to wget file" 160 | tar -xzvf lodestar.tar.gz -C "$HOME" || error "❌ Unable to untar file" 161 | rm lodestar.tar.gz 162 | test -f /etc/systemd/system/consensus.service && sudo systemctl stop consensus 163 | test -f /etc/systemd/system/validator.service && sudo service validator stop 164 | sudo rm -rf /usr/local/bin/lodestar 165 | sudo mkdir -p /usr/local/bin/lodestar 166 | sudo mv "$HOME"/lodestar /usr/local/bin/lodestar || error "❌ Unable to move file" 167 | test -f /etc/systemd/system/consensus.service && sudo systemctl start consensus 168 | test -f /etc/systemd/system/validator.service && sudo service validator start 169 | ;; 170 | Teku) 171 | updateJRE 172 | RELEASE_URL="https://api.github.com/repos/ConsenSys/teku/$_URL_SUFFIX" 173 | LATEST_TAG=$(curl -s "$RELEASE_URL" | jq -r ".tag_name") 174 | BINARIES_URL="https://artifacts.consensys.net/public/teku/raw/names/teku.tar.gz/versions/${LATEST_TAG}/teku-${LATEST_TAG}.tar.gz" 175 | info "✅ Downloading URL: $BINARIES_URL" 176 | cd "$HOME" || true 177 | wget -O teku.tar.gz "$BINARIES_URL" || error "❌ Unable to wget file" 178 | tar -xzvf teku.tar.gz -C "$HOME" || error "❌ Unable to untar file" 179 | mv teku-"${LATEST_TAG}" teku 180 | rm teku.tar.gz 181 | test -f /etc/systemd/system/consensus.service && sudo systemctl stop consensus 182 | test -f /etc/systemd/system/validator.service && sudo service validator stop 183 | sudo rm -rf /usr/local/bin/teku 184 | sudo mv "$HOME"/teku /usr/local/bin/teku || error "❌ Unable to move file" 185 | test -f /etc/systemd/system/consensus.service && sudo systemctl start consensus 186 | test -f /etc/systemd/system/validator.service && sudo service validator start 187 | ;; 188 | Nimbus) 189 | RELEASE_URL="https://api.github.com/repos/status-im/nimbus-eth2/$_URL_SUFFIX" 190 | BINARIES_URL=$(curl -s "$RELEASE_URL" | jq -r ".assets[] | select(.name) | .browser_download_url" | grep --ignore-case "_${_platform}_${_arch}.*.tar.gz$") 191 | info "✅ Downloading URL: $BINARIES_URL" 192 | cd "$HOME" || true 193 | wget -O nimbus.tar.gz "$BINARIES_URL" || error "❌ Unable to wget file" 194 | tar -xzvf nimbus.tar.gz -C "$HOME" || error "❌ Unable to untar file" 195 | mv nimbus-eth2_"${_platform}"_"${_arch}"* nimbus 196 | test -f /etc/systemd/system/consensus.service && sudo systemctl stop consensus 197 | test -f /etc/systemd/system/validator.service && sudo service validator stop 198 | sudo rm /usr/local/bin/nimbus_beacon_node 199 | sudo rm /usr/local/bin/nimbus_validator_client 200 | sudo mv nimbus/build/nimbus_beacon_node /usr/local/bin || error "❌ Unable to move file" 201 | sudo mv nimbus/build/nimbus_validator_client /usr/local/bin || error "❌ Unable to move file" 202 | test -f /etc/systemd/system/consensus.service && sudo systemctl start consensus 203 | test -f /etc/systemd/system/validator.service && sudo service validator start 204 | rm -r nimbus 205 | rm nimbus.tar.gz 206 | ;; 207 | Prysm) 208 | cd "$HOME" || true 209 | if [[ "$1" == "LATEST" ]]; then 210 | prysm_version=$(curl -f -s https://prysmaticlabs.com/releases/latest) 211 | else 212 | prysm_version="$1" 213 | fi 214 | # Convert to lower case 215 | _platform=${_platform,,} 216 | file_beacon=beacon-chain-${prysm_version}-${_platform}-${_arch} 217 | file_validator=validator-${prysm_version}-${_platform}-${_arch} 218 | file_prysmctl=prysmctl-${prysm_version}-${_platform}-${_arch} 219 | curl -f -L "https://prysmaticlabs.com/releases/${file_beacon}" -o beacon-chain || error "❌ Unable to download beacon-chain" 220 | curl -f -L "https://prysmaticlabs.com/releases/${file_validator}" -o validator || error "❌ Unable to download validator" 221 | curl -f -L "https://prysmaticlabs.com/releases/${file_prysmctl}" -o prysmctl || error "❌ Unable to download prysmctl" 222 | chmod +x beacon-chain validator prysmctl 223 | test -f /etc/systemd/system/consensus.service && sudo systemctl stop consensus 224 | test -f /etc/systemd/system/validator.service && sudo service validator stop 225 | sudo rm /usr/local/bin/beacon-chain 226 | sudo rm /usr/local/bin/validator 227 | sudo rm /usr/local/bin/prysmctl 228 | sudo mv beacon-chain validator prysmctl /usr/local/bin || error "❌ Unable to move prysm files" 229 | test -f /etc/systemd/system/consensus.service && sudo systemctl start consensus 230 | test -f /etc/systemd/system/validator.service && sudo systemctl start validator 231 | ;; 232 | esac 233 | } 234 | 235 | function updateJRE(){ 236 | # Check if OpenJDK-21-JRE or OpenJDK-21-JDK is already installed 237 | if dpkg --list | grep -q -E "openjdk-21-jre|openjdk-21-jdk"; then 238 | info "✅ OpenJDK-21-JRE or OpenJDK-21-JDK is already installed. Skipping installation." 239 | else 240 | # Install OpenJDK-21-JRE 241 | sudo apt-get update 242 | sudo apt-get install -y openjdk-21-jre 243 | 244 | # Check if the installation was successful 245 | # shellcheck disable=SC2181 246 | if [ $? -eq 0 ]; then 247 | info "✅ OpenJDK-21-JRE installed successfully!" 248 | else 249 | error "❌ Error installing OpenJDK-21-JRE. Please check the error log." 250 | fi 251 | fi 252 | } 253 | 254 | getClient 255 | getCurrentVersion 256 | getLatestVersion 257 | promptYesNo -------------------------------------------------------------------------------- /plugins/aztec/plugin_aztec.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Author: coincashew.eth | coincashew.com 4 | # License: GNU GPL 5 | # Source: https://github.com/coincashew/ethpillar 6 | # Description: Aztec plugin inspired by https://github.com/cryptocattelugu/Aztec-Network 7 | # 8 | # Made for home and solo stakers 🏠🥩 9 | set -euo pipefail 10 | 11 | SOURCE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 12 | 13 | # Variables 14 | RELEASE_URL="https://api.github.com/repos/AztecProtocol/aztec-packages/releases/latest" 15 | DESCRIPTION="🪿 Aztec Sequencer Node: a privacy first L2 on Ethereum by Aztec Labs" 16 | DOCUMENTATION="https://aztec.network/network" 17 | SOURCE_CODE="https://github.com/AztecProtocol/aztec-packages" 18 | APP_NAME="aztec-sequencer" 19 | PLUGIN_INSTALL_PATH="/opt/ethpillar/aztec" 20 | PLUGIN_SOURCE_PATH="$SOURCE_DIR" 21 | DOCKER_IMAGE=aztecprotocol/aztec 22 | 23 | # Colors 24 | g="\033[32m" # Green 25 | r="\033[31m" # Red 26 | nc="\033[0m" # No-color 27 | bold="\033[1m" 28 | 29 | function info { 30 | echo -e "${g}INFO: $1${nc}" 31 | } 32 | 33 | function error { 34 | echo -e "${r}${bold}ERROR: ❌ $1${nc}" 35 | exit 1 36 | } 37 | 38 | function get_arch(){ 39 | machine_arch="$(uname --machine)" 40 | if [[ "${machine_arch}" = "x86_64" ]]; then 41 | binary_arch="amd64" 42 | elif [[ "${machine_arch}" = "aarch64" ]]; then 43 | binary_arch="arm64" 44 | else 45 | echo "Unsupported architecture: ${machine_arch}" 46 | exit 1 47 | fi 48 | echo "${binary_arch}" 49 | } 50 | 51 | # Gets latest tag 52 | function getLatestVersion(){ 53 | TAG=$(curl -s $RELEASE_URL | jq -r .tag_name ) 54 | if [[ -z "$TAG" ]]; then error "Failed to fetch latest version"; fi 55 | } 56 | 57 | # Install cli binaries 58 | function downloadClient(){ 59 | export NON_INTERACTIVE=1; export SKIP_PULL=1; bash -i <(curl -s https://install.aztec.network) 60 | } 61 | 62 | function install_docker() { 63 | bash -c "$SOURCE_DIR/../../helpers/install_docker.sh" 64 | info "Adding current user to docker group..." 65 | sudo usermod -aG docker "$USER" 66 | } 67 | 68 | function install_foundry() { 69 | _architecture=$(get_arch) 70 | RELEASE_URL="https://api.github.com/repos/foundry-rs/foundry/releases/latest" 71 | BINARIES_URL="$(curl -s "$RELEASE_URL" | jq -r ".assets[] | select(.name) | .browser_download_url" | grep --ignore-case alpine_"${_architecture}".tar.gz$)" 72 | echo Downloading URL: "$BINARIES_URL" 73 | cd "$HOME" || true 74 | tmpdir=$(mktemp -d) 75 | cd "$tmpdir" || error "Unable to change to tmpdir" 76 | wget -O foundry.tar.gz "$BINARIES_URL" 77 | if [ ! -f foundry.tar.gz ]; then 78 | echo "Error: Downloading foundry archive failed!" 79 | exit 1 80 | fi 81 | sudo tar -xzvf foundry.tar.gz -C "$tmpdir" || error "Unable to untar foundry.tar.gz" 82 | # shellcheck disable=SC2015 83 | [[ -f "$tmpdir"/cast ]] && sudo mv "$tmpdir"/cast /usr/local/bin || error "Unable to install cast to /usr/local/bin" 84 | sudo touch "$PLUGIN_INSTALL_PATH/.cast_installed_by_plugin" || true 85 | # shellcheck disable=SC2015 86 | rm -rf "$tmpdir" || error "Unable to cleanup temp foundry files" 87 | } 88 | 89 | function download_snapshot() { 90 | # Install dependency 91 | if ! command -v lz4 &> /dev/null; then 92 | sudo apt-get install lz4 -y 93 | fi 94 | 95 | # Create directory structure 96 | mkdir -p $PLUGIN_INSTALL_PATH || error "Unable to create directory structure for snapshot" 97 | 98 | # Download the snapshot 99 | wget https://files5.blacknodes.net/aztec/aztec-alpha-testnet.tar.lz4 -O "$HOME"/aztec-alpha-testnet.tar.lz4 || error "Unable to download snapshot" 100 | 101 | # Extract the snapshot 102 | lz4 -d "$HOME"/aztec-alpha-testnet.tar.lz4 | sudo tar -x -C $PLUGIN_INSTALL_PATH || error "Unable to extract snapshot" 103 | 104 | # Cleanup the downloaded file 105 | rm "$HOME"/aztec-alpha-testnet.tar.lz4 || error "Unable cleanup tar file" 106 | } 107 | 108 | # Allow EL RPC access (port 8545) to docker 109 | function exposeETHRPC(){ 110 | _exposed='0.0.0.0' 111 | _service='execution' 112 | _file="/etc/systemd/system/${_service}.service" 113 | sudo test -f /etc/systemd/system/execution.service || return 0 114 | EL=$(sudo grep Description= /etc/systemd/system/execution.service | awk -F'=' '{print $2}' | awk '{print $1}') 115 | case "${EL}" in 116 | Nethermind ) _flag='--JsonRpc.Host';; 117 | Besu ) _flag='--rpc-http-host';; 118 | Erigon ) _flag='--http.addr';; 119 | Geth ) _flag='--http.addr';; 120 | Reth ) _flag='--http.addr';; 121 | * ) echo "Execution client not detected"; return 0;; 122 | esac 123 | if sudo grep -e "^ExecStart=.*${_flag}=${_exposed}" "$_file"; then 124 | info "✅ Execution client RPC access already accessible..." 125 | else 126 | info "🔧 Updating execution client RPC access..." 127 | # Add new value to end of ExecStart line 128 | sudo sed -i -e "s/^ExecStart.*$/& ${_flag}=${_exposed}/" $_file 129 | # Reload and restart 130 | sudo systemctl daemon-reload && sudo service ${_service} restart 131 | fi 132 | } 133 | 134 | function enableSupernode(){ 135 | _service='consensus' 136 | _file="/etc/systemd/system/${_service}.service" 137 | sudo test -f /etc/systemd/system/consensus.service || return 0 138 | CL=$(sudo grep Description= /etc/systemd/system/consensus.service | awk -F'=' '{print $2}' | awk '{print $1}') 139 | case "${CL}" in 140 | Nimbus ) _flag='--peerdas-supernode';; 141 | Lodestar ) _flag='--semiSupernode';; 142 | Lighthouse ) _flag='--semi-supernode';; 143 | Prysm ) _flag='--subscribe-all-subnets';; 144 | Teku ) _flag='--p2p-subscribe-all-custody-subnets-enabled';; 145 | * ) echo "Consensus client not detected."; return 0;; 146 | esac 147 | if sudo grep -e "^ExecStart=.*${_flag}" $_file; then 148 | info "✅ Consensus client is already supernode..." 149 | else 150 | info "🔧 Updating consensus client to be supernode..." 151 | # Add new value to end of ExecStart line 152 | sudo sed -i -e "s/^ExecStart.*$/& ${_flag}/" $_file 153 | # Reload and restart 154 | sudo systemctl daemon-reload && sudo service ${_service} restart 155 | fi 156 | } 157 | 158 | # Installation function 159 | function install_plugin(){ 160 | MSG_ABOUT="🪿 Aztec Sepolia Sequencer: a privacy first L2 on Ethereum by Aztec Labs 161 | \nBackground: 162 | Aztec is the first fully decentralized L2, thanks to its permissionless network of sequencers and provers. 163 | 164 | Your sequencer node takes part in three key actions: 165 | -Assemble unprocessed transactions and propose the next block 166 | -Attest to correct execution of txs in the proposed block 167 | -Submit the successfully attested block to L1 168 | 169 | Requirements: 170 | - Synced Nimbus + Nethermind Full Node (uses ~750GB) for Sepolia Testnet 171 | - 2 core / 4 vCPU - 16 GB RAM 172 | - 100GB NVMe SDD for Aztec Node - 25 Mbps network connection 173 | - 850GB+ for Full Node Setup (execution L1 RPC + consensus beacon RPC + aztec L2) 174 | - Validator Private Key (for attesting, proposals), Coinbase Address (for block rewards) 175 | 176 | Documentation: $DOCUMENTATION 177 | \nContinue to install?" 178 | 179 | # Intro screen 180 | if ! whiptail --title "$APP_NAME: Installation" --yesno "$MSG_ABOUT" 29 78; then exit; fi 181 | 182 | # Local or remote RPC 183 | RPC_CONFIG=$(whiptail --title "🔧 RPC Configuration" --menu \ 184 | "Where's the RPCs?" 10 78 2 \ 185 | "LOCAL" "| Installs full node on this machine. Requires 850GB+ disk space" \ 186 | "REMOTE" "| I will provide Ethereum RPC and Beacon RPC URLs" \ 187 | 3>&1 1>&2 2>&3) 188 | if [ -z "$RPC_CONFIG" ]; then exit; fi # pressed cancel 189 | if [[ $RPC_CONFIG == "REMOTE" ]]; then 190 | while true; do 191 | ETH_RPC=$(whiptail --title "Ethereum RPC URL(s) (ETHEREUM_HOSTS)" --inputbox "🔗 Enter one or more URLs, comma-separated (e.g. https://sepolia.rpc.url,http://192.168.1.123:8545):" 9 78 --ok-button "Submit" 3>&1 1>&2 2>&3) 192 | if [ -z "$ETH_RPC" ]; then exit; fi #pressed cancel 193 | # sanitize: strip spaces 194 | ETH_RPC=$(echo "$ETH_RPC" | tr -d '[:space:]') 195 | if [[ "$ETH_RPC" =~ ^https?:// ]]; then 196 | break 197 | else 198 | whiptail --title "Error" --msgbox "ETHEREUM_HOSTS must start with http(s)://" 8 78 199 | fi 200 | done 201 | while true; do 202 | BEACON_RPC=$(whiptail --title "Beacon RPC URL(s) (L1_CONSENSUS_HOST_URLS)" --inputbox "🔗 Enter one or more URLs, comma-separated (e.g. https://beacon.rpc.url,http://192.168.1.123:5052):" 9 78 --ok-button "Submit" 3>&1 1>&2 2>&3) 203 | if [ -z "$BEACON_RPC" ]; then exit; fi #pressed cancel 204 | # sanitize: strip spaces 205 | BEACON_RPC=$(echo "$BEACON_RPC" | tr -d '[:space:]') 206 | if [[ "$BEACON_RPC" =~ ^https?:// ]]; then 207 | break 208 | else 209 | whiptail --title "Error" --msgbox "L1_CONSENSUS_HOST_URLS must start with http(s)://" 8 78 210 | fi 211 | done 212 | else 213 | # Install EL/CL 214 | if [[ ! -f /etc/systemd/system/execution.service ]] && [[ ! -f /etc/systemd/system/consensus.service ]]; then 215 | sudo bash -c "$SOURCE_DIR/../../install-node.sh deploy-nimbus-nethermind.py true" 216 | fi 217 | fi 218 | 219 | # Install packages 220 | sudo apt-get update 221 | sudo apt-get upgrade -y 222 | 223 | info "🔧 Setup installation directory" 224 | sudo mkdir -p $PLUGIN_INSTALL_PATH || error "Unable to setup installation directory" 225 | sudo chmod -R 755 "$PLUGIN_INSTALL_PATH" || error "Unable to chmod installation directory permissions" 226 | 227 | info "🔧 Install env file and compose file..." 228 | sudo cp "$PLUGIN_SOURCE_PATH"/.env.example $PLUGIN_INSTALL_PATH/.env || error "Unable to create .env" 229 | sudo cp "$PLUGIN_SOURCE_PATH"/docker-compose.yml.example $PLUGIN_INSTALL_PATH/docker-compose.yml || error "Unable to create docker-compose.yml" 230 | 231 | TAG=$(grep "^DOCKER_TAG" $PLUGIN_INSTALL_PATH/.env | sed "s/^DOCKER_TAG=\(.*\)/\1/") 232 | #TAG=$(docker inspect aztec-sequencer | jq -r '.[0].Config.Image') 233 | echo "$TAG" | sudo tee $PLUGIN_INSTALL_PATH/current_version 234 | info "✏️ Storing current version... $TAG" 235 | 236 | if ! command -v cast &> /dev/null; then 237 | info "🔧 Installing foundry for cast..." 238 | install_foundry 239 | fi 240 | 241 | if [[ $RPC_CONFIG == "REMOTE" ]]; then 242 | sudo sed -i "s|^ETHEREUM_HOSTS.*$|ETHEREUM_HOSTS=${ETH_RPC}|" $PLUGIN_INSTALL_PATH/.env || error "Unable to set ETHEREUM_HOSTS" 243 | sudo sed -i "s|^L1_CONSENSUS_HOST_URLS.*$|L1_CONSENSUS_HOST_URLS=${BEACON_RPC}|" $PLUGIN_INSTALL_PATH/.env || error "Unable to set L1_CONSENSUS_HOST_URLS" 244 | fi 245 | 246 | if [[ $RPC_CONFIG == "LOCAL" ]]; then 247 | info "🔧 Checking Execution client RPC access" 248 | exposeETHRPC 249 | info "🔧 Checking Execution client supernode configuration" 250 | enableSupernode 251 | fi 252 | 253 | # sanitize with grep 254 | P2P_IP=$(grep -m1 -oE '^[0-9]{1,3}(\.[0-9]{1,3}){3}$' <<< "$( 255 | curl -fsS --max-time 5 https://ipv4.icanhazip.com \ 256 | || curl -fsS --max-time 5 https://api.ipify.org \ 257 | || curl -fsS --max-time 5 http://ip1.dynupdate.no-ip.com 258 | )") 259 | 260 | # shellcheck disable=SC2015 261 | [[ -n $P2P_IP ]] && sudo sed -i "s/^P2P_IP.*$/P2P_IP=${P2P_IP}/" $PLUGIN_INSTALL_PATH/.env || error "Unable to get and set P2P_IP" 262 | info "🔧 Configuring and updating P2P_IP to $P2P_IP" 263 | 264 | #info "📁 Downloading aztec snapshot, minimizes syncing issues and faster sync..." 265 | #download_snapshot 266 | #[[ -f /opt/ethpillar/aztec/data/archiver/data.mdb ]] && info "✅ Verfied snapshot extract exists" || error "Unable to verify snapshot extract" 267 | 268 | info "🔧 Configuring UFW firewall" 269 | sudo ufw allow 40400 comment 'Allow aztec node p2p port' || error "Unable to configure ufw" 270 | if [[ $RPC_CONFIG == "LOCAL" ]]; then 271 | info "🔧 Configuring UFW firewall to allow access from host.docker.internal" 272 | docker_bridge_subnet=$(docker network inspect bridge --format '{{(index .IPAM.Config 0).Subnet}}' 2>/dev/null || echo '172.16.0.0/12') 273 | if [[ -z "$docker_bridge_subnet" || "$docker_bridge_subnet" == "" ]]; then 274 | docker_bridge_subnet='172.16.0.0/12' 275 | fi 276 | sudo ufw allow from "$docker_bridge_subnet" to any port 8545 comment 'Allow host.docker.internal to ETH RPC' 277 | sudo ufw allow from "$docker_bridge_subnet" to any port 5052 comment 'Allow host.docker.internal to BEACON RPC' 278 | fi 279 | 280 | info "🔧 Updating ownership of $PLUGIN_INSTALL_PATH to current user: $USER" 281 | sudo chown -R "$USER":"$USER" "$PLUGIN_INSTALL_PATH" 282 | 283 | info "🎉 INSTALL COMPLETE :: To run, type \"ethpillar\"" 284 | 285 | MSG_COMPLETE="✅ Done! $APP_NAME is now installed. 286 | \nNext Steps: 287 | \n1. Review .env configuration: Update values if desired 288 | \n2. Start aztec-sequencer: Ensure Sepolia RPC Node is fully synced first! 289 | \n3. Backup aztec validator key: Use the 🔐 menu option 290 | 291 | Port forwarding: Forward the p2p port (default: 40400) to your local node ip address. Configure in your router. 292 | \nRead documentation: $DOCUMENTATION 293 | 294 | Join the Discord to connect with the community and get help with your setup. 295 | - Aztec: https://discord.gg/aztec 296 | - EthPillar: https://discord.gg/WS8E3PMzrb 297 | \nHappy private sequencing! 298 | " 299 | 300 | # Installation complete screen 301 | whiptail --title "$APP_NAME: Install Complete" --msgbox "$MSG_COMPLETE" 28 78 302 | } 303 | 304 | # Uninstall 305 | function removeAll() { 306 | if whiptail --title "Uninstall $APP_NAME" --defaultno --yesno "Are you sure you want to remove $APP_NAME" 9 78; then 307 | cd $PLUGIN_INSTALL_PATH 2>/dev/null && docker compose down || true 308 | sudo docker rm -f $APP_NAME 2>/dev/null || true 309 | TAG=$(grep "DOCKER_TAG" $PLUGIN_INSTALL_PATH/.env | sed "s/^DOCKER_TAG=\(.*\)/\1/") 310 | sudo docker rmi -f $DOCKER_IMAGE:"$TAG" || true 311 | if [[ -f "$PLUGIN_INSTALL_PATH/.cast_installed_by_plugin" && -f /usr/local/bin/cast ]]; then 312 | sudo rm /usr/local/bin/cast 313 | fi 314 | sudo rm -rf "$PLUGIN_INSTALL_PATH" 315 | whiptail --title "Uninstall finished" --msgbox "You have uninstalled $APP_NAME." 8 78 316 | fi 317 | } 318 | 319 | # Displays usage info 320 | function usage() { 321 | cat << EOF 322 | Usage: $(basename "$0") [-i] [-u] [-r] 323 | 324 | $APP_NAME Helper Script 325 | 326 | Options: 327 | -i Install $APP_NAME 328 | -r Remove $APP_NAME 329 | -h Display help 330 | 331 | About $APP_NAME) 332 | - $DESCRIPTION 333 | - Source code: $SOURCE_CODE 334 | - Documentation: $DOCUMENTATION 335 | EOF 336 | } 337 | 338 | # Install docker, prompt to relog if not root user 339 | if ! command -v docker &> /dev/null; then 340 | info "🔧 Installing docker..." 341 | install_docker 342 | # Except root, user account requires re-log for new docker permissions 343 | if [ "$(id -u)" -ne 0 ]; then 344 | whiptail --title "Docker Install Complete" --msgbox "Log off and log in again for new docker permissions and then try again." 8 78 345 | exit 0 346 | fi 347 | fi 348 | 349 | # Process command line options 350 | while getopts :irh opt; do 351 | case ${opt} in 352 | i ) install_plugin ;; 353 | r ) removeAll ;; 354 | h) 355 | usage 356 | exit 0 357 | ;; 358 | \?) 359 | echo "Invalid option: -${OPTARG}" >&2 360 | usage 361 | exit 1 362 | ;; 363 | :) 364 | echo "Option -${OPTARG} requires an argument." >&2 365 | usage 366 | exit 1 367 | ;; 368 | esac 369 | done 370 | -------------------------------------------------------------------------------- /alert.rules.yml: -------------------------------------------------------------------------------- 1 | groups: 2 | - name: ethpillar.rules 3 | rules: 4 | - alert: HostOutOfMemory 5 | expr: '(node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes * 100 < 10) * on(instance) group_left (nodename) node_uname_info{nodename=~".+"}' 6 | for: 2m 7 | labels: 8 | severity: warning 9 | annotations: 10 | summary: Host out of memory (instance {{ $labels.instance }}) 11 | description: "Node memory is filling up (< 10% left)\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" 12 | 13 | - alert: HostMemoryUnderMemoryPressure 14 | expr: '(rate(node_vmstat_pgmajfault[1m]) > 1000) * on(instance) group_left (nodename) node_uname_info{nodename=~".+"}' 15 | for: 2m 16 | labels: 17 | severity: warning 18 | annotations: 19 | summary: Host memory under memory pressure (instance {{ $labels.instance }}) 20 | description: "The node is under heavy memory pressure. High rate of major page faults\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" 21 | 22 | - alert: HostMemoryIsUnderutilized 23 | expr: '(100 - (avg_over_time(node_memory_MemAvailable_bytes[30m]) / node_memory_MemTotal_bytes * 100) < 20) * on(instance) group_left (nodename) node_uname_info{nodename=~".+"}' 24 | for: 1w 25 | labels: 26 | severity: info 27 | annotations: 28 | summary: Host Memory is underutilized (instance {{ $labels.instance }}) 29 | description: "Node memory is < 20% for 1 week. Consider reducing memory space. (instance {{ $labels.instance }})\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" 30 | 31 | - alert: HostUnusualNetworkThroughputIn 32 | expr: '(sum by (instance) (rate(node_network_receive_bytes_total[2m])) / 1024 / 1024 > 100) * on(instance) group_left (nodename) node_uname_info{nodename=~".+"}' 33 | for: 5m 34 | labels: 35 | severity: warning 36 | annotations: 37 | summary: Host unusual network throughput in (instance {{ $labels.instance }}) 38 | description: "Host network interfaces are probably receiving too much data (> 100 MB/s)\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" 39 | 40 | - alert: HostUnusualNetworkThroughputOut 41 | expr: '(sum by (instance) (rate(node_network_transmit_bytes_total[2m])) / 1024 / 1024 > 100) * on(instance) group_left (nodename) node_uname_info{nodename=~".+"}' 42 | for: 5m 43 | labels: 44 | severity: warning 45 | annotations: 46 | summary: Host unusual network throughput out (instance {{ $labels.instance }}) 47 | description: "Host network interfaces are probably sending too much data (> 100 MB/s)\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" 48 | 49 | - alert: HostUnusualDiskReadRate 50 | expr: '(sum by (instance) (rate(node_disk_read_bytes_total[2m])) / 1024 / 1024 > 50) * on(instance) group_left (nodename) node_uname_info{nodename=~".+"}' 51 | for: 5m 52 | labels: 53 | severity: warning 54 | annotations: 55 | summary: Host unusual disk read rate (instance {{ $labels.instance }}) 56 | description: "Disk is probably reading too much data (> 50 MB/s)\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" 57 | 58 | - alert: HostUnusualDiskWriteRate 59 | expr: '(sum by (instance) (rate(node_disk_written_bytes_total[2m])) / 1024 / 1024 > 50) * on(instance) group_left (nodename) node_uname_info{nodename=~".+"}' 60 | for: 2m 61 | labels: 62 | severity: warning 63 | annotations: 64 | summary: Host unusual disk write rate (instance {{ $labels.instance }}) 65 | description: "Disk is probably writing too much data (> 50 MB/s)\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" 66 | 67 | - alert: HostOutOfDiskSpace 68 | expr: '((node_filesystem_avail_bytes * 100) / node_filesystem_size_bytes < 10 and ON (instance, device, mountpoint) node_filesystem_readonly == 0) * on(instance) group_left (nodename) node_uname_info{nodename=~".+"}' 69 | for: 2m 70 | labels: 71 | severity: warning 72 | annotations: 73 | summary: Host out of disk space (instance {{ $labels.instance }}) 74 | description: "Disk is almost full (< 10% left)\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" 75 | 76 | - alert: HostDiskWillFillIn24Hours 77 | expr: '((node_filesystem_avail_bytes * 100) / node_filesystem_size_bytes < 10 and ON (instance, device, mountpoint) predict_linear(node_filesystem_avail_bytes{fstype!~"tmpfs"}[1h], 24 * 3600) < 0 and ON (instance, device, mountpoint) node_filesystem_readonly == 0) * on(instance) group_left (nodename) node_uname_info{nodename=~".+"}' 78 | for: 2m 79 | labels: 80 | severity: warning 81 | annotations: 82 | summary: Host disk will fill in 24 hours (instance {{ $labels.instance }}) 83 | description: "Filesystem is predicted to run out of space within the next 24 hours at current write rate\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" 84 | 85 | - alert: HostOutOfInodes 86 | expr: '(node_filesystem_files_free{fstype!="msdosfs"} / node_filesystem_files{fstype!="msdosfs"} * 100 < 10 and ON (instance, device, mountpoint) node_filesystem_readonly == 0) * on(instance) group_left (nodename) node_uname_info{nodename=~".+"}' 87 | for: 2m 88 | labels: 89 | severity: warning 90 | annotations: 91 | summary: Host out of inodes (instance {{ $labels.instance }}) 92 | description: "Disk is almost running out of available inodes (< 10% left)\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" 93 | 94 | - alert: HostFilesystemDeviceError 95 | expr: 'node_filesystem_device_error == 1' 96 | for: 2m 97 | labels: 98 | severity: critical 99 | annotations: 100 | summary: Host filesystem device error (instance {{ $labels.instance }}) 101 | description: "{{ $labels.instance }}: Device error with the {{ $labels.mountpoint }} filesystem\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" 102 | 103 | - alert: HostInodesWillFillIn24Hours 104 | expr: '(node_filesystem_files_free{fstype!="msdosfs"} / node_filesystem_files{fstype!="msdosfs"} * 100 < 10 and predict_linear(node_filesystem_files_free{fstype!="msdosfs"}[1h], 24 * 3600) < 0 and ON (instance, device, mountpoint) node_filesystem_readonly{fstype!="msdosfs"} == 0) * on(instance) group_left (nodename) node_uname_info{nodename=~".+"}' 105 | for: 2m 106 | labels: 107 | severity: warning 108 | annotations: 109 | summary: Host inodes will fill in 24 hours (instance {{ $labels.instance }}) 110 | description: "Filesystem is predicted to run out of inodes within the next 24 hours at current write rate\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" 111 | 112 | - alert: HostUnusualDiskReadLatency 113 | expr: '(rate(node_disk_read_time_seconds_total[1m]) / rate(node_disk_reads_completed_total[1m]) > 0.1 and rate(node_disk_reads_completed_total[1m]) > 0) * on(instance) group_left (nodename) node_uname_info{nodename=~".+"}' 114 | for: 2m 115 | labels: 116 | severity: warning 117 | annotations: 118 | summary: Host unusual disk read latency (instance {{ $labels.instance }}) 119 | description: "Disk latency is growing (read operations > 100ms)\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" 120 | 121 | - alert: HostUnusualDiskWriteLatency 122 | expr: '(rate(node_disk_write_time_seconds_total[1m]) / rate(node_disk_writes_completed_total[1m]) > 0.1 and rate(node_disk_writes_completed_total[1m]) > 0) * on(instance) group_left (nodename) node_uname_info{nodename=~".+"}' 123 | for: 2m 124 | labels: 125 | severity: warning 126 | annotations: 127 | summary: Host unusual disk write latency (instance {{ $labels.instance }}) 128 | description: "Disk latency is growing (write operations > 100ms)\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" 129 | 130 | - alert: HostHighCpuLoad 131 | expr: '(sum by (instance) (avg by (mode, instance) (rate(node_cpu_seconds_total{mode!="idle"}[2m]))) > 0.8) * on(instance) group_left (nodename) node_uname_info{nodename=~".+"}' 132 | for: 10m 133 | labels: 134 | severity: warning 135 | annotations: 136 | summary: Host high CPU load (instance {{ $labels.instance }}) 137 | description: "CPU load is > 80%\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" 138 | 139 | - alert: HostCpuIsUnderutilized 140 | expr: '(100 - (rate(node_cpu_seconds_total{mode="idle"}[30m]) * 100) < 20) * on(instance) group_left (nodename) node_uname_info{nodename=~".+"}' 141 | for: 1w 142 | labels: 143 | severity: info 144 | annotations: 145 | summary: Host CPU is underutilized (instance {{ $labels.instance }}) 146 | description: "CPU load is < 20% for 1 week. Consider reducing the number of CPUs.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" 147 | 148 | - alert: HostCpuStealNoisyNeighbor 149 | expr: '(avg by(instance) (rate(node_cpu_seconds_total{mode="steal"}[5m])) * 100 > 10) * on(instance) group_left (nodename) node_uname_info{nodename=~".+"}' 150 | for: 0m 151 | labels: 152 | severity: warning 153 | annotations: 154 | summary: Host CPU steal noisy neighbor (instance {{ $labels.instance }}) 155 | description: "CPU steal is > 10%. A noisy neighbor is killing VM performances or a spot instance may be out of credit.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" 156 | 157 | - alert: HostCpuHighIowait 158 | expr: '(avg by (instance) (rate(node_cpu_seconds_total{mode="iowait"}[5m])) * 100 > 10) * on(instance) group_left (nodename) node_uname_info{nodename=~".+"}' 159 | for: 0m 160 | labels: 161 | severity: warning 162 | annotations: 163 | summary: Host CPU high iowait (instance {{ $labels.instance }}) 164 | description: "CPU iowait > 10%. A high iowait means that you are disk or network bound.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" 165 | 166 | - alert: HostUnusualDiskIo 167 | expr: '(rate(node_disk_io_time_seconds_total[1m]) > 0.5) * on(instance) group_left (nodename) node_uname_info{nodename=~".+"}' 168 | for: 5m 169 | labels: 170 | severity: warning 171 | annotations: 172 | summary: Host unusual disk IO (instance {{ $labels.instance }}) 173 | description: "Time spent in IO is too high on {{ $labels.instance }}. Check storage for issues.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" 174 | 175 | - alert: HostSwapIsFillingUp 176 | expr: '((1 - (node_memory_SwapFree_bytes / node_memory_SwapTotal_bytes)) * 100 > 80) * on(instance) group_left (nodename) node_uname_info{nodename=~".+"}' 177 | for: 2m 178 | labels: 179 | severity: warning 180 | annotations: 181 | summary: Host swap is filling up (instance {{ $labels.instance }}) 182 | description: "Swap is filling up (>80%)\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" 183 | 184 | - alert: HostSystemdServiceCrashed 185 | expr: '(node_systemd_unit_state{state="failed"} == 1) * on(instance) group_left (nodename) node_uname_info{nodename=~".+"}' 186 | for: 0m 187 | labels: 188 | severity: warning 189 | annotations: 190 | summary: Host systemd service crashed (instance {{ $labels.instance }}) 191 | description: "systemd service crashed\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" 192 | 193 | - alert: HostPhysicalComponentTooHot 194 | expr: '((node_hwmon_temp_celsius * ignoring(label) group_left(instance, job, node, sensor) node_hwmon_sensor_label{label!="tctl"} > 75)) * on(instance) group_left (nodename) node_uname_info{nodename=~".+"}' 195 | for: 5m 196 | labels: 197 | severity: warning 198 | annotations: 199 | summary: Host physical component too hot (instance {{ $labels.instance }}) 200 | description: "Physical hardware component too hot\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" 201 | 202 | - alert: HostNodeOvertemperatureAlarm 203 | expr: '(node_hwmon_temp_crit_alarm_celsius == 1) * on(instance) group_left (nodename) node_uname_info{nodename=~".+"}' 204 | for: 0m 205 | labels: 206 | severity: critical 207 | annotations: 208 | summary: Host node overtemperature alarm (instance {{ $labels.instance }}) 209 | description: "Physical node temperature alarm triggered\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" 210 | 211 | - alert: HostOomKillDetected 212 | expr: '(increase(node_vmstat_oom_kill[1m]) > 0) * on(instance) group_left (nodename) node_uname_info{nodename=~".+"}' 213 | for: 0m 214 | labels: 215 | severity: warning 216 | annotations: 217 | summary: Host OOM kill detected (instance {{ $labels.instance }}) 218 | description: "OOM kill detected\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" 219 | 220 | - alert: HostEdacCorrectableErrorsDetected 221 | expr: '(increase(node_edac_correctable_errors_total[1m]) > 0) * on(instance) group_left (nodename) node_uname_info{nodename=~".+"}' 222 | for: 0m 223 | labels: 224 | severity: info 225 | annotations: 226 | summary: Host EDAC Correctable Errors detected (instance {{ $labels.instance }}) 227 | description: "Host {{ $labels.instance }} has had {{ printf \"%.0f\" $value }} correctable memory errors reported by EDAC in the last 5 minutes.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" 228 | 229 | - alert: HostEdacUncorrectableErrorsDetected 230 | expr: '(node_edac_uncorrectable_errors_total > 0) * on(instance) group_left (nodename) node_uname_info{nodename=~".+"}' 231 | for: 0m 232 | labels: 233 | severity: warning 234 | annotations: 235 | summary: Host EDAC Uncorrectable Errors detected (instance {{ $labels.instance }}) 236 | description: "Host {{ $labels.instance }} has had {{ printf \"%.0f\" $value }} uncorrectable memory errors reported by EDAC in the last 5 minutes.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" 237 | 238 | - alert: HostNetworkReceiveErrors 239 | expr: '(rate(node_network_receive_errs_total[2m]) / rate(node_network_receive_packets_total[2m]) > 0.01) * on(instance) group_left (nodename) node_uname_info{nodename=~".+"}' 240 | for: 2m 241 | labels: 242 | severity: warning 243 | annotations: 244 | summary: Host Network Receive Errors (instance {{ $labels.instance }}) 245 | description: "Host {{ $labels.instance }} interface {{ $labels.device }} has encountered {{ printf \"%.0f\" $value }} receive errors in the last two minutes.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" 246 | 247 | - alert: HostNetworkTransmitErrors 248 | expr: '(rate(node_network_transmit_errs_total[2m]) / rate(node_network_transmit_packets_total[2m]) > 0.01) * on(instance) group_left (nodename) node_uname_info{nodename=~".+"}' 249 | for: 2m 250 | labels: 251 | severity: warning 252 | annotations: 253 | summary: Host Network Transmit Errors (instance {{ $labels.instance }}) 254 | description: "Host {{ $labels.instance }} interface {{ $labels.device }} has encountered {{ printf \"%.0f\" $value }} transmit errors in the last two minutes.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" 255 | 256 | - alert: HostNetworkInterfaceSaturated 257 | expr: '((rate(node_network_receive_bytes_total{device!~"^tap.*|^vnet.*|^veth.*|^tun.*"}[1m]) + rate(node_network_transmit_bytes_total{device!~"^tap.*|^vnet.*|^veth.*|^tun.*"}[1m])) / node_network_speed_bytes{device!~"^tap.*|^vnet.*|^veth.*|^tun.*"} > 0.8 < 10000) * on(instance) group_left (nodename) node_uname_info{nodename=~".+"}' 258 | for: 1m 259 | labels: 260 | severity: warning 261 | annotations: 262 | summary: Host Network Interface Saturated (instance {{ $labels.instance }}) 263 | description: "The network interface \"{{ $labels.device }}\" on \"{{ $labels.instance }}\" is getting overloaded.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" 264 | 265 | - alert: HostClockSkew 266 | expr: '((node_timex_offset_seconds > 0.05 and deriv(node_timex_offset_seconds[5m]) >= 0) or (node_timex_offset_seconds < -0.05 and deriv(node_timex_offset_seconds[5m]) <= 0)) * on(instance) group_left (nodename) node_uname_info{nodename=~".+"}' 267 | for: 10m 268 | labels: 269 | severity: warning 270 | annotations: 271 | summary: Host clock skew (instance {{ $labels.instance }}) 272 | description: "Clock skew detected. Clock is out of sync. Ensure NTP is configured correctly on this host.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" 273 | 274 | - alert: HostClockNotSynchronising 275 | expr: '(min_over_time(node_timex_sync_status[1m]) == 0 and node_timex_maxerror_seconds >= 16) * on(instance) group_left (nodename) node_uname_info{nodename=~".+"}' 276 | for: 2m 277 | labels: 278 | severity: warning 279 | annotations: 280 | summary: Host clock not synchronising (instance {{ $labels.instance }}) 281 | description: "Clock not synchronising. Ensure NTP is configured on this host.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" 282 | 283 | - alert: HostRequiresReboot 284 | expr: '(node_reboot_required > 0) * on(instance) group_left (nodename) node_uname_info{nodename=~".+"}' 285 | for: 4h 286 | labels: 287 | severity: info 288 | annotations: 289 | summary: Host requires reboot (instance {{ $labels.instance }}) 290 | description: "{{ $labels.instance }} requires a reboot.\n VALUE = {{ $value }}\n LABELS = {{ $labels }}" 291 | --------------------------------------------------------------------------------