├── 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 | [](https://github.com/coincashew/ethpillar/releases)
8 | [](https://github.com/coincashew/EthPillar/blob/main/LICENSE)
9 | [](https://github.com/coincashew/EthPillar/stargazers)
10 | [](https://github.com/coincashew/EthPillar/network/members)
11 | [](https://github.com/coincashew/EthPillar/commits/main)
12 | [](https://t.co/lnlom4iImq)
13 | [](https://x.com/coincashew_)
14 | 
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 | 
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 | 
70 |
71 | ---
72 |
73 | ## 🎬 Demo
74 |
75 | [](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 | [](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 |
--------------------------------------------------------------------------------