diff --git a/docs/Build/dbsync.md b/docs/Build/dbsync.md index d3bcf4c95..62d27d2e8 100644 --- a/docs/Build/dbsync.md +++ b/docs/Build/dbsync.md @@ -22,6 +22,10 @@ You can use the instructions below to build the cardano-db-sync, same steps can ``` bash git fetch --tags --all git pull +# Include the cardano-crypto-praos and libsodium components for db-sync +# On CentOS 7 (GCC 4.8.5) we should also do +# echo -e "package cryptonite\n flags: -use_target_attributes" >> cabal.project.local +echo -e "package cardano-crypto-praos\n flags: -external-libsodium-vrf" > cabal.project.local # Replace master with appropriate tag if you'd like to avoid compiling against master git checkout master $CNODE_HOME/scripts/cabal-build-all.sh @@ -42,7 +46,7 @@ scripts/postgresql-setup.sh --createdb ##### Start cardano-db-sync-tool ``` bash cd ~/git/cardano-db-sync -PGPASSFILE=$CNODE_HOME/priv/.pgpass cardano-db-sync-extended --config $CNODE_HOME/files/config.json --genesis-file $CNODE_HOME/files/genesis.json --socket-path $CNODE_HOME/sockets/node0.socket --schema-dir schema/ +PGPASSFILE=$CNODE_HOME/priv/.pgpass cardano-db-sync-extended --config $CNODE_HOME/files/config.json --socket-path $CNODE_HOME/sockets/node0.socket --schema-dir schema/ ``` You can use same instructions above to repeat and execute `cardano-db-sync` as well, but [cardano-graphql](Build/graphql.md) uses `cardano-db-sync-extended`, so we'll stick to it diff --git a/docs/Build/graphql.md b/docs/Build/graphql.md index 86a4e7201..0b893172c 100644 --- a/docs/Build/graphql.md +++ b/docs/Build/graphql.md @@ -1,4 +1,4 @@ -!> - An average pool operator may not require cardano-wallet at all. Please verify if it is required for your use as mentioned [here](build.md#components) +!> We have temporarily disabled updating build documentation for Cardano-GraphQL. The specific component does not follow the process/technology/language (requires npm, yarn) used by other components (cabal/stack), and the value provided by `cardano-graphql` over the (haskell-based) hasura instance has been negligible. Also, an average pool operator may not require cardano-graphql at all, please verify if it is required for your use as mentioned [here](build.md#components). The instructions below are `out of date`. > Ensure the [Pre-Requisites](basics.md#pre-requisites) are in place before you proceed. diff --git a/docs/Build/node-cli.md b/docs/Build/node-cli.md index 8b4a75d47..8bedabcbe 100644 --- a/docs/Build/node-cli.md +++ b/docs/Build/node-cli.md @@ -18,11 +18,14 @@ You can use the instructions below to build the cardano-node, same steps can be ``` bash git fetch --tags --all -# Replace release 1.18.1 with the version/branch/tag you'd like to build -git checkout tags/1.18.1 +# Replace release 1.19.0 with the version/branch/tag you'd like to build git pull +git checkout 1.19.0 echo -e "package cardano-crypto-praos\n flags: -external-libsodium-vrf" > cabal.project.local +# On CentOS 7 (GCC 4.8.5) we should also do +# echo -e "package cryptonite\n flags: -use_target_attributes" >> cabal.project.local + $CNODE_HOME/scripts/cabal-build-all.sh ``` @@ -34,19 +37,69 @@ Execute cardano-cli and cardano-node to verify output as below: ```bash cardano-cli version -# cardano-cli 1.18.1 - linux-x86_64 - ghc-8.6 -# git rev a4b6dae699fa21dc3c025c8a83d1718475cb3afc +# cardano-cli 1.19.0 - linux-x86_64 - ghc-8.6 +# git rev 4814003f14340d5a1fc02f3ac15437387a7ada9f cardano-node version -# cardano-node 1.18.1 - linux-x86_64 - ghc-8.6 -# git rev a4b6dae699fa21dc3c025c8a83d1718475cb3afc +# cardano-node 1.19.0 - linux-x86_64 - ghc-8.6 +# git rev 4814003f14340d5a1fc02f3ac15437387a7ada9f +``` + +##### Update port number or pool name for relative paths + +Before you go ahead with starting your node, you may want to update values for CNODE_PORT in `$CNODE_HOME/scripts/cnode.sh`. Note that it is imperative for operational relays and pools to ensure that the port mentioned is opened via firewall to the destination your node is supposed to connect from. Update your network/firewall configuration accordingly. Future executions of prereqs.sh will preserve and not overwrite these values. + +```bash +## Static (content that will not be overwritten by prereqs.sh) +## Begin + +POOL_NAME="GUILD" +CNODE_PORT=6000 +POOL_DIR="$CNODE_HOME/priv/pool/$POOL_NAME" ``` -##### Start a passive node +> POOL_NAME is the name of folder that you will use when registering pools and starting node in core mode. This folder would typically contain your `hot.skey`,`vrf.skey` and `op.cert` files required. If the mentioned files are absent, the node will automatically start in a passive mode. + +##### Start the node -To start the node in passive mode, you can use the pre-built script below: +To test starting the node in interactive mode, you can use the pre-built script below (note that the config now uses `SimpleView` so you may not see much output): ```bash cd $CNODE_HOME/scripts ./cnode.sh ``` +##### Run as systemd service + +The preferred way to run the node is through a service manager like systemd. This section explains how to setup a systemd service file. + +**1. Deploy as a systemd service** +Execute the below command to deploy your node as a systemd service (from the respective scripts folder): +```bash +cd $CNODE_HOME/scripts +./deploy-as-systemd.sh +``` + +**2. Start the node** +Run below commands to enable automatic start of service on startup and start it. +``` bash +sudo systemctl start cnode.service +``` + +**3. Check status and stop/start commands** +Replace `status` with `stop`/`start`/`restart` depending on what action to take. +``` bash +sudo systemctl status cnode.service +``` + +?> In case you see the node exit unsuccessfully upon checking status, please verify you've followed the transition process correctly as documented below, and that you do not have another instance of node already running. It would help to check your system logs (/var/log/syslog for debian-based and /var/log/messages for redhat/CentOS/Fedora systems) for any errors while starting node. + +You can use [gLiveView](Scripts/gliveview.md) to monitor your pool that was started as systemd, if you miss the LiveView functionality. + +##### Steps to transition from LiveView in tmux to systemd setup + +If you've followed guide from this repo previously and would like to transfer to systemd usage, please checkout the steps below: + +1. Stop previous instance of node if already running (eg: in tmux) +2. Run `prereqs.sh`, but remember to preserve your customisations to cnode.sh, topology.json, env files (you can also compare and update cnode.sh and env files from github repo). +3. Follow the instructions [above](#run-as-systemd-service) to setup your node as a service and start it using systemctl as directed. +4. If you need to monitor via interactive terminal as before, use [gLiveView](Scripts/gliveview.md). diff --git a/docs/Contributors.md b/docs/Contributors.md deleted file mode 100644 index d192d0ad2..000000000 --- a/docs/Contributors.md +++ /dev/null @@ -1,18 +0,0 @@ -Everyone is welcome to contribute to the guide, as well as the repository. Below is just a thank you to people who have been contributing consistently: - -[Markus](https://github.com/gufmar) -[Pal Dorogi](https://github.com/ilap) -[OCG](https://github.com/oldcryptogeek) -[RedOracle](https://github.com/RedOracle) -[Papacarp](https://github.com/papacarp) -[Bergr01](https://github.com/bergr01) -[Ola Ahlman](https://github.com/Scitz0) -[SmaugPool](https://github.com/SmaugPool) -[Priyank](https://github.com/rdlrt) -[Psychomb](https://github.com/psychomb) -[PegasusPool](https://github.com/PegasusPool) -[Matthijs](https://github.com/matthijs-aeon) -[Marek](https://github.com/mmahut) - - -> To start contributing, simply hit the [github repository](https://github.com/cardano-community/guild-operators) and raise Issue/Pull Request diff --git a/docs/Scripts/cntools-blocks.md b/docs/Scripts/cntools-blocks.md index 39b2f9eb4..9a727fb43 100644 --- a/docs/Scripts/cntools-blocks.md +++ b/docs/Scripts/cntools-blocks.md @@ -17,7 +17,7 @@ In this example normal output from the `cntoolsBlockCollector.sh` script is igno **1. Create systemd service file** Replace `$USER` with the correct user for your system. Copy & paste all code below to create the service file. ``` bash -sudo bash -c 'cat < /etc/systemd/system/cntools-blockcollector.service +sudo bash -c "cat << 'EOF' > /etc/systemd/system/cntools-blockcollector.service [Unit] Description=CNTools - Block Collector After=network.target @@ -28,7 +28,7 @@ Restart=on-failure RestartSec=10 User=$USER WorkingDirectory=/opt/cardano/cnode/scripts -ExecStart=/opt/cardano/cnode/scripts/cntoolsBlockCollector.sh +ExecStart=/bin/bash -l -c 'exec \"\$@\"' _ /opt/cardano/cnode/scripts/cntoolsBlockCollector.sh SuccessExitStatus=143 StandardOutput=null StandardError=syslog @@ -36,7 +36,7 @@ SyslogIdentifier=cntools-blockcollector [Install] WantedBy=multi-user.target -EOF' +EOF" ``` **2. Reload systemd** diff --git a/docs/Scripts/cntools-changelog.md b/docs/Scripts/cntools-changelog.md index d2ca4f6b1..0241f8993 100644 --- a/docs/Scripts/cntools-changelog.md +++ b/docs/Scripts/cntools-changelog.md @@ -5,19 +5,15 @@ All notable changes to this tool will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [5.3.4] - 2020-08-18 +## [5.4.1] - 2020-09-10 -##### Changed -- Use manual calculation based on slot tip to get KES period - -## [5.3.3] - 2020-08-14 +##### Fixed +- KES Expiry to use KES Period instead of Epoch duration -##### Added -- Use secure remove (`srm`) when available when deleting files. -## [5.3.2] - 2020-08-05 +## [5.4.0] - 2020-08-23 -> If you're coming version 5.2.1 (not required if you're on 5.3.0), We have made quite a few changes to not use ptn0 in our scripts and source github structures (except template files), alongwith other changes listed beneath. Please follow steps below for upgrade: +> A non-breaking change have been made to files outside of CNTools. Internal update function is not enough to update all files. > - Execute the below (by default it will set you up against mainnet network), do not overwrite config please: > `cd "$CNODE_HOME"/scripts` > `curl -sS -o prereqs.sh https://raw.githubusercontent.com/cardano-community/guild-operators/master/scripts/cnode-helper-scripts/prereqs.sh` @@ -27,6 +23,54 @@ and this adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ======= +##### Added +- Sanity check before launching a second instance of cnode.sh +- Doc update to run cnode.sh as a systemd service + +##### Removed +- `Pool >> Delegators` removed. + - If/when a better option than dumping and parsing ledger-state dump arise re-adding it will be considered. + - Utilize the community explorers listed at https://cardano-community.github.io/support-faq/#/explorers + +##### Fixed +- Block Collector script adapted for cardano-node 1.19.0. + - Block hash is now truncated in log, issue https://github.com/input-output-hk/cardano-node/issues/1738 +- High cpu usage reported in a few cases when running Block Collector + - Depending on log level, parsing and byte64 enc each entry with jq could potentially put high load on weaker systems. Replaced with grep to only parse entries containing specific traces. +- Docs for creating systemd block collector service file updated to include user env in run command + + +## [5.3.6] - 2020-08-22 + +##### Fixed +- cardano-node 1.19.0 introduced an issue that required us to use KES as current - 1 while rotating. + + +## [5.3.5] - 2020-08-20 + +##### Changed +- CNTools now uses and works with `cardano-node 1.19.0`, please upgrade if you're not using this version. + +##### Fixed +- A new getPoolID helper function added to extract both hex and bech32 pool ID + - Added `--output-format hex` when extracting pool ID in hex format + - A new pool.id-bech32 file gets created if cold.vkey is available and decrypted +- Added error check to see if cardano-cli is in $PATH before continuing. + + +## [5.3.4] - 2020-08-18 + +##### Changed +- Use manual calculation based on slot tip to get KES period + +## [5.3.3] - 2020-08-14 + +##### Added +- Use secure remove (`srm`) when available when deleting files. + + +## [5.3.2] - 2020-08-05 + #### Fixed - Backup & Restore paths were failing on machines due to alnum class availability on certain interpreters. - Rewards were not counted in stake and pledge @@ -45,6 +89,7 @@ and this adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - Balance check added to `Funds >> Withdraw` for base address as this is used to pay the withdraw transaction fee - Resolve issue with Multi Owner causing an error with new pool registration (error was due to quotes) + ## [5.3.0] - 2020-08-03 ##### Added - Ability to select a different pool owner and reward wallet diff --git a/docs/Scripts/gliveview.md b/docs/Scripts/gliveview.md new file mode 100644 index 000000000..58dcbe84a --- /dev/null +++ b/docs/Scripts/gliveview.md @@ -0,0 +1,66 @@ +!> Ensure the [Pre-Requisites](basics.md#pre-requisites) are in place before you proceed. + +**Guild LiveView - gLiveView** is a utility to display an equivalent subset of LiveView interface that cardano-node users have grown accustomed to. This is especially useful when moving to a systemd deployment - if you haven't done so already - while looking for a familiar UI to monitor the node status. + +The tool is independent from other files and can run as a standalone utility that can be stopped/started without affecting the status of cardano-node. + +##### Download & Setup + +The tool in itself should only require a single file. If you've used [prereqs.sh](basics.md#pre-requisites), you can skip this part , as this is already set up for you. +To get current epoch blocks, [cntoolsBlockCollector.sh](Scripts/cntools-blocks.md) script is needed. This is optional and **Guild LiveView** will function without it. + +?> For those who follow guild's [folder structure](basics.md#folder-structure) and do not wish to run prereqs.sh, you can run the below in `$CNODE_HOME/scripts` folder + +To download the script: +```bash +curl -s -o gLiveView.sh https://raw.githubusercontent.com/cardano-community/guild-operators/master/scripts/cnode-helper-scripts/gLiveView.sh +chmod 755 gLiveView.sh +``` + +##### Startup + +For most standard deployments, this should lead you to a stage where you can now start running `./gLiveView.sh` in the folder you downloaded the script (default location for cntools users would be `$CNODE_HOME/scripts`). Note that the script is smart enough to automatically detect when you're running as a Core or Relay and will show fields accordingly. + +The tool can be run in legacy mode with only standard ASCII characters for terminals with trouble displaying the box-drawing characters. Run `./gLiveView.sh -h` to show available command-line parameters or permanently set it directly in script. + +A sample output from both core and relay(with peer analysis): + +![Core](https://raw.githubusercontent.com/cardano-community/guild-operators/images/gliveview-core.png) + +![Relay](https://raw.githubusercontent.com/cardano-community/guild-operators/images/gliveview-relay.png) + +##### Description + +**Upper main section** +Displays live metrics gathered from EKG. Epoch number and progress is live from node while date calculation until epoch boundary is based on offline genesis parameters. Reference tip is also an offline calculation based on genesis values used to compare against the node tip to see how far of the tip(diff value) the node is. With current parameters a slot diff up to 40 from reference tip is considered good but it ussually stay below 30. In/Out peers show how many connections the node have established in and out. + +**Core section** +If the node is run as a core, identified by the 'forge-about-to-lead' EKG parameter, a second core section is displayed. This section contain current and remaining KES periods as well as a calculated date for the expiration. When getting close to expire date the values will change color. Blocks created by the node since node start is another metric shown in this section. If [cntoolsBlockCollector.sh](Scripts/cntools-blocks.md) is running for the core node a second row with current epoch blocks is displayed. + +**Peer analysis** +A manual peer analysis can be triggered by key press `p`. A latency test will be done on incoming and outgoing connections to the node. For outgoing connections a normal ICMP ping is done as a first try. If this is blocked, tcptraceroute program is used to do a tcp ping against the cardano-node port of the remote peer. For incoming connections only ICMP ping is used as remote peer port is unknown. It's not uncommon to see many unreachable peers for incoming connections as it's a good security practice to disable ICMP in firewall. + +Once run it will display RTT for the peers and group them in the ranges 0-50, 50-100, 100-200, 200<. The analysis filter out multiple connections to the same IP. Unique Peers + Skipped should match the total number seen in top section. The analysis is **NOT** live. Last update timestamp is shown to know when it was last run. Press `p` for update or `h` to hide it. + +##### Troubleshooting/Customisations + +In case you run into trouble while running the script, you might want to edit `gLiveView.sh` and look at User Variables section shown below. You can override the values if the automatic detection do not provide the right information, but we would appreciate if you could also notify us by raising an issue against github repo: + +```bash +###################################### +# User Variables - Change as desired # +###################################### + +#CNODE_HOME="/opt/cardano/cnode" # Override default CNODE_HOME path +#CNODE_PORT=6000 # Override automatic detection of node port +NODE_NAME="Cardano Node" # Change your node's name prefix here, keep at or below 19 characters! +REFRESH_RATE=2 # How often (in seconds) to refresh the view +#CONFIG="${CNODE_HOME}/files/config.json" # Override automatic detection of node config path +EKG_HOST=127.0.0.1 # Set node EKG host +#EKG_PORT=12788 # Override automatic detection of node EKG port +#PROTOCOL="Cardano" # Default: Combinator network (leave commented if unsure) +#BLOCK_LOG_DIR="${CNODE_HOME}/db/blocks" # CNTools Block Collector block dir set in cntools.config, override path if enabled and using non standard path +legacy_mode=false # (true|false) If enabled unicode box-drawing characters will be replaced by standard ASCII characters +THEME="dark" # dark = suited for terminals with a dark background + # light = suited for terminals with a bright background +``` diff --git a/docs/Scripts/itnrewards.md b/docs/Scripts/itnrewards.md index 43fd7376b..ec2e634dd 100644 --- a/docs/Scripts/itnrewards.md +++ b/docs/Scripts/itnrewards.md @@ -28,15 +28,6 @@ graph TB cd $CNODE_HOME/scripts ./itnRewards.sh MyITNWallet ~/jormu/account/priv/owner.sk ~/jormu/account/priv/owner.pk ``` -- However, if an extended secret key was used(ed25519e_sk), a recent version (min 1.18.x) of cardano-cli is needed to convert the key. - - Follow these instructions to build this version of cardano-cli: -``` bash -cd ~/git/cardano-node # Go to folder where you normally build cardano-node -git checkout release/1.18.x -git pull -$CNODE_HOME/scripts/cabal-build-all.sh -``` - - Re-run `itnRewards.sh` - Start CNTools and verify that correct balance is shown in the wallet reward address - Fund base address of wallet with enough funds to pay for withdraw tx fee - Use FUNDS >> WITHDRAW to move rewards to base address of wallet diff --git a/docs/_sidebar.md b/docs/_sidebar.md index 70d0349ea..c9e25670d 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -11,6 +11,7 @@ - [Common tasks](Scripts/cntools-common.md) - [Block Collector](Scripts/cntools-blocks.md) - [Changelog](Scripts/cntools-changelog.md) + * [Guild LiveView](Scripts/gliveview.md) * [ITN Metadata Proof](Scripts/itnwitness.md) * [ITN Rewards](Scripts/itnrewards.md) * [Topology Updater](Scripts/topologyupdater.md) diff --git a/docs/basics.md b/docs/basics.md index 9ace11c2b..d922a32de 100644 --- a/docs/basics.md +++ b/docs/basics.md @@ -27,13 +27,11 @@ chmod 755 prereqs.sh # -i Interactive mode (Default: silent mode) # -g Connect to guild network instead of public network (Default: connect to public cardano network) # -p Copy Transitional Praos config as default instead of Combinator networks (Default: copies combinator network) - +# -t Alternate name for top level folder # You can use one of the options above, if you'd like to defer from defaults (below). # Running without any parameters will run script in silent mode with OS Dependencies, and overwriting existing files. ./prereqs.sh - -## Follow the prompts for execution. To make sure environment variables are available for session you're running, make sure to source bashrc . "${HOME}/.bashrc" ``` diff --git a/docs/contributors.md b/docs/contributors.md index d192d0ad2..314315254 100644 --- a/docs/contributors.md +++ b/docs/contributors.md @@ -8,11 +8,13 @@ Everyone is welcome to contribute to the guide, as well as the repository. Below [Bergr01](https://github.com/bergr01) [Ola Ahlman](https://github.com/Scitz0) [SmaugPool](https://github.com/SmaugPool) +[Adam](https://github.com/Crypto2099) +[Chris](https://github.com/Straightpool) +[Damjan](https://github.com/DamjanOstrelic) [Priyank](https://github.com/rdlrt) [Psychomb](https://github.com/psychomb) [PegasusPool](https://github.com/PegasusPool) [Matthijs](https://github.com/matthijs-aeon) [Marek](https://github.com/mmahut) - > To start contributing, simply hit the [github repository](https://github.com/cardano-community/guild-operators) and raise Issue/Pull Request diff --git a/files/ptn0-combinator.json b/files/ptn0-combinator.json index feaf54d30..1124c2dd7 100644 --- a/files/ptn0-combinator.json +++ b/files/ptn0-combinator.json @@ -41,14 +41,14 @@ "TracingVerbosity": "MaximalVerbosity", "TurnOnLogMetrics": true, "TurnOnLogging": true, - "ViewMode": "LiveView", + "ViewMode": "SimpleView", "defaultBackends": [ "KatipBK" ], "defaultScribes": [ [ "FileSK", - "/opt/cardano/cnode/logs/node-0.json" + "/opt/cardano/cnode/logs/node0.json" ] ], "hasEKG": 12788, @@ -156,7 +156,7 @@ }, "rotation": { "rpKeepFilesNum": 10, - "rpLogLimitBytes": 5000000, + "rpLogLimitBytes": 50000000, "rpMaxAgeHours": 24 }, "setupBackends": [ @@ -166,7 +166,7 @@ "setupScribes": [ { "scKind": "FileSK", - "scName": "/opt/cardano/cnode/logs/node-0.json", + "scName": "/opt/cardano/cnode/logs/node0.json", "scFormat": "ScJson", "scRotation": null } diff --git a/files/ptn0-mainnet.json b/files/ptn0-mainnet.json index 12d685962..f315e1df9 100644 --- a/files/ptn0-mainnet.json +++ b/files/ptn0-mainnet.json @@ -2,6 +2,7 @@ "ApplicationName": "cardano-sl", "ApplicationVersion": 1, "ByronGenesisFile": "/opt/cardano/cnode/files/byron-genesis.json", + "ByronGenesisHash": "5f20df933584822601f9e3f8c024eb5eb252fe8cefb24d1317dc3d432e940ebb", "LastKnownBlockVersion-Alt": 0, "LastKnownBlockVersion-Major": 2, "LastKnownBlockVersion-Minor": 0, @@ -9,6 +10,7 @@ "Protocol": "Cardano", "RequiresNetworkMagic": "RequiresNoMagic", "ShelleyGenesisFile": "/opt/cardano/cnode/files/genesis.json", + "ShelleyGenesisHash": "1a3be38bcbb7911969283716ad7aa550250226b76a61fc51cc9a9a35d9276d81", "bfcMaxConcurrencyDeadline": 2, "TraceBlockFetchClient": true, "TraceBlockFetchDecisions": true, @@ -39,14 +41,14 @@ "TracingVerbosity": "NormalVerbosity", "TurnOnLogMetrics": true, "TurnOnLogging": true, - "ViewMode": "LiveView", + "ViewMode": "SimpleView", "defaultBackends": [ "KatipBK" ], "defaultScribes": [ [ "FileSK", - "/opt/cardano/cnode/logs/node-0.json" + "/opt/cardano/cnode/logs/node0.json" ] ], "hasEKG": 12788, @@ -154,7 +156,7 @@ }, "rotation": { "rpKeepFilesNum": 10, - "rpLogLimitBytes": 5000000, + "rpLogLimitBytes": 50000000, "rpMaxAgeHours": 24 }, "setupBackends": [ @@ -164,12 +166,13 @@ "setupScribes": [ { "scKind": "FileSK", - "scName": "/opt/cardano/cnode/logs/node-0.json", + "scName": "/opt/cardano/cnode/logs/node0.json", "scFormat": "ScJson", "scRotation": null } ], "SocketPath": "/opt/cardano/cnode/sockets/node0.socket", + "NetworkName": "mainnet", "EnableLogging": true, "EnableLogMetrics": false } diff --git a/files/ptn0-praos.json b/files/ptn0-praos.json index d84f86e78..c5070140e 100644 --- a/files/ptn0-praos.json +++ b/files/ptn0-praos.json @@ -36,14 +36,14 @@ "TracingVerbosity": "MaximalVerbosity", "TurnOnLogMetrics": true, "TurnOnLogging": true, - "ViewMode": "LiveView", + "ViewMode": "SimpleView", "defaultBackends": [ "KatipBK" ], "defaultScribes": [ [ "FileSK", - "/opt/cardano/cnode/logs/node-0.json" + "/opt/cardano/cnode/logs/node0.json" ] ], "hasEKG": 12788, @@ -151,7 +151,7 @@ }, "rotation": { "rpKeepFilesNum": 10, - "rpLogLimitBytes": 5000000, + "rpLogLimitBytes": 50000000, "rpMaxAgeHours": 24 }, "setupBackends": [ @@ -161,7 +161,7 @@ "setupScribes": [ { "scKind": "FileSK", - "scName": "/opt/cardano/cnode/logs/node-0.json", + "scName": "/opt/cardano/cnode/logs/node0.json", "scFormat": "ScJson", "scRotation": null } diff --git a/scripts/cnode-helper-scripts/cabal-build-all.sh b/scripts/cnode-helper-scripts/cabal-build-all.sh index 2b26ebe60..e5ed540aa 100755 --- a/scripts/cnode-helper-scripts/cabal-build-all.sh +++ b/scripts/cnode-helper-scripts/cabal-build-all.sh @@ -14,6 +14,6 @@ grep "^Linking" /tmp/build.log | while read -r line ; do act_bin_path=$(echo "$line" | awk '{print $2}') act_bin=$(echo "$act_bin_path" | awk -F "/" '{print $NF}') echo "Copying $act_bin to $HOME/.cabal/bin/" - cp "$act_bin_path" "$HOME/.cabal/bin/" + cp -f "$act_bin_path" "$HOME/.cabal/bin/" done diff --git a/scripts/cnode-helper-scripts/cnode.sh.templ b/scripts/cnode-helper-scripts/cnode.sh.templ index ec652a0ea..7d61f48ce 100755 --- a/scripts/cnode-helper-scripts/cnode.sh.templ +++ b/scripts/cnode-helper-scripts/cnode.sh.templ @@ -3,14 +3,48 @@ [[ -z "$CNODE_HOME" ]] && CNODE_HOME="/opt/cardano/cnode" +. "${CNODE_HOME}"/scripts/env + +if [[ -S "$CNODE_HOME/sockets/node0.socket" ]]; then + if [ `ps -ef | grep -c [c]ardano-node.*.$CNODE_HOME/sockets/node0.socket` -gt 0 ]; then + echo "ERROR: A Cardano node is already running, please terminate this node before starting a new one with this script." + exit 1 + else + echo "WARN: A prior running Cardano node was not cleanly shutdown, socket file still exists. Cleaning up." + unlink $CNODE_HOME/sockets/node0.socket + fi +fi + [[ ! -d "$CNODE_HOME/logs/archive" ]] && mkdir -p "$CNODE_HOME/logs/archive" -[[ $(find "$CNODE_HOME"/logs/*.json | wc -l) -gt 0 ]] && mv $CNODE_HOME/logs/*.json $CNODE_HOME/logs/archive/ +[[ $(find "$CNODE_HOME"/logs/*.json 2>/dev/null | wc -l) -gt 0 ]] && mv $CNODE_HOME/logs/*.json $CNODE_HOME/logs/archive/ + +## Static (content that will not be overwritten by prereqs.sh) +## Begin -cardano-node run \ +POOL_NAME="TEST" +CNODE_PORT=6000 +POOL_DIR="$CNODE_HOME/priv/pool/$POOL_NAME" + +if [[ -f "$POOL_DIR/op.cert" && -f "$POOL_DIR/vrf.skey" && -f "$POOL_DIR/hot.skey" ]]; then + cardano-node run \ --topology $CNODE_HOME/files/topology.json \ --config $CNODE_HOME/files/config.json \ --database-path $CNODE_HOME/db \ --socket-path $CNODE_HOME/sockets/node0.socket \ --host-addr 0.0.0.0 \ - --port 6000 + --shelley-kes-key $POOL_DIR/hot.skey \ + --shelley-vrf-key $POOL_DIR/vrf.skey \ + --shelley-operational-certificate $POOL_DIR/op.cert \ + --port $CNODE_PORT +else + cardano-node run \ + --topology $CNODE_HOME/files/topology.json \ + --config $CNODE_HOME/files/config.json \ + --database-path $CNODE_HOME/db \ + --socket-path $CNODE_HOME/sockets/node0.socket \ + --host-addr 0.0.0.0 \ + --port $CNODE_PORT +fi + +## End diff --git a/scripts/cnode-helper-scripts/cntools.library b/scripts/cnode-helper-scripts/cntools.library index 8e150f24c..bc7767054 100644 --- a/scripts/cnode-helper-scripts/cntools.library +++ b/scripts/cnode-helper-scripts/cntools.library @@ -9,9 +9,9 @@ # Any breaking changes (eg: that requires change of cntools.config, env or a change in priv folder would be considered breaking and will be exempt from auto-update) CNTOOLS_MAJOR_VERSION=5 # Changes that can be applied without breaking existing functionality that can be applied from within CNTools -CNTOOLS_MINOR_VERSION=3 +CNTOOLS_MINOR_VERSION=4 # Backwards compatible bug fixes. No additional functionality or major changes and can be applied from within CNTools -CNTOOLS_PATCH_VERSION=4 +CNTOOLS_PATCH_VERSION=1 CNTOOLS_VERSION="${CNTOOLS_MAJOR_VERSION}.${CNTOOLS_MINOR_VERSION}.${CNTOOLS_PATCH_VERSION}" ############################################################ @@ -486,8 +486,7 @@ kesExpiration() { current_kes_period=$(getCurrentKESperiod) remaining_kes_periods=$(( max_kes_evolutions - ( current_kes_period - pool_kes_start_period ) )) current_time_sec=$(date -u +%s) - slot_in_epoch=$(grep "cardano_node_ChainDB_metrics_slotInEpoch_int" "${TMP_FOLDER}"/prom_metrics | awk '{print $2}') - expiration_time_sec=$(( current_time_sec - ( slot_length * slot_in_epoch ) + ( slot_length * slots_per_kes_period * remaining_kes_periods ) )) + expiration_time_sec=$(( current_time_sec - ( slot_length * slots_per_kes_period ) + ( slot_length * slots_per_kes_period * remaining_kes_periods ) )) expiration_time_sec_diff=$(( expiration_time_sec - current_time_sec )) expiration_date=$(date --date=@${expiration_time_sec}) } @@ -615,6 +614,25 @@ fractionToPCT() { } +# Command : getPoolID [pool name] +# Description: create and save pool id in hex & bech32 encoded format +# Return : populates $pool_id & $pool_id_bech32 +getPoolID() { + pool_id_file="${POOL_FOLDER}/${1}/${POOL_ID_FILENAME}" + pool_id="" + pool_id_bech32_file="${POOL_FOLDER}/${1}/${POOL_ID_FILENAME}-bech32" + pool_id_bech32="" + pool_coldkey_vk_file="${POOL_FOLDER}/${1}/${POOL_COLDKEY_VK_FILENAME}" + if [[ -f ${pool_coldkey_vk_file} ]]; then + pool_id=$(${CCLI} shelley stake-pool id --verification-key-file "${pool_coldkey_vk_file}" --output-format hex 2>/dev/null | tee "${pool_id_file}") && \ + pool_id_bech32=$(${CCLI} shelley stake-pool id --verification-key-file "${pool_coldkey_vk_file}" 2>/dev/null | tee "${pool_id_bech32_file}") && \ + return 0 || return 1 + fi + [[ -f ${pool_id_file} ]] && pool_id=$(cat ${pool_id_file}) + [[ -f ${pool_id_bech32_file} ]] && pool_id_bech32=$(cat ${pool_id_bech32_file}) +} + + # Command : getPayAddress [wallet name] # Description: create and save payment address # Return : populates $pay_addr @@ -861,7 +879,7 @@ sendADA() { # Return : 0 if registered, else 1 isWalletRegistered() { if getRewardAddress $1; then - stakeAddressInfo=$(${CCLI} shelley query stake-address-info ${PROTOCOL_IDENTIFIER} ${NETWORK_IDENTIFIER} --address ${reward_addr} | jq -r '.[] // empty') + stakeAddressInfo=$(${CCLI} shelley query stake-address-info ${PROTOCOL_IDENTIFIER} ${NETWORK_IDENTIFIER} --address ${reward_addr} | jq -r '.[0] // empty') [[ -n "${stakeAddressInfo}" ]] && return 0 fi return 1 @@ -1709,7 +1727,7 @@ rotatePoolKeys() { return 1 fi - start_kes_period=$(getCurrentKESperiod) + start_kes_period=$(( $(getCurrentKESperiod) - 1 )) echo "${start_kes_period}" > ${pool_saved_kes_start} say "creating new hot keys and certificate" 1 diff --git a/scripts/cnode-helper-scripts/cntools.sh b/scripts/cnode-helper-scripts/cntools.sh index 8b540caec..d15790b03 100755 --- a/scripts/cnode-helper-scripts/cntools.sh +++ b/scripts/cnode-helper-scripts/cntools.sh @@ -346,8 +346,8 @@ case $OPERATION in if [[ -n ${delegation_pool_id} ]]; then unset poolName while IFS= read -r -d '' pool; do - pool_id=$(cat "${pool}/${POOL_ID_FILENAME}") - if [[ "${pool_id}" = "${delegation_pool_id}" ]]; then + getPoolID "$(basename ${pool})" + if [[ "${pool_id_bech32}" = "${delegation_pool_id}" ]]; then poolName=$(basename ${pool}) && break fi done < <(find "${POOL_FOLDER}" -mindepth 1 -maxdepth 1 -type d -print0 | sort -z) @@ -437,8 +437,8 @@ case $OPERATION in if [[ -n ${delegation_pool_id} ]]; then unset poolName while IFS= read -r -d '' pool; do - pool_id=$(cat "${pool}/${POOL_ID_FILENAME}") - if [[ "${pool_id}" = "${delegation_pool_id}" ]]; then + getPoolID "$(basename ${pool})" + if [[ "${pool_id_bech32}" = "${delegation_pool_id}" ]]; then poolName=$(basename ${pool}) && break fi done < <(find "${POOL_FOLDER}" -mindepth 1 -maxdepth 1 -type d -print0 | sort -z) @@ -1017,12 +1017,12 @@ case $OPERATION in getBalance ${base_addr} [[ ${lovelace} -eq 0 ]] && continue if getRewardAddress ${dir}; then - delegation_pool_id=$(${CCLI} shelley query stake-address-info ${PROTOCOL_IDENTIFIER} ${NETWORK_IDENTIFIER} --address "${reward_addr}" | jq -r '.[].delegation // empty') + delegation_pool_id=$(${CCLI} shelley query stake-address-info ${PROTOCOL_IDENTIFIER} ${NETWORK_IDENTIFIER} --address "${reward_addr}" | jq -r '.[0].delegation // empty') unset poolName if [[ -n ${delegation_pool_id} ]]; then while IFS= read -r -d '' pool; do - pool_id=$(cat "${pool}/${POOL_ID_FILENAME}") - if [[ "${pool_id}" = "${delegation_pool_id}" ]]; then + getPoolID "$(basename ${pool})" + if [[ "${pool_id_bech32}" = "${delegation_pool_id}" ]]; then poolName=$(basename ${pool}) && break fi done < <(find "${POOL_FOLDER}" -mindepth 1 -maxdepth 1 -type d -print0 | sort -z) @@ -1155,25 +1155,23 @@ case $OPERATION in say " ) Retire - de-register stake pool from chain in specified epoch" say " ) List - a compact list view of available local pools" say " ) Show - detailed view of specified pool" - say " ) Delegators - list all delegators for pool" say " ) Rotate - rotate pool KES keys" say " ) Decrypt - remove write protection and decrypt pool" say " ) Encrypt - encrypt pool cold keys and make all files immutable" say "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" say " Select Pool operation\n" - case $(select_opt "[n] New" "[r] Register" "[m] Modify" "[x] Retire" "[l] List" "[s] Show" "[g] Delegators" "[o] Rotate" "[d] Decrypt" "[e] Encrypt" "[h] Home") in + case $(select_opt "[n] New" "[r] Register" "[m] Modify" "[x] Retire" "[l] List" "[s] Show" "[o] Rotate" "[d] Decrypt" "[e] Encrypt" "[h] Home") in 0) SUBCOMMAND="new" ;; 1) SUBCOMMAND="register" ;; 2) SUBCOMMAND="modify" ;; 3) SUBCOMMAND="retire" ;; 4) SUBCOMMAND="list" ;; 5) SUBCOMMAND="show" ;; - 6) SUBCOMMAND="delegators" ;; - 7) SUBCOMMAND="rotate" ;; - 8) SUBCOMMAND="decrypt" ;; - 9) SUBCOMMAND="encrypt" ;; - 10) continue ;; + 6) SUBCOMMAND="rotate" ;; + 7) SUBCOMMAND="decrypt" ;; + 8) SUBCOMMAND="encrypt" ;; + 9) continue ;; esac case $SUBCOMMAND in @@ -1193,7 +1191,6 @@ case $OPERATION in say "" mkdir -p "${POOL_FOLDER}/${pool_name}" - pool_id_file="${POOL_FOLDER}/${pool_name}/${POOL_ID_FILENAME}" pool_hotkey_vk_file="${POOL_FOLDER}/${pool_name}/${POOL_HOTKEY_VK_FILENAME}" pool_hotkey_sk_file="${POOL_FOLDER}/${pool_name}/${POOL_HOTKEY_SK_FILENAME}" pool_coldkey_vk_file="${POOL_FOLDER}/${pool_name}/${POOL_COLDKEY_VK_FILENAME}" @@ -1214,12 +1211,13 @@ case $OPERATION in rm -r ${POOL_FOLDER}'-pregen/'${pool_name} else ${CCLI} shelley node key-gen --cold-verification-key-file "${pool_coldkey_vk_file}" --cold-signing-key-file "${pool_coldkey_sk_file}" --operational-certificate-issue-counter-file "${pool_opcert_counter_file}" - ${CCLI} shelley stake-pool id --verification-key-file "${pool_coldkey_vk_file}" > "${pool_id_file}" fi ${CCLI} shelley node key-gen-VRF --verification-key-file "${pool_vrf_vk_file}" --signing-key-file "${pool_vrf_sk_file}" + getPoolID ${pool_name} say "Pool: ${GREEN}${pool_name}${NC}" "log" - say "PoolPubKey: $(cat "${pool_id_file}")" "log" + [[ -n ${pool_id} ]] && say "ID (hex) : ${pool_id}" "log" + [[ -n ${pool_id_bech32} ]] && say "ID (bech32) : ${pool_id_bech32}" "log" say "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" waitForInput && continue @@ -1495,12 +1493,12 @@ case $OPERATION in getBalance ${base_addr} [[ ${lovelace} -eq 0 ]] && continue if getRewardAddress ${dir}; then - delegation_pool_id=$(${CCLI} shelley query stake-address-info ${PROTOCOL_IDENTIFIER} ${NETWORK_IDENTIFIER} --address "${reward_addr}" | jq -r '.[].delegation // empty') + delegation_pool_id=$(${CCLI} shelley query stake-address-info ${PROTOCOL_IDENTIFIER} ${NETWORK_IDENTIFIER} --address "${reward_addr}" | jq -r '.[0].delegation // empty') unset poolName if [[ -n ${delegation_pool_id} ]]; then while IFS= read -r -d '' pool; do - pool_id=$(cat "${pool}/${POOL_ID_FILENAME}") - if [[ "${pool_id}" = "${delegation_pool_id}" ]]; then + getPoolID "$(basename ${pool})" + if [[ "${pool_id_bech32}" = "${delegation_pool_id}" ]]; then poolName=$(basename ${pool}) && break fi done < <(find "${POOL_FOLDER}" -mindepth 1 -maxdepth 1 -type d -print0 | sort -z) @@ -1973,12 +1971,12 @@ case $OPERATION in getBalance ${base_addr} [[ ${lovelace} -eq 0 ]] && continue if getRewardAddress ${dir}; then - delegation_pool_id=$(${CCLI} shelley query stake-address-info ${PROTOCOL_IDENTIFIER} ${NETWORK_IDENTIFIER} --address "${reward_addr}" | jq -r '.[].delegation // empty') + delegation_pool_id=$(${CCLI} shelley query stake-address-info ${PROTOCOL_IDENTIFIER} ${NETWORK_IDENTIFIER} --address "${reward_addr}" | jq -r '.[0].delegation // empty') unset poolName if [[ -n ${delegation_pool_id} ]]; then while IFS= read -r -d '' pool; do - pool_id=$(cat "${pool}/${POOL_ID_FILENAME}") - if [[ "${pool_id}" = "${delegation_pool_id}" ]]; then + getPoolID "$(basename ${pool})" + if [[ "${pool_id_bech32}" = "${delegation_pool_id}" ]]; then poolName=$(basename ${pool}) && break fi done < <(find "${POOL_FOLDER}" -mindepth 1 -maxdepth 1 -type d -print0 | sort -z) @@ -2284,11 +2282,12 @@ case $OPERATION in while IFS= read -r -d '' pool; do say "" - pool_id=$(cat "${pool}/${POOL_ID_FILENAME}") + getPoolID "$(basename ${pool})" pool_regcert_file="${pool}/${POOL_REGCERT_FILENAME}" [[ -f "${pool_regcert_file}" ]] && pool_registered="YES" || pool_registered="NO" say "${GREEN}$(basename ${pool})${NC} " - say "$(printf "%-21s : %s" "ID" "${pool_id}")" "log" + say "$(printf "%-21s : %s" "ID (hex)" "${pool_id}")" "log" + [[ -n ${pool_id_bech32} ]] && say "$(printf "%-21s : %s" "ID (bech32)" "${pool_id_bech32}")" "log" say "$(printf "%-21s : %s" "Registered" "${pool_registered}")" "log" if [[ -f "${pool}/${POOL_CURRENT_KES_START}" ]]; then kesExpiration "$(cat "${pool}/${POOL_CURRENT_KES_START}")" @@ -2327,8 +2326,8 @@ case $OPERATION in pool_dirs=() if ! getDirs "${POOL_FOLDER}"; then continue; fi # dirs() array populated with all pool folders for dir in "${dirs[@]}"; do - pool_id="${POOL_FOLDER}/${dir}/${POOL_ID_FILENAME}" - [[ ! -f "${pool_id}" ]] && continue + pool_id_file="${POOL_FOLDER}/${dir}/${POOL_ID_FILENAME}" + [[ ! -f "${pool_id_file}" ]] && continue pool_dirs+=("${dir}") done if [[ ${#pool_dirs[@]} -eq 0 ]]; then @@ -2349,13 +2348,14 @@ case $OPERATION in say "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" say "" - pool_id=$(cat "${POOL_FOLDER}/${pool_name}/${POOL_ID_FILENAME}") + getPoolID ${pool_name} ledger_pParams=$(jq -r '.esLState._delegationState._pstate._pParams."'"${pool_id}"'" // empty' "${TMP_FOLDER}"/ledger-state.json) ledger_fPParams=$(jq -r '.esLState._delegationState._pstate._fPParams."'"${pool_id}"'" // empty' "${TMP_FOLDER}"/ledger-state.json) [[ -z "${ledger_fPParams}" ]] && ledger_fPParams="${ledger_pParams}" [[ -n "${ledger_pParams}" ]] && pool_registered="YES" || pool_registered="NO" say "${GREEN}${pool_name}${NC} " - say "$(printf "%-21s : %s" "ID" "${pool_id}")" "log" + say "$(printf "%-21s : %s" "ID (hex)" "${pool_id}")" "log" + [[ -n ${pool_id_bech32} ]] && say "$(printf "%-21s : %s" "ID (bech32)" "${pool_id_bech32}")" "log" say "$(printf "%-21s : %s" "Registered" "${pool_registered}")" "log" pool_config="${POOL_FOLDER}/${pool_name}/${POOL_CONFIG_FILENAME}" if [[ -f "${pool_config}" ]]; then @@ -2483,57 +2483,6 @@ case $OPERATION in ;; ################################################################### - delegators) - - clear - say " >> POOL >> DELEGATORS" "log" - say "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" - say "" - - if [[ ! -f "${TMP_FOLDER}"/protparams.json ]]; then - say "${RED}ERROR${NC}: CNTools started without node access, only offline functions available!" - waitForInput && continue - fi - - pool_dirs=() - if ! getDirs "${POOL_FOLDER}"; then continue; fi # dirs() array populated with all pool folders - for dir in "${dirs[@]}"; do - pool_id="${POOL_FOLDER}/${dir}/${POOL_ID_FILENAME}" - [[ ! -f "${pool_id}" ]] && continue - pool_dirs+=("${dir}") - done - if [[ ${#pool_dirs[@]} -eq 0 ]]; then - say "${ORANGE}WARN${NC}: No pools available!" - say "first create a pool" - waitForInput && continue - fi - say "Select Pool:\n" - if ! selectDir "${pool_dirs[@]}"; then continue; fi # ${dir_name} populated by selectDir function - pool_name="${dir_name}" - pool_id=$(cat "${POOL_FOLDER}/${pool_name}/${POOL_ID_FILENAME}") - - say "Looking for delegators, please wait..." - if ! getDelegators ${pool_id}; then - waitForInput && continue - fi - - clear - say " >> POOL >> DELEGATORS" "log" - say "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" - say "" - say "${BLUE}${delegator_nbr}${NC} wallet(s) delegated to ${GREEN}${pool_name}${NC} of which ${ORANGE}${owner_nbr}${NC} are owner(s)\n" - say "Total Stake: $(formatLovelace ${total_stake}) ADA [ owners pledge: $(formatLovelace ${total_pledged}) | delegators: $(formatLovelace $((total_stake-total_pledged))) ]\n" - - if [[ ${total_pledged} -lt ${pledge} ]]; then - say "${ORANGE}WARN${NC}: Owners pledge does not cover registered pledge of $(formatLovelace ${pledge}) ADA\n" - fi - - printTable ';' "$(say 'Hex Key;Stake;Rewards' | cat - <(jq -r -c '.[] | "\(.hex_key);\(.stake);\(.rewards)"' <<< "${delegators_json}"))" - - waitForInput && continue - - ;; ################################################################### - rotate) clear diff --git a/scripts/cnode-helper-scripts/cntoolsBlockCollector.sh b/scripts/cnode-helper-scripts/cntoolsBlockCollector.sh index 8d728091e..e4ab32fb8 100755 --- a/scripts/cnode-helper-scripts/cntoolsBlockCollector.sh +++ b/scripts/cnode-helper-scripts/cntoolsBlockCollector.sh @@ -46,13 +46,10 @@ echo "monitoring json logfile for block traces" # Continuously parse cardano-node json log file for block traces while read -r logentry; do - _jq() { - echo "${logentry}" | base64 --decode | jq -r "${1}" - } - if [[ $(_jq '.data.kind') = "TraceNodeIsLeader" ]]; then - at="$(_jq '.at')" + if grep -q "TraceNodeIsLeader" <<< "${logentry}"; then + at="$(jq -r '.at' <<< ${logentry})" at_local="$(date '+%F_%T_%Z' -d "${at}")" - slot="$(_jq '.data.slot')" + slot="$(jq -r '.data.slot' <<< ${logentry})" epoch=$(getEpoch) # create epoch block file if missing blocks_file="${BLOCK_LOG_DIR}/blocks_${epoch}.json" @@ -70,10 +67,10 @@ while read -r logentry; do '. += [{"at": $_at,"slot": $_slot}]' \ "${blocks_file}" > "${TMP_FOLDER}/blocks.json" && mv -f "${TMP_FOLDER}/blocks.json" "${blocks_file}" fi - elif [[ $(_jq '.data.kind') = "TraceAdoptedBlock" ]]; then - slot="$(_jq '.data.slot')" - [[ "$(_jq '.data."block hash"')" =~ ([[:alnum:]]+) ]] && block_hash="${BASH_REMATCH[1]}" || block_hash="" - block_size="$(_jq '.data."block size"')" + elif grep -q "TraceAdoptedBlock" <<< "${logentry}"; then + slot="$(jq -r '.data.slot' <<< ${logentry})" + [[ "$(jq -r '.data.blockHash' <<< ${logentry})" =~ ([[:alnum:]]+) ]] && block_hash="${BASH_REMATCH[1]}" || block_hash="" + block_size="$(jq -r '.data.blockSize' <<< ${logentry})" epoch=$(( slot / $(jq -r .epochLength "${GENESIS_JSON}") )) echo " ~~ ADOPTED BLOCK ~~" printTable ',' "Size,Hash\n${block_size},${block_hash}" @@ -82,15 +79,15 @@ while read -r logentry; do --arg _block_hash "${block_hash}" \ '[.[] | select(.slot == $_slot) += {"size": $_block_size,"hash": $_block_hash}]' \ "${blocks_file}" > "${TMP_FOLDER}/blocks.json" && mv -f "${TMP_FOLDER}/blocks.json" "${blocks_file}" - elif [[ $(_jq '.data.kind') = "TraceForgedInvalidBlock" ]]; then - slot="$(_jq '.data.slot')" + elif grep -q "TraceForgedInvalidBlock" <<< "${logentry}"; then + slot="$(jq -r '.data.slot' <<< ${logentry})" epoch=$(( slot / $(jq -r .epochLength "${GENESIS_JSON}") )) echo " ~~ INVALID BLOCK ~~" echo "Base 64 encoded json trace" echo -e "run this command to decode:\necho ${logentry} | base64 -d | jq -r" jq --arg _slot "${slot}" \ - --arg _json_trace "Invalid Block (base64 enc json): ${logentry}" \ + --arg _json_trace "Invalid Block (base64 enc json): $(jq -c -r '. | @base64' <<< ${logentry})" \ '[.[] | select(.slot == $_slot) += {"hash": $_json_trace}]' \ "${blocks_file}" > "${TMP_FOLDER}/blocks.json" && mv -f "${TMP_FOLDER}/blocks.json" "${blocks_file}" fi -done < <(tail -F -n0 "${logfile}" | jq -c -r '. | @base64') \ No newline at end of file +done < <(tail -F -n0 "${logfile}") \ No newline at end of file diff --git a/scripts/cnode-helper-scripts/deploy-as-systemd.sh b/scripts/cnode-helper-scripts/deploy-as-systemd.sh new file mode 100755 index 000000000..203c2118c --- /dev/null +++ b/scripts/cnode-helper-scripts/deploy-as-systemd.sh @@ -0,0 +1,28 @@ +sudo bash -c "cat << 'EOF' > /etc/systemd/system/cnode.service +[Unit] +Description=Cardano Node +After=network.target + +[Service] +Type=simple +Restart=on-failure +RestartSec=5 +User=$USER +LimitNOFILE=1048576 +WorkingDirectory=$CNODE_HOME/scripts +ExecStart=/bin/bash -l -c \"exec $CNODE_HOME/scripts/cnode.sh\" +ExecStop=/bin/bash -l -c \"exec kill -2 \$(ps -ef | grep [c]ardano-node.*.${CNODE_HOME} | tr -s ' ' | cut -d ' ' -f2)\" +KillSignal=SIGINT +SuccessExitStatus=143 +StandardOutput=syslog +StandardError=syslog +SyslogIdentifier=cnode +TimeoutStopSec=5 +KillMode=mixed + +[Install] +WantedBy=multi-user.target +EOF" + +sudo systemctl daemon-reload +sudo systemctl enable cnode.service diff --git a/scripts/cnode-helper-scripts/env b/scripts/cnode-helper-scripts/env index f43b860b7..ef7724212 100644 --- a/scripts/cnode-helper-scripts/env +++ b/scripts/cnode-helper-scripts/env @@ -1,6 +1,16 @@ #!/usr/bin/env bash # shellcheck disable=SC2034,SC2086,SC2230 -CCLI=$(which cardano-cli) +CCLI=$(command -v cardano-cli) +if [[ -z "${CCLI}" ]]; then + if [[ -f "${HOME}/.cabal/bin/cardano-cli" ]]; then + # Assumption being made that cardano-cli and cardano-node are both present, if not - prereqs and build instructions were not followed + export PATH="${HOME}/.cabal/bin":$PATH + CCLI=$(command -v cardano-cli) + else + echo "You do not have a cardano-cli binary available in \$PATH. Please make it available before using CNTools. Refer to guild-operators documentation for the same." + exit 1 + fi +fi CNODE_HOME=/opt/cardano/cnode CONFIG=$CNODE_HOME/files/config.json PROTOCOL=$(jq -r .Protocol "$CONFIG") diff --git a/scripts/cnode-helper-scripts/gLiveView.sh b/scripts/cnode-helper-scripts/gLiveView.sh new file mode 100755 index 000000000..4929b2305 --- /dev/null +++ b/scripts/cnode-helper-scripts/gLiveView.sh @@ -0,0 +1,915 @@ +#!/bin/bash +#shellcheck disable=SC2009,SC2034,SC2059,SC2206,SC2086,SC2015 + +GLV_VERSION=v1.1 + +###################################### +# User Variables - Change as desired # +# Leave as is if usure # +###################################### + +#CNODE_HOME="/opt/cardano/cnode" # Override default CNODE_HOME path +#CNODE_PORT=6000 # Override automatic detection of node port +NODE_NAME="Cardano Node" # Change your node's name prefix here, keep at or below 19 characters! +REFRESH_RATE=2 # How often (in seconds) to refresh the view +#CONFIG="${CNODE_HOME}/files/config.json" # Override automatic detection of node config path +EKG_HOST=127.0.0.1 # Set node EKG host +#EKG_PORT=12788 # Override automatic detection of node EKG port +#PROTOCOL="Cardano" # Default: Combinator network (leave commented if unsure) +#BLOCK_LOG_DIR="${CNODE_HOME}/db/blocks" # CNTools Block Collector block dir set in cntools.config, override path if enabled and using non standard path +LEGACY_MODE=false # (true|false) If enabled unicode box-drawing characters will be replaced by standard ASCII characters +THEME="dark" # dark = suited for terminals with a dark background + # light = suited for terminals with a bright background + +##################################### +# Themes # +##################################### + +setTheme() { + if [[ ${THEME} = "dark" ]]; then + style_title=${FG_MAGENTA}${BOLD} # style of title + style_base=${FG_WHITE} # default color for text and lines + style_values_1=${FG_CYAN} # color of most live values + style_values_2=${FG_GREEN} # color of node name + style_info=${FG_YELLOW} # info messages + style_status_1=${FG_GREEN} # :) + style_status_2=${FG_YELLOW} # :| + style_status_3=${FG_RED} # :( + style_status_4=${FG_MAGENTA} # :(( + elif [[ ${THEME} = "light" ]]; then + style_title=${FG_MAGENTA}${BOLD} # style of title + style_base=${FG_BLACK} # default color for text and lines + style_values_1=${FG_BLUE} # color of most live values + style_values_2=${FG_GREEN} # color of node name + style_info=${FG_YELLOW} # info messages + style_status_1=${FG_GREEN} # :) + style_status_2=${FG_YELLOW} # :| + style_status_3=${FG_RED} # :( + style_status_4=${FG_MAGENTA} # :(( + else + myExit 1 "Please specify a valid THEME name!" + fi +} + +##################################### +# Do NOT Modify below # +##################################### + +tput smcup # Save screen +tput civis # Disable cursor +stty -echo # Disable user input + +# General exit handler +cleanup() { + [[ -n $1 ]] && err=$1 || err=$? + tput rmcup # restore screen + tput cnorm # restore cursor + tput sgr0 # turn off all attributes + [[ -n ${exit_msg} ]] && echo -e "\n${exit_msg}\n" || echo -e "\nGuild LiveView terminated, cleaning up...\n" + exit $err +} +trap cleanup HUP INT TERM +trap 'stty echo' EXIT + +# Command : myExit [exit code] [message] +# Description : gracefully handle an exit and restore terminal to original state +myExit() { + exit_msg="$2" + cleanup "$1" +} + +usage() { + cat </dev/null; then + myExit 1 "'ss' command missing, please install using latest prereqs.sh script or with your packet manager of choice.\nhttps://command-not-found.com/ss can be used to check package name to install." +elif ! command -v "tcptraceroute" &>/dev/null; then + myExit 1 "'tcptraceroute' command missing, please install using latest prereqs.sh script or with your packet manager of choice.\nhttps://command-not-found.com/tcptraceroute can be used to check package name to install." +fi + +####################################################### +# Version Check # +####################################################### +clear +echo "Guild LiveView version check..." +URL="https://raw.githubusercontent.com/cardano-community/guild-operators/master/scripts/cnode-helper-scripts" +if wget -q -T 10 -O /tmp/gLiveView.sh "${URL}/gLiveView.sh" 2>/dev/null; then + GIT_VERSION=$(grep -r ^GLV_VERSION= /tmp/gLiveView.sh | cut -d'=' -f2) + : "${GIT_VERSION:=v0.0}" + if [[ "${GLV_VERSION}" != "${GIT_VERSION}" ]]; then + echo -e "\nA new version of Guild LiveView is available" + echo "Installed Version : ${GLV_VERSION}" + echo "Available Version : ${GIT_VERSION}" + echo -e "\nPress 'u' to update to latest version, or any other key to continue\n" + read -r -n 1 -s -p "" answer + if [[ "${answer}" = "u" ]]; then + mv "${CNODE_HOME}/scripts/gLiveView.sh" "${CNODE_HOME}/scripts/gLiveView.sh.bkp_$(date +%s)" + cp -f /tmp/gLiveView.sh "${CNODE_HOME}/scripts/gLiveView.sh" + chmod 750 "${CNODE_HOME}/scripts/gLiveView.sh" + myExit 0 "Update applied successfully!\n\nPlease start Guild LiveView again!" + fi + fi +else + echo -e "\nFailed to download gLiveView.sh from GitHub, unable to perform version check!\n" + read -r -n 1 -s -p "press any key to proceed" answer +fi + +####################################################### +# Automatically grab a few parameters # +# Can be overridden in 'User Variables' section above # +####################################################### + +# The commands below will try to detect the information assuming you run single node on a machine. +# Please override values if they dont match your system in the 'User Variables' section below +[[ ${#NODE_NAME} -gt 19 ]] && myExit 1 "Please keep node name at or below 19 characters in length!" +[[ ! ${REFRESH_RATE} =~ ^[0-9]+$ ]] && myExit 1 "Please set a valid refresh rate number!" +if [[ ${EKG_HOST} =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then + OIFS=$IFS + IFS='.' + EKG_OCTETS=(${EKG_HOST}) + IFS=$OIFS + if ! [[ ${EKG_OCTETS[0]} -le 255 && ${EKG_OCTETS[1]} -le 255 && ${EKG_OCTETS[2]} -le 255 && ${EKG_OCTETS[3]} -le 255 ]]; then + myExit 1 "Not a valid IP range set for EKG host, please check configuration!" + fi +else + myExit 1 "Not a valid IP format set for EKG host, please check configuration!" +fi +[[ -z "${CNODE_HOME}" ]] && CNODE_HOME=/opt/cardano/cnode +if [[ -z "${CNODE_PORT}" ]]; then + if [[ "$(ps -ef | grep "[c]ardano-node.*.${CNODE_HOME}")" =~ --port[[:space:]]([[:digit:]]+) ]]; then + CNODE_PORT=${BASH_REMATCH[1]} + else + myExit 1 "Node port not set and automatic detection failed!" + fi +fi +if [[ -z "${CONFIG}" ]]; then + if [[ "$(ps -ef | grep "[c]ardano-node.*.${CNODE_HOME}")" =~ --config[[:space:]]([^[:space:]]+) ]]; then + CONFIG=${BASH_REMATCH[1]} + else + myExit 1 "Node config not set and automatic detection failed!" + fi +fi +if [[ -f "${CONFIG}" ]]; then + if ! EKG_PORT=$(jq -er '.hasEKG' "${CONFIG}" 2>/dev/null); then + myExit 1 "Could not get 'hasEKG' port from the node configuration file" + fi + if ! PROTOCOL=$(jq -er '.Protocol' "${CONFIG}" 2>/dev/null); then + myExit 1 "Could not get 'Protocol' from the node configuration file" + fi +else + myExit 1 "Node config not found: ${CONFIG}" +fi +[[ -z ${BLOCK_LOG_DIR} && -d "${CNODE_HOME}/db/blocks" ]] && BLOCK_LOG_DIR="${CNODE_HOME}/db/blocks" # optional + +# Style +width=63 +second_col=$((width/2 + 3)) +FG_BLACK=$(tput setaf 0) +FG_RED=$(tput setaf 1) +FG_GREEN=$(tput setaf 2) +FG_YELLOW=$(tput setaf 3) +FG_BLUE=$(tput setaf 4) +FG_MAGENTA=$(tput setaf 5) +FG_CYAN=$(tput setaf 6) +FG_WHITE=$(tput setaf 7) +STANDOUT=$(tput smso) +BOLD=$(tput bold) + +setTheme # call function to set theme colors +NC=$(tput sgr0 && printf "${style_base}") # reset style and set base color + +# Progressbar +if [[ ${LEGACY_MODE} = "true" ]]; then + char_marked="#" + char_unmarked="." +else + char_marked=$(printf "\\u258C") + char_unmarked=$(printf "\\u2596") +fi +granularity=$((width-3)) +granularity_small=$((granularity/2)) +bar_col_small=$((width - granularity_small)) + +# Lines +if [[ ${LEGACY_MODE} = "true" ]]; then + VL=$(printf "${NC}|") + HL=$(printf "${NC}=") + LVL=$(printf "${NC}|") + RVL=$(printf "${NC}|") + UHL=$(printf "${NC}=") + DHL=$(printf "${NC}=") + UR=$(printf "${NC}=") + UL=$(printf "${NC}|") + DR=$(printf "${NC}|") + DL=$(printf "${NC}|") + tdivider=$(printf "${NC}|" && printf "%0.s=" $(seq $((width-1))) && printf "|") + mdivider=$(printf "${NC}|" && printf "%0.s=" $(seq $((width-1))) && printf "|") + m2divider=$(printf "${NC}|" && printf "%0.s-" $(seq $((width-1))) && printf "|") + m3divider=$(printf "${NC}|" && printf "%0.s- " $(seq $((width/2))) && printf "|") + bdivider=$(printf "${NC}|" && printf "%0.s=" $(seq $((width-1))) && printf "|") +else + VL=$(printf "${NC}\\u2502") + HL=$(printf "${NC}\\u2500") + LVL=$(printf "${NC}\\u2524") + RVL=$(printf "${NC}\\u251C") + UHL=$(printf "${NC}\\u2534") + DHL=$(printf "${NC}\\u252C") + UR=$(printf "${NC}\\u2514") + UL=$(printf "${NC}\\u2518") + DR=$(printf "${NC}\\u250C") + DL=$(printf "${NC}\\u2510") + tdivider=$(printf "${NC}\\u250C" && printf "%0.s\\u2500" $(seq $((width-1))) && printf "\\u2510") + mdivider=$(printf "${NC}\\u251C" && printf "%0.s\\u2500" $(seq $((width-1))) && printf "\\u2524") + m2divider=$(printf "${NC}\\u2502" && printf "%0.s-" $(seq $((width-1))) && printf "\\u2502") + m3divider=$(printf "${NC}\\u2502" && printf "%0.s- " $(seq $((width/2))) && printf "\\u2502") + bdivider=$(printf "${NC}\\u2514" && printf "%0.s\\u2500" $(seq $((width-1))) && printf "\\u2518") +fi + +# Title +title="Guild LiveView ${GLV_VERSION}" + +##################################### +# Helper functions # +##################################### + +# Command : waitForInput +# Description : wait for user keypress to quit, else do nothing if timeout expire +waitForInput() { + ESC=$(printf "\033") + if ! read -rsn1 -t ${REFRESH_RATE} key1; then return; fi + [[ ${key1} = "${ESC}" ]] && read -rsn2 -t 0.3 key2 # read 2 more chars + [[ ${key1} = "p" && ${show_peers} = "false" ]] && check_peers="true" && clear && return + [[ ${key1} = "h" && ${show_peers} = "true" ]] && show_peers="false" && clear && return + [[ ${key1} = "q" ]] && myExit 0 "Guild LiveView stopped!" + [[ ${key1} = "${ESC}" && ${key2} = "" ]] && myExit 0 "Guild LiveView stopped!" + sleep 1 +} + +# Command : showTimeLeft time_in_seconds +# Description: calculation of days, hours, minutes and seconds +timeLeft() { + local T=$1 + local D=$((T/60/60/24)) + local H=$((T/60/60%24)) + local M=$((T/60%60)) + local S=$((T%60)) + (( D > 0 )) && printf '%d day' $D && { + (( D > 1 )) && printf 's ' || printf ' ' + } + printf '%02d:%02d:%02d' $H $M $S +} + +# Command : getShelleyTransitionEpoch [1 = no user verification] +# Description: Calculate shelley transition epoch +getShelleyTransitionEpoch() { + calc_slot=0 + byron_epochs=${epochnum} + shelley_epochs=0 + while [[ ${byron_epochs} -ge 0 ]]; do + calc_slot=$(( (byron_epochs*byron_epoch_length) + (shelley_epochs*epoch_length) + slot_in_epoch )) + [[ ${calc_slot} -eq ${slotnum} ]] && break + ((byron_epochs--)) + ((shelley_epochs++)) + done + if [[ "${nwmagic}" = "764824073" ]]; then + shelley_transition_epoch=208 + elif [[ ${calc_slot} -ne ${slotnum} || ${shelley_epochs} -eq 0 ]]; then + if [[ $1 -ne 1 ]]; then + clear + printf "\n ${style_status_3}Failed${NC} to get shelley transition epoch, calculations will not work correctly!" + printf "\n\n Possible causes:" + printf "\n - Node in startup mode" + printf "\n - Shelley era not reached" + printf "\n After successful node boot or when sync to shelley era has been reached, calculations will be correct" + printf "\n\n ${style_info}Press c to continue or any other key to quit${NC}" + read -r -n 1 -s -p "" answer + [[ "${answer}" != "c" ]] && myExit 1 "Guild LiveView terminated!" + fi + shelley_transition_epoch=-1 + else + shelley_transition_epoch=${byron_epochs} + fi +} + +# Command : getEpoch +# Description: Offline calculation of current epoch based on genesis file +getEpoch() { + current_time_sec=$(date -u +%s) + if [[ "${PROTOCOL}" = "Cardano" ]]; then + [[ shelley_transition_epoch -eq -1 ]] && echo 0 && return + byron_end_time=$(( byron_genesis_start_sec + ( shelley_transition_epoch * byron_epoch_length * byron_slot_length ) )) + echo $(( shelley_transition_epoch + ( (current_time_sec - byron_end_time) / slot_length / epoch_length ) )) + else + echo $(( (current_time_sec - shelley_genesis_start_sec) / slot_length / epoch_length )) + fi +} + +# Command : getTimeUntilNextEpoch +# Description: Offline calculation of time in seconds until next epoch +timeUntilNextEpoch() { + current_time_sec=$(date -u +%s) + if [[ "${PROTOCOL}" = "Cardano" ]]; then + [[ shelley_transition_epoch -eq -1 ]] && echo 0 && return + echo $(( (shelley_transition_epoch * byron_slot_length * byron_epoch_length) + ( ( $(getEpoch) + 1 - shelley_transition_epoch ) * slot_length * epoch_length ) - current_time_sec + byron_genesis_start_sec )) + else + echo $(( ( ( ( (current_time_sec - shelley_genesis_start_sec) / slot_length / epoch_length ) + 1 ) * slot_length * epoch_length ) - current_time_sec + shelley_genesis_start_sec )) + fi +} + +# Command : getSlotTipRef +# Description: Get calculated slot number tip +getSlotTipRef() { + current_time_sec=$(date -u +%s) + if [[ "${PROTOCOL}" = "Cardano" ]]; then + [[ shelley_transition_epoch -eq -1 ]] && echo 0 && return + # Combinator network + byron_slots=$(( shelley_transition_epoch * byron_epoch_length )) # since this point will only be reached once we're in Shelley phase + byron_end_time=$(( byron_genesis_start_sec + ( shelley_transition_epoch * byron_epoch_length * byron_slot_length ) )) + if [[ "${current_time_sec}" -lt "${byron_end_time}" ]]; then + # In Byron phase + echo $(( ( current_time_sec - byron_genesis_start_sec ) / byron_slot_length )) + else + # In Shelley phase + echo $(( byron_slots + (( current_time_sec - byron_end_time ) / slot_length ) )) + fi + else + # Shelley Mode only, no Byron slots + echo $(( ( current_time_sec - shelley_genesis_start_sec ) / slot_length )) + fi +} + +# Command : kesExpiration [pools remaining KES periods] +# Description: Calculate KES expiration +kesExpiration() { + current_time_sec=$(date -u +%s) + tip_ref=$(getSlotTipRef) + expiration_time_sec=$(( current_time_sec - ( slot_length * (tip_ref % slots_per_kes_period) ) + ( slot_length * slots_per_kes_period * remaining_kes_periods ) )) + kes_expiration=$(date '+%F %T Z' --date=@${expiration_time_sec}) +} + +endLine() { + tput -S </dev/null | grep "${pid}," | awk -v port=":${CNODE_PORT}" '$3 !~ port {print $4}') + else + netstatPeers=$(ss -tnp state established 2>/dev/null | grep "${pid}," | awk -v port=":${CNODE_PORT}" '$3 ~ port {print $4}') + fi + [[ -z ${netstatPeers} ]] && return + + netstatSorted=$(printf '%s\n' "${netstatPeers[@]}" | sort ) + peerCNTABS=$(wc -w <<< "${netstatPeers}") + + # Sort/filter peers + lastpeerIP=""; lastpeerPORT="" + for peer in ${netstatSorted}; do + peerIP=$(echo "${peer}" | cut -d: -f1); peerPORT=$(echo "${peer}" | cut -d: -f2) + if [[ ! "${peerIP}" = "${lastpeerIP}" ]]; then + lastpeerIP=${peerIP} + lastpeerPORT=${peerPORT} + uniquePeers+=("${peerIP}:${peerPORT} ") + ((peerCNTUnique++)) + fi + done + netstatPeers=$(printf '%s\n' "${uniquePeers[@]}") + + # Ping every node in the list + for peer in ${netstatPeers}; do + peerIP=$(echo "${peer}" | cut -d: -f1) + peerPORT=$(echo "${peer}" | cut -d: -f2) + + if checkPEER=$(ping -c 2 -i 0.3 -w 1 "${peerIP}" 2>&1); then # Ping OK, show RTT + peerRTT=$(echo "${checkPEER}" | tail -n 1 | cut -d/ -f5 | cut -d. -f1) + ((peerCNT++)) + peerRTTSUM=$((peerRTTSUM + peerRTT)) + elif [[ ${direction} = "in" ]]; then # No need to continue with tcptraceroute for incoming connection as destination port is unknown + peerRTT=99999 + else # Normal ping is not working, try tcptraceroute to the given port + checkPEER=$(tcptraceroute -n -S -f 255 -m 255 -q 1 -w 1 "${peerIP}" "${peerPORT}" 2>&1 | tail -n 1) + if [[ ${checkPEER} = *'[open]'* ]]; then + peerRTT=$(echo "${checkPEER}" | awk '{print $4}' | cut -d. -f1) + ((peerCNT++)) + peerRTTSUM=$((peerRTTSUM + peerRTT)) + else # Nope, no response + peerRTT=99999 + fi + fi + + # Update counters + if [[ ${peerRTT} -lt 50 ]]; then ((peerCNT1++)) + elif [[ ${peerRTT} -lt 100 ]]; then ((peerCNT2++)) + elif [[ ${peerRTT} -lt 200 ]]; then ((peerCNT3++)) + elif [[ ${peerRTT} -lt 99999 ]]; then ((peerCNT4++)) + else ((peerCNT0++)); fi + rttResults+=("${peerRTT}:${peerIP}:${peerPORT} ") + done + rttResultsSorted=$(printf '%s\n' "${rttResults[@]}" | sort -n) + if [[ ${peerCNT} -gt 0 ]]; then + peerRTTAVG=$((peerRTTSUM / peerCNT)) + fi + peerCNTSKIPPED=$(( peerCNTABS - peerCNTUnique )) + + peerMAX=0 + if [[ ${peerCNT} -gt 0 ]]; then + peerPCT1=$(echo "scale=4;(${peerCNT1}/${peerCNT})*100" | bc -l) + peerPCT1items=$(printf %.0f "$(echo "scale=4;${peerPCT1}*${granularity_small}/100" | bc -l)") + peerPCT2=$(echo "scale=4;(${peerCNT2}/${peerCNT})*100" | bc -l) + peerPCT2items=$(printf %.0f "$(echo "scale=4;${peerPCT2}*${granularity_small}/100" | bc -l)") + peerPCT3=$(echo "scale=4;(${peerCNT3}/${peerCNT})*100" | bc -l) + peerPCT3items=$(printf %.0f "$(echo "scale=4;${peerPCT3}*${granularity_small}/100" | bc -l)") + peerPCT4=$(echo "scale=4;(${peerCNT4}/${peerCNT})*100" | bc -l) + peerPCT4items=$(printf %.0f "$(echo "scale=4;${peerPCT4}*${granularity_small}/100" | bc -l)") + fi +} + +##################################### +# Static variables/calculations # +##################################### +version=$("$(command -v cardano-node)" version) +node_version=$(grep "cardano-node" <<< "${version}" | cut -d ' ' -f2) +node_rev=$(grep "git rev" <<< "${version}" | cut -d ' ' -f3 | cut -c1-8) +pid=$(ps -ef | grep "[-]-port ${CNODE_PORT}" | awk '{print $2}') +[[ -z ${pid} ]] && myExit 1 "Failed to locate cardano-node process ID, make sure CNODE_PORT is correctly set in script!" +check_peers="false" +show_peers="false" +data=$(curl -s -H 'Accept: application/json' "http://${EKG_HOST}:${EKG_PORT}/" 2>/dev/null) +epochnum=$(jq '.cardano.node.ChainDB.metrics.epoch.int.val //0' <<< "${data}") +slot_in_epoch=$(jq '.cardano.node.ChainDB.metrics.slotInEpoch.int.val //0' <<< "${data}") +slotnum=$(jq '.cardano.node.ChainDB.metrics.slotNum.int.val //0' <<< "${data}") +remaining_kes_periods=$(jq '.cardano.node.Forge.metrics.remainingKESPeriods.int.val //0' <<< "${data}") + +##################################### +# Static genesis variables # +##################################### +shelley_genesis_file=$(jq -r .ShelleyGenesisFile "${CONFIG}") +[[ ! ${shelley_genesis_file} =~ ^/ ]] && shelley_genesis_file="$(dirname "${CONFIG}")/${shelley_genesis_file}" +byron_genesis_file=$(jq -r .ByronGenesisFile "${CONFIG}") +[[ ! ${byron_genesis_file} =~ ^/ ]] && byron_genesis_file="$(dirname "${CONFIG}")/${byron_genesis_file}" +nwmagic=$(jq -r .networkMagic < "${shelley_genesis_file}") +[[ "${nwmagic}" == "764824073" ]] && NWNAME="Mainnet" || { [[ "${nwmagic}" = "1097911063" ]] && NWNAME="Testnet" || NWNAME="Custom"; } +shelley_genesis_start=$(jq -r .systemStart "${shelley_genesis_file}") +shelley_genesis_start_sec=$(date --date="${shelley_genesis_start}" +%s) +epoch_length=$(jq -r .epochLength "${shelley_genesis_file}") +slot_length=$(jq -r .slotLength "${shelley_genesis_file}") +active_slots_coeff=$(jq -r .activeSlotsCoeff "${shelley_genesis_file}") +decentralisation=$(jq -r .protocolParams.decentralisationParam "${shelley_genesis_file}") +slots_per_kes_period=$(jq -r .slotsPerKESPeriod "${shelley_genesis_file}") +max_kes_evolutions=$(jq -r .maxKESEvolutions "${shelley_genesis_file}") +if [[ "${PROTOCOL}" = "Cardano" ]]; then + byron_genesis_start_sec=$(jq -r .startTime "${byron_genesis_file}") + byron_k=$(jq -r .protocolConsts.k "${byron_genesis_file}") + byron_slot_length=$(( $(jq -r .blockVersionData.slotDuration "${byron_genesis_file}") / 1000 )) + byron_epoch_length=$(( 10 * byron_k )) + getShelleyTransitionEpoch +else + shelley_transition_epoch=-2 +fi +##################################### +slot_interval=$(echo "(${slot_length} / ${active_slots_coeff} / ${decentralisation}) + 0.5" | bc -l | awk '{printf "%.0f\n", $1}') +kesExpiration +##################################### + +clear +tlines=$(tput lines) # set initial terminal lines +tcols=$(tput cols) # set initial terminal columns +printf "${NC}" # reset and set default color + +##################################### +# MAIN LOOP # +##################################### +while true; do + tlines=$(tput lines) # update terminal lines + tcols=$(tput cols) # update terminal columns + [[ ${width} -ge $((tcols)) || ${line} -ge $((tlines)) ]] && clear + while [[ ${width} -ge $((tcols)) ]]; do + tput cup 1 1 + printf "${style_status_3}Terminal width too small!${NC}" + tput cup 3 1 + printf "Please increase by ${style_info}$(( width - tcols + 1 ))${NC} columns" + tput cup 5 1 + printf "${style_info}[esc/q] Quit${NC}" + waitForInput + tlines=$(tput lines) # update terminal lines + tcols=$(tput cols) # update terminal columns + done + while [[ ${line} -ge $((tlines)) ]]; do + tput cup 1 1 + printf "${style_status_3}Terminal height too small!${NC}" + tput cup 3 1 + printf "Please increase by ${style_info}$(( line - tlines + 1 ))${NC} lines" + tput cup 5 1 + printf "${style_info}[esc/q] Quit${NC}" + waitForInput + tlines=$(tput lines) # update terminal lines + tcols=$(tput cols) # update terminal columns + done + + line=0; tput cup 0 0 # reset position + + if [[ ${show_peers} = "false" ]]; then + # Gather some data + data=$(curl -s -H 'Accept: application/json' "http://${EKG_HOST}:${EKG_PORT}/" 2>/dev/null) + uptimens=$(jq '.cardano.node.metrics.upTime.ns.val //0' <<< "${data}") + if ((uptimens<=0)); then + myExit 1 "${style_status_3}COULD NOT CONNECT TO A RUNNING INSTANCE!${NC}" + fi + peers_in=$(ss -tnp state established 2>/dev/null | grep "${pid}," | awk -v port=":${CNODE_PORT}" '$3 ~ port {print}' | wc -l) + peers_out=$(ss -tnp state established 2>/dev/null | grep "${pid}," | awk -v port=":${CNODE_PORT}" '$3 !~ port {print}' | wc -l) + blocknum=$(jq '.cardano.node.ChainDB.metrics.blockNum.int.val //0' <<< "${data}") + epochnum=$(jq '.cardano.node.ChainDB.metrics.epoch.int.val //0' <<< "${data}") + slot_in_epoch=$(jq '.cardano.node.ChainDB.metrics.slotInEpoch.int.val //0' <<< "${data}") + slotnum=$(jq '.cardano.node.ChainDB.metrics.slotNum.int.val //0' <<< "${data}") + density=$(jq -r '.cardano.node.ChainDB.metrics.density.real.val //0' <<< "${data}") + density=$(printf "%3.3e" "${density}"| cut -d 'e' -f1) + tx_processed=$(jq '.cardano.node.metrics.txsProcessedNum.int.val //0' <<< "${data}") + mempool_tx=$(jq '.cardano.node.metrics.txsInMempool.int.val //0' <<< "${data}") + mempool_bytes=$(jq '.cardano.node.metrics.mempoolBytes.int.val //0' <<< "${data}") + kesperiod=$(jq '.cardano.node.Forge.metrics.currentKESPeriod.int.val //0' <<< "${data}") + remaining_kes_periods=$(jq '.cardano.node.Forge.metrics.remainingKESPeriods.int.val //0' <<< "${data}") + isleader=$(jq '.cardano.node.metrics.Forge["node-is-leader"].int.val //0' <<< "${data}") + forged=$(jq '.cardano.node.metrics.Forge.forged.int.val //0' <<< "${data}") + adopted=$(jq '.cardano.node.metrics.Forge.adopted.int.val //0' <<< "${data}") + didntadopt=$(jq '.cardano.node.metrics.Forge["didnt-adopt"].int.val //0' <<< "${data}") + about_to_lead=$(jq '.cardano.node.metrics.Forge["forge-about-to-lead"].int.val //0' <<< "${data}") + [[ ${about_to_lead} -gt 0 ]] && nodemode="Core" || nodemode="Relay" + if [[ "${PROTOCOL}" = "Cardano" && ${shelley_transition_epoch} -eq -1 ]]; then # if Shelley transition epoch calc failed during start, try until successful + getShelleyTransitionEpoch 1 + kesExpiration + fi + fi + + header_length=$(( ${#NODE_NAME} + ${#nodemode} + ${#node_version} + ${#node_rev} + ${#NWNAME} + 20 )) + [[ ${header_length} -gt ${width} ]] && header_padding=0 || header_padding=$(( (width - header_length) / 2 )) + printf "%${header_padding}s > ${style_values_2}%s${NC} - ${style_info}(%s - %s)${NC} : ${style_values_1}%s${NC} [${style_values_1}%s${NC}] < \n" "" "${NODE_NAME}" "${nodemode}" "${NWNAME}" "${node_version}" "${node_rev}" + ((line++)) + + ## Base section ## + printf "${tdivider}" + if [[ ${show_peers} = "false" && ${check_peers} = "false" ]]; then + tput cup ${line} $(( width - ${#title} - 3 )) + printf "${DHL}" + fi + tput cup $((++line)) 0 + + if [[ ${check_peers} = "true" ]]; then + tput ed + printf "${VL} ${style_info}Output peer analysis started... please wait!${NC}" + endLine ${line} + echo "${bdivider}" + checkPeers out + # Save values + peerCNT0_out=${peerCNT0}; peerCNT1_out=${peerCNT1}; peerCNT2_out=${peerCNT2}; peerCNT3_out=${peerCNT3}; peerCNT4_out=${peerCNT4} + peerPCT1_out=${peerPCT1}; peerPCT2_out=${peerPCT2}; peerPCT3_out=${peerPCT3}; peerPCT4_out=${peerPCT4} + peerPCT1items_out=${peerPCT1items}; peerPCT2items_out=${peerPCT2items}; peerPCT3items_out=${peerPCT3items}; peerPCT4items_out=${peerPCT4items} + peerRTTAVG_out=${peerRTTAVG}; peerCNTUnique_out=${peerCNTUnique}; peerCNTSKIPPED_out=${peerCNTSKIPPED}; rttResultsSorted_out=${rttResultsSorted} + time_out=$(date -u '+%T Z') + tput cup ${line} 0 + tput ed + printf "${VL} ${style_info}Output peer analysis done!${NC}" + endLine $((line++)) + + echo "${m2divider}" + ((line++)) + + printf "${VL} ${style_info}Input peer analysis started... please wait!${NC}" + endLine $((line++)) + echo "${bdivider}" + ((line++)) + checkPeers in + # Save values + peerCNT0_in=${peerCNT0}; peerCNT1_in=${peerCNT1}; peerCNT2_in=${peerCNT2}; peerCNT3_in=${peerCNT3}; peerCNT4_in=${peerCNT4} + peerPCT1_in=${peerPCT1}; peerPCT2_in=${peerPCT2}; peerPCT3_in=${peerPCT3}; peerPCT4_in=${peerPCT4} + peerPCT1items_in=${peerPCT1items}; peerPCT2items_in=${peerPCT2items}; peerPCT3items_in=${peerPCT3items}; peerPCT4items_in=${peerPCT4items} + peerRTTAVG_in=${peerRTTAVG}; peerCNTUnique_in=${peerCNTUnique}; peerCNTSKIPPED_in=${peerCNTSKIPPED}; rttResultsSorted_in=${rttResultsSorted} + time_in=$(date -u '+%T Z') + elif [[ ${show_peers} = "true" ]]; then + printf "${VL}${STANDOUT} OUT ${NC} RTT : Peers / Percent" + tput cup ${line} $(( width - 20 )) + printf "Updated: ${style_info}%s${NC} ${VL}\n" "${time_out}" + ((line++)) + + printf "${VL} 0-50ms : ${style_values_1}%5s${NC} / ${style_values_1}%.f${NC}%% ${style_status_1}" "${peerCNT1_out}" "${peerPCT1_out}" + tput cup ${line} ${bar_col_small} + for i in $(seq 0 $((granularity_small-1))); do + [[ $i -lt ${peerPCT1items_out} ]] && printf "${char_marked}" || printf "${NC}${char_unmarked}" + done + printf "${NC}" + endLine $((line++)) + + printf "${VL} 50-100ms : ${style_values_1}%5s${NC} / ${style_values_1}%.f${NC}%% ${style_status_2}" "${peerCNT2_out}" "${peerPCT2_out}" + tput cup ${line} ${bar_col_small} + for i in $(seq 0 $((granularity_small-1))); do + [[ $i -lt ${peerPCT2items_out} ]] && printf "${char_marked}" || printf "${NC}${char_unmarked}" + done + printf "${NC}" + endLine $((line++)) + + printf "${VL} 100-200ms : ${style_values_1}%5s${NC} / ${style_values_1}%.f${NC}%% ${style_status_3}" "${peerCNT3_out}" "${peerPCT3_out}" + tput cup ${line} ${bar_col_small} + for i in $(seq 0 $((granularity_small-1))); do + [[ $i -lt ${peerPCT3items_out} ]] && printf "${char_marked}" || printf "${NC}${char_unmarked}" + done + printf "${NC}" + endLine $((line++)) + + printf "${VL} 200ms < : ${style_values_1}%5s${NC} / ${style_values_1}%.f${NC}%% ${style_status_4}" "${peerCNT4_out}" "${peerPCT4_out}" + tput cup ${line} ${bar_col_small} + for i in $(seq 0 $((granularity_small-1))); do + [[ $i -lt ${peerPCT4items_out} ]] && printf "${char_marked}" || printf "${NC}${char_unmarked}" + done + printf "${NC}" + endLine $((line++)) + if [[ ${peerRTTAVG_out} -ge 200 ]]; then printf "${VL} Average : ${style_status_4}%s${NC} ms" "${peerRTTAVG_out}" + elif [[ ${peerRTTAVG_out} -ge 100 ]]; then printf "${VL} Average : ${style_status_3}%s${NC} ms" "${peerRTTAVG_out}" + elif [[ ${peerRTTAVG_out} -ge 50 ]]; then printf "${VL} Average : ${style_status_2}%s${NC} ms" "${peerRTTAVG_out}" + elif [[ ${peerRTTAVG_out} -ge 0 ]]; then printf "${VL} Average : ${style_status_1}%s${NC} ms" "${peerRTTAVG_out}" + else printf "${VL} Average : - ms"; fi + endLine $((line++)) + + echo "${m3divider}" + ((line++)) + + printf "${VL} Unique Peers / Unreachable / Skipped : ${style_values_1}%s${NC} / " "${peerCNTUnique_out}" + [[ ${peerCNT0_out} -eq 0 ]] && printf "${style_values_1}%s${NC} / " "${peerCNT0_out}" || printf "${style_status_3}%s${NC} / " "${peerCNT0_out}" + [[ ${peerCNTSKIPPED_out} -eq 0 ]] && printf "${style_values_1}%s${NC}" "${peerCNTSKIPPED_out}" || printf "${style_status_2}%s${NC}" "${peerCNTSKIPPED_out}" + endLine $((line++)) + + echo "${m3divider}" + ((line++)) + + printf "${VL}${style_info} # : %20s : RTT (ms)${NC}" "REMOTE PEER" + tput cup ${line} $((width-6)) + printf "TOP 8 ${VL}\n" + ((line++)) + peerCNT=1 + for peer in ${rttResultsSorted_out}; do + peerRTT=$(echo ${peer} | cut -d: -f1) + peerIP=$(echo ${peer} | cut -d: -f2) + peerPORT=$(echo ${peer} | cut -d: -f3) + if [[ ${peerRTT} -lt 50 ]]; then printf "${VL} %3s : %15s:%-6s : ${style_status_1}%s${NC}" ${peerCNT} ${peerIP} ${peerPORT} ${peerRTT} + elif [[ ${peerRTT} -lt 100 ]]; then printf "${VL} %3s : %15s:%-6s : ${style_status_2}%s${NC}" ${peerCNT} ${peerIP} ${peerPORT} ${peerRTT} + elif [[ ${peerRTT} -lt 200 ]]; then printf "${VL} %3s : %15s:%-6s : ${style_status_3}%s${NC}" ${peerCNT} ${peerIP} ${peerPORT} ${peerRTT} + elif [[ ${peerRTT} -lt 99999 ]]; then printf "${VL} %3s : %15s:%-6s : ${style_status_4}%s${NC}" ${peerCNT} ${peerIP} ${peerPORT} ${peerRTT} + else printf "${VL} %3s : %15s:%-6s : ---" ${peerCNT} ${peerIP} ${peerPORT}; fi + endLine $((line++)) + [[ ${peerCNT} -eq 8 ]] && break + ((peerCNT++)) + done + + echo "${mdivider}" + ((line++)) + + printf "${VL}${STANDOUT} In ${NC} RTT : Peers / Percent" + tput cup ${line} $(( width - 20 )) + printf "Updated: ${style_info}%s${NC} ${VL}\n" "${time_in}" + ((line++)) + + printf "${VL} 0-50ms : ${style_values_1}%5s${NC} / ${style_values_1}%.f${NC}%% ${style_status_1}" "${peerCNT1_in}" "${peerPCT1_in}" + tput cup ${line} ${bar_col_small} + for i in $(seq 0 $((granularity_small-1))); do + [[ $i -lt ${peerPCT1items_in} ]] && printf "${char_marked}" || printf "${NC}${char_unmarked}" + done + printf "${NC}" + endLine $((line++)) + + printf "${VL} 50-100ms : ${style_values_1}%5s${NC} / ${style_values_1}%.f${NC}%% ${style_status_2}" "${peerCNT2_in}" "${peerPCT2_in}" + tput cup ${line} ${bar_col_small} + for i in $(seq 0 $((granularity_small-1))); do + [[ $i -lt ${peerPCT2items_in} ]] && printf "${char_marked}" || printf "${NC}${char_unmarked}" + done + printf "${NC}" + endLine $((line++)) + + printf "${VL} 100-200ms : ${style_values_1}%5s${NC} / ${style_values_1}%.f${NC}%% ${style_status_3}" "${peerCNT3_in}" "${peerPCT3_in}" + tput cup ${line} ${bar_col_small} + for i in $(seq 0 $((granularity_small-1))); do + [[ $i -lt ${peerPCT3items_in} ]] && printf "${char_marked}" || printf "${NC}${char_unmarked}" + done + printf "${NC}" + endLine $((line++)) + + printf "${VL} 200ms < : ${style_values_1}%5s${NC} / ${style_values_1}%.f${NC}%% ${style_status_4}" "${peerCNT4_in}" "${peerPCT4_in}" + tput cup ${line} ${bar_col_small} + for i in $(seq 0 $((granularity_small-1))); do + [[ $i -lt ${peerPCT4items_in} ]] && printf "${char_marked}" || printf "${NC}${char_unmarked}" + done + printf "${NC}" + endLine $((line++)) + if [[ ${peerRTTAVG_in} -ge 200 ]]; then printf "${VL} Average : ${style_status_4}%s${NC} ms" "${peerRTTAVG_in}" + elif [[ ${peerRTTAVG_in} -ge 100 ]]; then printf "${VL} Average : ${style_status_3}%s${NC} ms" "${peerRTTAVG_in}" + elif [[ ${peerRTTAVG_in} -ge 50 ]]; then printf "${VL} Average : ${style_status_2}%s${NC} ms" "${peerRTTAVG_in}" + elif [[ ${peerRTTAVG_in} -ge 0 ]]; then printf "${VL} Average : ${style_status_1}%s${NC} ms" "${peerRTTAVG_in}" + else printf "${VL} Average : - ms"; fi + endLine $((line++)) + + echo "${m3divider}" + ((line++)) + + printf "${VL} Unique Peers / Unreachable / Skipped : ${style_values_1}%s${NC} / " "${peerCNTUnique_in}" + [[ ${peerCNT0_in} -eq 0 ]] && printf "${style_values_1}%s${NC} / " "${peerCNT0_in}" || printf "${style_status_3}%s${NC} / " "${peerCNT0_in}" + [[ ${peerCNTSKIPPED_in} -eq 0 ]] && printf "${style_values_1}%s${NC}" "${peerCNTSKIPPED_in}" || printf "${style_status_2}%s${NC}" "${peerCNTSKIPPED_in}" + endLine $((line++)) + + echo "${m3divider}" + ((line++)) + + printf "${VL}${style_info} # : %20s : RTT (ms)${NC}" "REMOTE PEER" + tput cup ${line} $((width-6)) + printf "TOP 8 ${VL}\n" + ((line++)) + peerCNT=1 + for peer in ${rttResultsSorted_in}; do + peerRTT=$(echo ${peer} | cut -d: -f1) + peerIP=$(echo ${peer} | cut -d: -f2) + peerPORT=$(echo ${peer} | cut -d: -f3) + if [[ ${peerRTT} -lt 50 ]]; then printf "${VL} %3s : %15s:%-6s : ${style_status_1}%s${NC}" ${peerCNT} ${peerIP} ${peerPORT} ${peerRTT} + elif [[ ${peerRTT} -lt 100 ]]; then printf "${VL} %3s : %15s:%-6s : ${style_status_2}%s${NC}" ${peerCNT} ${peerIP} ${peerPORT} ${peerRTT} + elif [[ ${peerRTT} -lt 200 ]]; then printf "${VL} %3s : %15s:%-6s : ${style_status_3}%s${NC}" ${peerCNT} ${peerIP} ${peerPORT} ${peerRTT} + elif [[ ${peerRTT} -lt 99999 ]]; then printf "${VL} %3s : %15s:%-6s : ${style_status_4}%s${NC}" ${peerCNT} ${peerIP} ${peerPORT} ${peerRTT} + else printf "${VL} %3s : %15s:%-6s : ---" ${peerCNT} ${peerIP} ${peerPORT}; fi + endLine $((line++)) + [[ ${peerCNT} -eq 8 ]] && break + ((peerCNT++)) + done + else + printf "${VL} Uptime: ${style_values_1}%s${NC}" "$(timeLeft $(( uptimens/1000000000 )))" + tput cup ${line} $(( width - ${#title} - 3 )) + printf "${VL} ${style_title}${title} ${VL}\n" + ((line++)) + printf "${m2divider}" + tput cup ${line} $(( width - ${#title} - 3 )) + printf "${UR}" + printf "%0.s${HL}" $(seq $(( ${#title} + 2 ))) + printf "${LVL}\n" + ((line++)) + + if [[ ${shelley_transition_epoch} -eq -2 ]] || [[ ${shelley_transition_epoch} -ne -1 && ${epochnum} -ge ${shelley_transition_epoch} ]]; then + epoch_progress=$(echo "(${slot_in_epoch}/${epoch_length})*100" | bc -l) # in Shelley era or Shelley only TestNet + else + epoch_progress=$(echo "(${slot_in_epoch}/${byron_epoch_length})*100" | bc -l) # in Byron era + fi + printf "${VL} Epoch ${style_values_1}%s${NC} [${style_values_1}%2.1f%%${NC}] (node)" "${epochnum}" "${epoch_progress}" + endLine $((line++)) + printf "${VL} ${style_values_1}%s${NC} until epoch boundary (chain)" "$(timeLeft "$(timeUntilNextEpoch)")" + endLine $((line++)) + + epoch_items=$(( $(printf %.0f "${epoch_progress}") * granularity / 100 )) + printf "${VL} ${style_values_1}" + for i in $(seq 0 $((granularity-1))); do + [[ $i -lt ${epoch_items} ]] && printf "${char_marked}" || printf "${NC}${char_unmarked}" + done + printf "${NC} ${VL}\n"; ((line++)) + + printf "${VL}"; tput cup $((line++)) ${width}; printf "${VL}\n" # empty line + + tip_ref=$(getSlotTipRef) + tip_diff=$(( tip_ref - slotnum )) + printf "${VL} Block : ${style_values_1}%s${NC}" "${blocknum}" + tput cup ${line} ${second_col} + printf "Tip (ref) : ${style_values_1}%s${NC}" "${tip_ref}" + endLine $((line++)) + printf "${VL} Slot : ${style_values_1}%s${NC}" "${slot_in_epoch}" + tput cup ${line} ${second_col} + printf "Tip (node) : ${style_values_1}%s${NC}" "${slotnum}" + endLine $((line++)) + printf "${VL} Density : ${style_values_1}%s${NC}%%" "${density}" + tput cup ${line} ${second_col} + if [[ ${slotnum} -eq 0 ]]; then + printf "Status : ${style_info}starting...${NC}" + elif [[ "${PROTOCOL}" = "Cardano" && ${shelley_transition_epoch} -eq -1 ]]; then + printf "Status : ${style_info}syncing...${NC}" + elif [[ ${tip_diff} -le $(( slot_interval * 2 )) ]]; then + printf "Tip (diff) : ${style_status_1}%s${NC}" "${tip_diff} :)" + elif [[ ${tip_diff} -le $(( slot_interval * 3 )) ]]; then + printf "Tip (diff) : ${style_status_2}%s${NC}" "${tip_diff} :|" + else + printf "Tip (diff) : ${style_status_3}%s${NC}" "${tip_diff} :(" + fi + endLine $((line++)) + + echo "${m2divider}" + ((line++)) + + printf "${VL} Processed TX : ${style_values_1}%s${NC}" "${tx_processed}" + tput cup ${line} $((second_col)) + printf " Out / In" + endLine $((line++)) + printf "${VL} Mempool TX/Bytes : ${style_values_1}%s${NC} / ${style_values_1}%s${NC}" "${mempool_tx}" "${mempool_bytes}" + tput el; tput cup ${line} $((second_col)) + printf "Peers : ${style_values_1}%3s${NC} ${style_values_1}%s${NC}" "${peers_out}" "${peers_in}" + endLine $((line++)) + + ## Core section ## + if [[ ${nodemode} = "Core" ]]; then + echo "${mdivider}" + ((line++)) + + printf "${VL} KES current/remaining" + tput cup ${line} $((second_col-2)) + printf ": ${style_values_1}%s${NC} / " "${kesperiod}" + if [[ ${remaining_kes_periods} -le 0 ]]; then + printf "${style_status_4}%s${NC}" "${remaining_kes_periods}" + elif [[ ${remaining_kes_periods} -le 8 ]]; then + printf "${style_status_3}%s${NC}" "${remaining_kes_periods}" + else + printf "${style_values_1}%s${NC}" "${remaining_kes_periods}" + fi + endLine $((line++)) + printf "${VL} KES expiration date" + tput cup ${line} $((second_col-2)) + printf ": ${style_values_1}%s${NC}" "${kes_expiration}" + endLine $((line++)) + + echo "${m2divider}" + ((line++)) + + printf "${VL}" + tput cup ${line} ${second_col} + printf "IsLeader / Adopted / Missed" + endLine $((line++)) + printf "${VL} Blocks since node start" + tput cup ${line} $((second_col-2)) + printf ": ${style_values_1}%-11s${NC}" "${isleader}" + if [[ ${adopted} -ne ${isleader} ]]; then + printf "${style_status_2}%-10s${NC}" "${adopted}" + else + printf "${style_values_1}%-10s${NC}" "${adopted}" + fi + if [[ ${didntadopt} -gt 0 ]]; then + printf "${style_status_3}%-9s${NC}" "${didntadopt}" + else + printf "${style_values_1}%-9s${NC}" "${didntadopt}" + fi + endLine $((line++)) + + if [[ -n ${BLOCK_LOG_DIR} ]]; then + blocks_file="${BLOCK_LOG_DIR}/blocks_${epochnum}.json" + if [[ -f "${blocks_file}" ]]; then + isleader_epoch=$(jq -c '[.[].slot //empty] | length' "${blocks_file}") + invalid_epoch=$(jq -c '[.[].hash //empty | select(startswith("Invalid"))] | length' "${blocks_file}") + adopted_epoch=$(( $(jq -c '[.[].hash //empty] | length' "${blocks_file}") - invalid_epoch )) + else + isleader_epoch=0 + invalid_epoch=0 + adopted_epoch=0 + fi + printf "${VL} Blocks this epoch : ${style_values_1}%-11s${NC}" "${isleader_epoch}" + if [[ ${adopted_epoch} -ne ${isleader_epoch} ]]; then + printf "${style_status_2}%-10s${NC}" "${adopted_epoch}" + else + printf "${style_values_1}%-10s${NC}" "${adopted_epoch}" + fi + if [[ ${invalid_epoch} -gt 0 ]]; then + printf "${style_status_3}%-9s${NC}" "${invalid_epoch}" + else + printf "${style_values_1}%-9s${NC}" "${invalid_epoch}" + fi + endLine $((line++)) + fi + fi + fi + + [[ ${check_peers} = "true" ]] && check_peers=false && show_peers=true && clear && continue + + echo "${bdivider}" + ((line++)) + [[ ${show_peers} = "true" ]] && printf " ${style_info}[esc/q] Quit${NC} | ${style_info}[h] Home${NC}" || \ + printf " ${style_info}[esc/q] Quit${NC} | ${style_info}[p] Peer Analysis${NC}" + tput el + waitForInput +done diff --git a/scripts/cnode-helper-scripts/itnRewards.sh b/scripts/cnode-helper-scripts/itnRewards.sh index ab6c7c950..83fadaaa6 100755 --- a/scripts/cnode-helper-scripts/itnRewards.sh +++ b/scripts/cnode-helper-scripts/itnRewards.sh @@ -23,12 +23,12 @@ wallet_name="$1" itn_signing_key_file="$2" itn_verification_key_file="$3" -if ! [[ $(cat "${itn_signing_key_file}") == ed25519e_sk* ]] || ! [[ $(cat "${itn_signing_key_file}") != ed25519_sk* ]]; then - echo -e "\n${RED}ERROR${NC}: Invalid ITN Signing Key provided" +if [[ ! -f "${itn_signing_key_file}" || ! $(cat "${itn_signing_key_file}") =~ ^ed25519e?_sk* ]]; then + echo -e "\n${RED}ERROR${NC}: Invalid ITN Signing Key provided\n" exit 1 fi -if [[ $(cat "${itn_verification_key_file}") != ed25519_pk* ]]; then +if [[ ! -f "${itn_verification_key_file}" || $(cat "${itn_verification_key_file}") != ed25519_pk* ]]; then echo -e "\n${RED}ERROR${NC}: Invalid ITN Verification Key provided\n" exit 1 fi diff --git a/scripts/cnode-helper-scripts/prereqs.sh b/scripts/cnode-helper-scripts/prereqs.sh index 0175d77b9..87a079ac4 100755 --- a/scripts/cnode-helper-scripts/prereqs.sh +++ b/scripts/cnode-helper-scripts/prereqs.sh @@ -1,5 +1,8 @@ #!/bin/bash # shellcheck disable=SC2086 + +unset CNODE_HOME + get_input() { printf "%s (default: %s): " "$1" "$2" >&2; read -r answer if [ -z "$answer" ]; then echo "$2"; else echo "$answer"; fi @@ -30,11 +33,12 @@ usage() { Usage: $(basename "$0") [-o] [-s] [-i] [-g] [-p] Install pre-requisites for building cardano node and using cntools --o Do *NOT* overwrite existing cnode.sh, genesis.json, topology.json and topology-updater.sh files (Default: will overwrite) +-o Do *NOT* overwrite existing genesis.json, topology.json and topology-updater.sh files (Default: will overwrite) -s Skip installing OS level dependencies (Default: will check and install any missing OS level prerequisites) -i Interactive mode (Default: silent mode) -g Connect to guild network instead of public network (Default: connect to public cardano network) -p Copy Transitional Praos config as default instead of Combinator networks (Default: copies combinator network) +-t Alternate name for top level folder EOF exit 1 } @@ -42,7 +46,7 @@ EOF WANT_BUILD_DEPS='Y' OVERWRITE=' ' -while getopts :igpso opt; do +while getopts :igpsot: opt; do case ${opt} in i ) INTERACTIVE='Y' @@ -59,6 +63,9 @@ while getopts :igpso opt; do o ) OVERWRITE=' -C -' ;; + t ) + CNODE_NAME=${OPTARG} + ;; \? ) usage ;; @@ -72,7 +79,7 @@ G_ID=$(id -g) # Defaults CNODE_PATH="/opt/cardano" -CNODE_NAME="cnode" +[[ -z "${CNODE_NAME}" ]] && CNODE_NAME="cnode" CNODE_HOME=${CNODE_PATH}/${CNODE_NAME} CNODE_VNAME=$(echo "$CNODE_NAME" | awk '{print toupper($0)}') @@ -105,37 +112,34 @@ if [ "$WANT_BUILD_DEPS" = 'Y' ]; then OS_ID=$(grep -i ^id_like= /etc/os-release | cut -d= -f 2) DISTRO=$(grep -i ^NAME= /etc/os-release | cut -d= -f 2) - if [ -z "${OS_ID##*debian*}" ]; then +if [[ "${OS_ID}" =~ ebian ]] || [[ "${DISTRO}" =~ ebian ]]; then #Debian/Ubuntu echo "Using apt to prepare packages for ${DISTRO} system" echo " Updating system packages..." $sudo apt-get -y install curl > /dev/null - curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | $sudo apt-key add - > /dev/null - echo "deb https://dl.yarnpkg.com/debian/ stable main" | $sudo tee /etc/apt/sources.list.d/yarn.list > /dev/null $sudo apt-get -y update > /dev/null echo " Installing missing prerequisite packages, if any.." - $sudo apt-get -y install libpq-dev python3 build-essential pkg-config libffi-dev libgmp-dev libssl-dev libtinfo-dev systemd libsystemd-dev libsodium-dev zlib1g-dev yarn make g++ tmux git jq wget libncursesw5 gnupg aptitude libtool autoconf secure-delete > /dev/null;rc=$? + pkg_list="libpq-dev python3 build-essential pkg-config libffi-dev libgmp-dev libssl-dev libtinfo-dev systemd libsystemd-dev libsodium-dev zlib1g-dev make g++ tmux git jq wget libncursesw5 gnupg aptitude libtool autoconf secure-delete iproute2 bc tcptraceroute" + $sudo apt-get -y install ${pkg_list} > /dev/null;rc=$? if [ $rc != 0 ]; then echo "An error occurred while installing the prerequisite packages, please investigate by using the command below:" - echo "sudo apt-get -y install python3 build-essential pkg-config libffi-dev libgmp-dev libssl-dev libtinfo-dev systemd libsystemd-dev libsodium-dev zlib1g-dev npm yarn make g++ tmux git jq wget libncursesw5 gnupg libtool autoconf bsdmainutils" + echo "sudo apt-get -y install ${pkg_list}" echo "It would be best if you could submit an issue at https://github.com/cardano-community/guild-operators with the details to tackle in future, as some errors may be due to external/already present dependencies" exit; - else - $sudo aptitude install npm -yq > /dev/null fi - elif [ -z "${OS_ID##*rhel*}" ]; then + elif [[ "${OS_ID}" =~ rhel ]] || [[ "${DISTRO}" =~ Fedora ]]; then #CentOS/RHEL/Fedora echo "Using yum to prepare packages for ${DISTRO} system" echo " Updating system packages..." $sudo yum -y install curl > /dev/null - curl --silent --location https://dl.yarnpkg.com/rpm/yarn.repo | $sudo tee /etc/yum.repos.d/yarn.repo > /dev/null - $sudo rpm --import https://dl.yarnpkg.com/rpm/pubkey.gpg > /dev/null $sudo yum -y update > /dev/null echo " Installing missing prerequisite packages, if any.." - $sudo yum -y install python3 coreutils pkgconfig libffi-devel gmp-devel openssl-devel ncurses-libs ncurses-compat-libs systemd systemd-devel libsodium-devel zlib-devel npm yarn make gcc-c++ tmux git wget epel-release jq gnupg libtool autoconf srm > /dev/null;rc=$? + pkg_list="python3 coreutils pkgconfig libffi-devel gmp-devel openssl-devel ncurses-libs ncurses-compat-libs systemd systemd-devel libsodium-devel zlib-devel make gcc-c++ tmux git wget jq gnupg libtool autoconf srm iproute bc tcptraceroute" + [[ ! "${DISTRO}" =~ Fedora ]] && sudo yum -y install epel-release > /dev/null + $sudo yum -y install ${pkg_list} > /dev/null;rc=$? if [ $rc != 0 ]; then echo "An error occurred while installing the prerequisite packages, please investigate by using the command below:" - echo "sudo yum -y install coreutils python3 pkgconfig libffi-devel gmp-devel openssl-devel ncurses-libs ncurses-compat-libs systemd systemd-devel libsodium-devel zlib-devel npm yarn make gcc-c++ tmux git wget epel-release jq gnupg libtool autoconf util-linux" + echo "sudo yum -y install ${pkg_list}" echo "It would be best if you could submit an issue at https://github.com/cardano-community/guild-operators with the details to tackle in future, as some errors may be due to external/already present dependencies" exit; fi @@ -223,6 +227,7 @@ if [[ "$GUILD" = "Y" ]]; then else curl -sL -o byron-genesis.json ${OVERWRITE} https://hydra.iohk.io/job/Cardano/iohk-nix/cardano-deployment/latest-finished/download/1/mainnet-byron-genesis.json curl -sL -o genesis.json ${OVERWRITE} https://hydra.iohk.io/job/Cardano/iohk-nix/cardano-deployment/latest-finished/download/1/mainnet-shelley-genesis.json + [[ -f topology.json ]] && cp topology.json "topology.json_bkp$(date +%s)" curl -sL -o topology.json ${OVERWRITE} https://hydra.iohk.io/job/Cardano/iohk-nix/cardano-deployment/latest-finished/download/1/mainnet-topology.json fi @@ -232,32 +237,45 @@ else cp ptn0-mainnet.json config.json fi -# If using a different CNODE_HOME than in this example, execute the below: -# sed -i -e "s#/opt/cardano/cnode#${CNODE_HOME}#" $CNODE_HOME/files/*.json -## For future use: -## It generates random NodeID: -## -e "s#NodeId:.*#NodeId:$(od -A n -t u8 -N 8 /dev/urandom$(#" \ +sed -i -e "s#/opt/cardano/cnode#${CNODE_HOME}#" $CNODE_HOME/files/*.json cd "$CNODE_HOME"/scripts || return curl -s -o env https://raw.githubusercontent.com/cardano-community/guild-operators/master/scripts/cnode-helper-scripts/env -sed -e "s@CNODE_HOME@${CNODE_VNAME}_HOME@g" -i env +sed -e "s@CNODE_HOME=.*@${CNODE_VNAME}_HOME=${CNODE_HOME}@g" -e "s@CNODE_HOME@${CNODE_VNAME}_HOME@g" -i env curl -s -o createAddr.sh https://raw.githubusercontent.com/cardano-community/guild-operators/master/scripts/cnode-helper-scripts/createAddr.sh curl -s -o sendADA.sh https://raw.githubusercontent.com/cardano-community/guild-operators/master/scripts/cnode-helper-scripts/sendADA.sh curl -s -o balance.sh https://raw.githubusercontent.com/cardano-community/guild-operators/master/scripts/cnode-helper-scripts/balance.sh curl -s -o rotatePoolKeys.sh https://raw.githubusercontent.com/cardano-community/guild-operators/master/scripts/cnode-helper-scripts/rotatePoolKeys.sh -curl -s -o cnode.sh ${OVERWRITE} https://raw.githubusercontent.com/cardano-community/guild-operators/master/scripts/cnode-helper-scripts/cnode.sh.templ +curl -s -o cnode.sh.templ https://raw.githubusercontent.com/cardano-community/guild-operators/master/scripts/cnode-helper-scripts/cnode.sh.templ curl -s -o cntools.sh https://raw.githubusercontent.com/cardano-community/guild-operators/master/scripts/cnode-helper-scripts/cntools.sh curl -s -o cntools.config https://raw.githubusercontent.com/cardano-community/guild-operators/master/scripts/cnode-helper-scripts/cntools.config curl -s -o cntools.library https://raw.githubusercontent.com/cardano-community/guild-operators/master/scripts/cnode-helper-scripts/cntools.library curl -s -o cntoolsBlockCollector.sh https://raw.githubusercontent.com/cardano-community/guild-operators/master/scripts/cnode-helper-scripts/cntoolsBlockCollector.sh curl -s -o setup_mon.sh https://raw.githubusercontent.com/cardano-community/guild-operators/master/scripts/cnode-helper-scripts/setup_mon.sh +[[ -f topologyUpdater.sh ]] && cp topologyUpdater.sh "topologyUpdater.sh_bkp$(date +%s)" curl -s -o topologyUpdater.sh ${OVERWRITE} https://raw.githubusercontent.com/cardano-community/guild-operators/master/scripts/cnode-helper-scripts/topologyUpdater.sh curl -s -o itnRewards.sh https://raw.githubusercontent.com/cardano-community/guild-operators/master/scripts/cnode-helper-scripts/itnRewards.sh -sed -e "s@CNODE_HOME=.*@${CNODE_VNAME}_HOME=${CNODE_HOME}@g" -e "s@CNODE_HOME@${CNODE_VNAME}_HOME@g" -i cnode.sh curl -s -o cabal-build-all.sh https://raw.githubusercontent.com/cardano-community/guild-operators/master/scripts/cnode-helper-scripts/cabal-build-all.sh curl -s -o stack-build.sh https://raw.githubusercontent.com/cardano-community/guild-operators/master/scripts/cnode-helper-scripts/stack-build.sh curl -s -o system-info.sh https://raw.githubusercontent.com/cardano-community/guild-operators/master/scripts/cnode-helper-scripts/system-info.sh +curl -s -o sLiveView.sh https://raw.githubusercontent.com/cardano-community/guild-operators/master/scripts/cnode-helper-scripts/sLiveView.sh +curl -s -o gLiveView.sh https://raw.githubusercontent.com/cardano-community/guild-operators/master/scripts/cnode-helper-scripts/gLiveView.sh +curl -s -o deploy-as-systemd.sh https://raw.githubusercontent.com/cardano-community/guild-operators/master/scripts/cnode-helper-scripts/deploy-as-systemd.sh +sed -e "s@SyslogIdentifier=.*@SyslogIdentifier=${CNODE_NAME}@g" -e "s@cnode.service@${CNODE_NAME}.service@g" -i deploy-as-systemd.sh +sed -e "s@CNODE_HOME=.*@${CNODE_VNAME}_HOME=${CNODE_HOME}@g" -e "s@CNODE_HOME@${CNODE_VNAME}_HOME@g" -i ./*.* + +### Update cnode.sh retaining existing custom configs +if grep '## Static' cnode.sh >/dev/null 2>&1; then + TEMPL_CMD=$(awk '/#!/,/## Static/' cnode.sh.templ) + STATIC_CMD=$(awk '/## Begin/,/## End/' cnode.sh) + printf '%s\n%s\n' "$TEMPL_CMD" "$STATIC_CMD" > cnode.sh +elif grep 'cardano-node' cnode.sh >/dev/null 2>&1;then + cp cnode.sh "cnode.sh.bkp_$(date +%s)" + cp -f cnode.sh.templ cnode.sh + echo "One-time upgrade! Please edit ${CNODE_HOME}/scripts/cnode.sh for values against POOL_NAME and CNODE_PORT variables" +else + cp -f cnode.sh.templ cnode.sh +fi chmod 755 ./*.sh -# If you opt for an alternate CNODE_HOME, please run the below: -# sed -i -e "s#/opt/cardano/cnode#${CNODE_HOME}#" *.sh + cd - || return diff --git a/scripts/cnode-helper-scripts/sLiveView.sh b/scripts/cnode-helper-scripts/sLiveView.sh new file mode 100755 index 000000000..383369372 --- /dev/null +++ b/scripts/cnode-helper-scripts/sLiveView.sh @@ -0,0 +1,159 @@ +#!/bin/bash +#shellcheck disable=SC2009,SC2034 + +# Credits to original Author of the script : Adam Dean (from BUFFY | SPIKE pool) by Crypto2099 + +##################################### +# Change variables below as desired # +##################################### + +# The commands below will try to detect the information assuming you run single node on a machine. Please override values if they dont match your system + +cardanoport=$(ps -ef | grep "[c]ardano-node.*.port" | awk -F 'port ' '{print $2}' | awk '{print $1}') # example value: 6000 +nodename="RTFM Lazy Guy" # Change your node's name prefix here, 24 character limit!!! +refreshrate=2 # How often (in seconds) to refresh the view +config=$(ps -ef | grep "[c]ardano-node.*.config" | awk -F 'config ' '{print $2}' | awk '{print $1}') # example: /opt/cardano/cnode/files/config.json +ekghost=127.0.0.1 +if [[ -f "${config}" ]]; then + ekgport=$(jq -r '.hasEKG //empty' "${config}" 2>/dev/null) +else + ekgport=12788 +fi + + +##################################### +# Do NOT Modify below # +##################################### + + +version=$("$(command -v cardano-node)" version) +node_version=$(grep -oP '(?<=cardano-node )[0-9\.]+' <<< "${version}") +node_rev=$(grep -oP '(?<=rev )[a-z0-9]+' <<< "${version}" | cut -c1-8) + +node_version=$(printf "%14s" "${node_version}") +node_rev=$(printf "%14s" "${node_rev}") + +# Add some colors +REKT='\033[1;31m' +GOOD='\033[0;32m' +NC='\033[0m' +INFO='\033[1;34m' +while true +do + data=$(curl -s -H 'Accept: application/json' http://${ekghost}:${ekgport}/ 2>/dev/null) + remotepeers=$(netstat -an|awk "\$4 ~ /${cardanoport}/"|grep -c ESTABLISHED) + peers=$(jq '.cardano.node.BlockFetchDecision.peers.connectedPeers.int.val //0' <<< "${data}") + blocknum=$(jq '.cardano.node.ChainDB.metrics.blockNum.int.val //0' <<< "${data}") + epochnum=$(jq '.cardano.node.ChainDB.metrics.epoch.int.val //0' <<< "${data}") + slotnum=$(jq '.cardano.node.ChainDB.metrics.slotInEpoch.int.val //0' <<< "${data}") + density=$(jq -r '.cardano.node.ChainDB.metrics.density.real.val //0' <<< "${data}") + uptimens=$(jq '.cardano.node.metrics.upTime.ns.val //0' <<< "${data}") + transactions=$(jq '.cardano.node.metrics.txsProcessedNum.int.val //0' <<< "${data}") + kesperiod=$(jq '.cardano.node.Forge.metrics.currentKESPeriod.int.val //0' <<< "${data}") + kesremain=$(jq '.cardano.node.Forge.metrics.remainingKESPeriods.int.val //0' <<< "${data}") + isleader=$(jq '.cardano.node.metrics.Forge["node-is-leader"].int.val //0' <<< "${data}") + abouttolead=$(jq '.cardano.node.metrics.Forge["forge-about-to-lead"].int.val //0' <<< "${data}") + forged=$(jq '.cardano.node.metrics.Forge.forged.int.val //0' <<< "${data}") + adopted=$(jq '.cardano.node.metrics.Forge.adopted.int.val //0' <<< "${data}") + + if [[ ${abouttolead} -gt 0 ]]; then + name=$(printf "%s - Core\n" "${nodename}") + else + name=$(printf "%s - Relay\n" "${nodename}") + fi + + if ((uptimens<=0)); then + echo -e "${REKT}COULD NOT CONNECT TO A RUNNING INSTANCE! PLEASE CHECK THE PROMETHEUS PORT AND TRY AGAIN!${NC}" + exit + fi + +# remotepeers=$(printf "%14s" "${remotepeers}") + peers=$(printf "%14s" "${peers} / ${remotepeers}") + epoch=$(printf "%14s" "${epochnum} / ${slotnum}") + block=$(printf "%14s" "${blocknum}") + txcount=$(printf "%14s" "${transactions}") + density=$(printf "%12.4s %%" "${density}"*100) + + if [[ isleader -lt 0 ]]; then + isleader=0 + forged=0 + fi + + uptimes=$(( uptimens / 1000000000)) + min=0 + hour=0 + day=0 + if((uptimes > 59)); then + ((sec=uptimes%60)) + ((uptimes=uptimes/60)) + if((uptimes > 59)); then + ((min=uptimes%60)) + ((uptimes=uptimes/60)) + if((uptimes > 23)); then + ((hour=uptimes%24)) + ((day=uptimes/24)) + else + ((hour=uptimes)) + fi + else + ((min=uptimes)) + fi + else + ((sec=uptimes)) + fi + + day=$(printf "%02d\n" "${day}") + hour=$(printf "%02d\n" "${hour}") + min=$(printf "%02d\n" "${min}") + sec=$(printf "%02d\n" "${sec}") + + uptime="${day}":"${hour}":"${min}":"${sec}" + uptime=$(printf "%14s" "${uptime}") + + clear + echo -e '+----------------------------------------+' + echo -e '| Simple Node Stats |' + echo -e '+-----------------------+----------------+' + if [[ -n "${nodename}" ]]; then + name=$(printf "%32s" "${name}") + echo -e "| Name: ${INFO}${name}${NC} |" + echo -e '+-----------------------+----------------+' + fi + echo -e "| Version | ${INFO}${node_version}${NC} |" + echo -e '+-----------------------+----------------+' + echo -e "| Revision | ${INFO}${node_rev}${NC} |" + echo -e '+-----------------------+----------------+' + echo -e "| Peers (Out / In) | ${peers} |" + echo -e "+-----------------------+----------------+" + echo -e "| Epoch / Slot | ${epoch} |" + echo -e '+-----------------------+----------------+' + echo -e "| Block | ${block} |" + echo -e '+-----------------------+----------------+' + echo -e "| Density | ${density} |" + echo -e '+-----------------------+----------------+' + echo -e "| Uptime (D:H:M:S) | ${uptime} |" + echo -e '+-----------------------+----------------+' + echo -e "| Transactions | ${txcount} |" + echo -e '+-----------------------+----------------+' + if [[ ${abouttolead} -gt 0 ]]; then + kesperiod=$(printf "%14s" "${kesperiod}") + kesremain=$(printf "%14s" "${kesremain}") + isleader=$(printf "%14s" "${isleader}") + forged=$(printf "%14s" "${forged}/${adopted}") + echo -e "| KES PERIOD | ${kesperiod} |" + echo -e "+-----------------------+----------------+" + echo -e "| KES REMAINING | ${kesremain} |" + echo -e "+-----------------------+----------------+" + echo -e "| SLOTS LED | ${isleader} |" + echo -e "+-----------------------+----------------+" + echo -e "| BLOCKS FORGED/ADOPTED | ${forged} |" + echo -e "+-----------------------+----------------+" + else + echo -e "+----------------------------------------+" + fi + + + echo -e "\n${INFO}Press [CTRL+C] to stop...${NC}" + sleep ${refreshrate} +done +