From f73c2af662d38ebfb160226c49fa0409d4626c58 Mon Sep 17 00:00:00 2001 From: enchanted-elephant1 Date: Fri, 23 Aug 2024 14:13:40 -0400 Subject: [PATCH 01/56] feat: Enhance dump download with aria2 for improved speed and reliability - Switched from curl to aria2c for downloading the dump using 8 parallel connections. - Updated apt install command to include aria2. - Fixed typo in log message from "fuction" to "function". --- mytoninstaller/settings.py | 44 +++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/mytoninstaller/settings.py b/mytoninstaller/settings.py index d04c5673..cc7c64dd 100644 --- a/mytoninstaller/settings.py +++ b/mytoninstaller/settings.py @@ -86,28 +86,28 @@ def FirstNodeSettings(local): def DownloadDump(local): - dump = local.buffer.dump - if dump == False: - return - #end if - - local.add_log("start DownloadDump fuction", "debug") - url = "https://dump.ton.org" - dumpSize = requests.get(url + "/dumps/latest.tar.size.archive.txt").text - print("dumpSize:", dumpSize) - needSpace = int(dumpSize) * 3 - diskSpace = psutil.disk_usage("/var") - if needSpace > diskSpace.free: - return - #end if - - # apt install - cmd = "apt install plzip pv curl -y" - os.system(cmd) - - # download dump - cmd = "curl -s {url}/dumps/latest.tar.lz | pv | plzip -d -n8 | tar -xC /var/ton-work/db".format(url=url) - os.system(cmd) + dump = local.buffer.dump + if dump == False: + return + #end if + + local.add_log("start DownloadDump function", "debug") + url = "https://dump.ton.org" + dumpSize = requests.get(url + "/dumps/latest.tar.size.archive.txt").text + print("dumpSize:", dumpSize) + needSpace = int(dumpSize) * 3 + diskSpace = psutil.disk_usage("/var") + if needSpace > diskSpace.free: + return + #end if + + # apt install + cmd = "apt install plzip pv aria2 curl -y" + os.system(cmd) + + # download dump using aria2c with 8 connections + cmd = "aria2c -x 8 -s 8 -c {url}/dumps/latest.tar.lz -o - | pv | plzip -d -n8 | tar -xC /var/ton-work/db".format(url=url) + os.system(cmd) #end define From 00fbb95ecbc6e8c813117320fac8533c32eb5bfa Mon Sep 17 00:00:00 2001 From: enchanted-elephant1 Date: Fri, 23 Aug 2024 14:18:35 -0400 Subject: [PATCH 02/56] tmp-file-first. --- mytoninstaller/settings.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/mytoninstaller/settings.py b/mytoninstaller/settings.py index cc7c64dd..84260853 100644 --- a/mytoninstaller/settings.py +++ b/mytoninstaller/settings.py @@ -105,8 +105,13 @@ def DownloadDump(local): cmd = "apt install plzip pv aria2 curl -y" os.system(cmd) - # download dump using aria2c with 8 connections - cmd = "aria2c -x 8 -s 8 -c {url}/dumps/latest.tar.lz -o - | pv | plzip -d -n8 | tar -xC /var/ton-work/db".format(url=url) + # download dump using aria2c to a temporary file + temp_file = "/tmp/latest.tar.lz" + cmd = "aria2c -x 8 -s 8 -c {url}/dumps/latest.tar.lz -o {temp_file}".format(url=url, temp_file=temp_file) + os.system(cmd) + + # process the downloaded file + cmd = "pv {temp_file} | plzip -d -n8 | tar -xC /var/ton-work/db" os.system(cmd) #end define From 56c5236c7912663d24da28d5282ee26fcd73f50a Mon Sep 17 00:00:00 2001 From: enchanted-elephant1 Date: Fri, 23 Aug 2024 14:21:31 -0400 Subject: [PATCH 03/56] cleanup --- mytoninstaller/settings.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/mytoninstaller/settings.py b/mytoninstaller/settings.py index 84260853..96b470ba 100644 --- a/mytoninstaller/settings.py +++ b/mytoninstaller/settings.py @@ -84,7 +84,6 @@ def FirstNodeSettings(local): StartValidator(local) #end define - def DownloadDump(local): dump = local.buffer.dump if dump == False: @@ -113,8 +112,13 @@ def DownloadDump(local): # process the downloaded file cmd = "pv {temp_file} | plzip -d -n8 | tar -xC /var/ton-work/db" os.system(cmd) -#end define + # clean up the temporary file after processing + if os.path.exists(temp_file): + os.remove(temp_file) + local.add_log("Temporary file {temp_file} removed".format(temp_file=temp_file), "debug") + #end if +#end define def FirstMytoncoreSettings(local): local.add_log("start FirstMytoncoreSettings fuction", "debug") From 02a36df163f633a0c9350bc87c13b5b8d80acf63 Mon Sep 17 00:00:00 2001 From: yungwine Date: Thu, 12 Sep 2024 12:17:40 +0900 Subject: [PATCH 04/56] add backups support for mtc --- mytonctrl/mytonctrl.py | 46 +++++++++++++++++++++++++++++ mytonctrl/scripts/create_backup.sh | 44 +++++++++++++++++++++++++++ mytonctrl/scripts/restore_backup.sh | 42 ++++++++++++++++++++++++++ 3 files changed, 132 insertions(+) create mode 100644 mytonctrl/scripts/create_backup.sh create mode 100644 mytonctrl/scripts/restore_backup.sh diff --git a/mytonctrl/mytonctrl.py b/mytonctrl/mytonctrl.py index ba606750..c6eb43c6 100755 --- a/mytonctrl/mytonctrl.py +++ b/mytonctrl/mytonctrl.py @@ -80,6 +80,7 @@ def inject_globals(func): console.AddItem("get", inject_globals(GetSettings), local.translate("get_cmd")) console.AddItem("set", inject_globals(SetSettings), local.translate("set_cmd")) console.AddItem("rollback", inject_globals(rollback_to_mtc1), local.translate("rollback_cmd")) + console.AddItem("create_backup", inject_globals(create_backup), local.translate("create_backup_cmd")) #console.AddItem("xrestart", inject_globals(Xrestart), local.translate("xrestart_cmd")) #console.AddItem("xlist", inject_globals(Xlist), local.translate("xlist_cmd")) @@ -914,6 +915,51 @@ def disable_mode(local, ton, args): local.exit() #end define + +def create_backup(local, ton, args): + if len(args) > 2: + color_print("{red}Bad args. Usage:{endc} create_backup [path_to_archive] [-y]") + return + if '-y' not in args: + res = input(f'Node and Mytoncore services will be stopped for few seconds while backup is created, Proceed [y/n]?') + if res.lower() != 'y': + print('aborted.') + return + else: + args.pop(args.index('-y')) + command_args = ["-m", ton.local.buffer.my_work_dir] + if len(args) == 1: + command_args += ["-d", args[0]] + backup_script_path = pkg_resources.resource_filename('mytonctrl', 'scripts/backup.sh') + if run_as_root(["bash", backup_script_path] + command_args) == 0: + color_print("create_backup - {green}OK{endc}") + else: + color_print("create_backup - {red}Error{endc}") + + +def restore_backup(local, ton, args): + if len(args) == 0 or len(args) > 2: + color_print("{red}Bad args. Usage:{endc} restore_backup [-y]") + return + if '-y' not in args: + res = input(f'This action will overwrite existing configuration with contents of backup archive, please make sure that donor node is not in operation prior to this action. Proceed [y/n]') + if res.lower() != 'y': + print('aborted.') + return + else: + args.pop(args.index('-y')) + print('Before proceeding, mtc will create a backup of current configuration.') + create_backup(local, ton, ['-y']) + command_args = ["-m", ton.local.buffer.my_work_dir] + command_args += ["-n", args[0]] + restore_script_path = pkg_resources.resource_filename('mytonctrl', 'scripts/restore_backup.sh') + if run_as_root(["bash", restore_script_path]) == 0: + color_print("restore_backup - {green}OK{endc}") + local.exit() + else: + color_print("restore_backup - {red}Error{endc}") + + def Xrestart(inputArgs): if len(inputArgs) < 2: color_print("{red}Bad args. Usage:{endc} xrestart ") diff --git a/mytonctrl/scripts/create_backup.sh b/mytonctrl/scripts/create_backup.sh new file mode 100644 index 00000000..9738025f --- /dev/null +++ b/mytonctrl/scripts/create_backup.sh @@ -0,0 +1,44 @@ +dest="backup_$(hostname)_$(date +%s).tar.gz" +mtc_dir="$HOME/.local/share/mytoncore" +# Get arguments +while getopts d:m: flag +do + case "${flag}" in + d) dest=${OPTARG};; + m) mtc_dir=${OPTARG};; + *) + echo "Flag -${flag} is not recognized. Aborting" + exit 1 ;; + esac +done + +COLOR='\033[92m' +ENDC='\033[0m' + +systemctl stop validator +systemctl stop mytoncore + +echo -e "${COLOR}[1/4]${ENDC} Stopped validator and mytoncore" + + +tmp_dir="/tmp/mytoncore/backup" +rm -rf $tmp_dir +mkdir $tmp_dir + +cp /var/ton-work/db/config.json ${tmp_dir} +cp -r /var/ton-work/db/keyring ${tmp_dir} +cp -r /var/ton-work/keys ${tmp_dir} +cp -r $mtc_dir $tmp_dir + +echo -e "${COLOR}[2/4]${ENDC} Copied files to ${tmp_dir}" + + +systemctl start validator +systemctl start mytoncore + +echo -e "${COLOR}[3/4]${ENDC} Started validator and mytoncore" + +sudo tar -zcvf ${dest} -C ${tmp_dir} . + +echo -e "${COLOR}[4/4]${ENDC} Backup successfully created in ${dest}!" +echo -e "If you wish to use archive package to migrate node to different machine please make sure to stop validator and mytoncore on donor (this) host prior to migration." diff --git a/mytonctrl/scripts/restore_backup.sh b/mytonctrl/scripts/restore_backup.sh new file mode 100644 index 00000000..79d2a4c7 --- /dev/null +++ b/mytonctrl/scripts/restore_backup.sh @@ -0,0 +1,42 @@ +name="backup.tar.gz" +mtc_dir="$HOME/.local/share/mytoncore" +# Get arguments +while getopts n:m: flag +do + case "${flag}" in + n) name=${OPTARG};; + m) mtc_dir=${OPTARG};; + *) + echo "Flag -${flag} is not recognized. Aborting" + exit 1 ;; + esac +done + + +COLOR='\033[92m' +ENDC='\033[0m' + +systemctl stop validator +systemctl stop mytoncore + +echo -e "${COLOR}[1/3]${ENDC} Stopped validator and mytoncore" + + +tmp_dir="/tmp/mytoncore/backup" +rm -rf $tmp_dir +mkdir $tmp_dir +tar -xvzf $name -C $tmp_dir + +cp -f ${tmp_dir}/config.json /var/ton-work/db/ +cp -rf ${tmp_dir}/keyring /var/ton-work/db/ +cp -rf ${tmp_dir}/keys /var/ton-work +cp -rfT ${tmp_dir}/mytoncore $mtc_dir + +echo -e "${COLOR}[2/3]${ENDC} Extracted files from archive" + +rm /var/ton-work/db/dht-* + +systemctl start validator +systemctl start mytoncore + +echo -e "${COLOR}[3/3]${ENDC} Started validator and mytoncore" From 1412440a18af1c4a607b53481a914cf3c3680643 Mon Sep 17 00:00:00 2001 From: yungwine Date: Thu, 12 Sep 2024 12:24:59 +0900 Subject: [PATCH 05/56] fix restore_backup --- mytonctrl/mytonctrl.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/mytonctrl/mytonctrl.py b/mytonctrl/mytonctrl.py index c6eb43c6..239d8eb5 100755 --- a/mytonctrl/mytonctrl.py +++ b/mytonctrl/mytonctrl.py @@ -81,6 +81,7 @@ def inject_globals(func): console.AddItem("set", inject_globals(SetSettings), local.translate("set_cmd")) console.AddItem("rollback", inject_globals(rollback_to_mtc1), local.translate("rollback_cmd")) console.AddItem("create_backup", inject_globals(create_backup), local.translate("create_backup_cmd")) + console.AddItem("restore_backup", inject_globals(restore_backup), local.translate("restore_backup_cmd")) #console.AddItem("xrestart", inject_globals(Xrestart), local.translate("xrestart_cmd")) #console.AddItem("xlist", inject_globals(Xlist), local.translate("xlist_cmd")) @@ -921,8 +922,12 @@ def create_backup(local, ton, args): color_print("{red}Bad args. Usage:{endc} create_backup [path_to_archive] [-y]") return if '-y' not in args: - res = input(f'Node and Mytoncore services will be stopped for few seconds while backup is created, Proceed [y/n]?') - if res.lower() != 'y': + try: + res = input(f'Node and Mytoncore services will be stopped for few seconds while backup is created, Proceed [y/n]?') + if res.lower() != 'y': + print('aborted.') + return + except KeyboardInterrupt: print('aborted.') return else: @@ -935,6 +940,7 @@ def create_backup(local, ton, args): color_print("create_backup - {green}OK{endc}") else: color_print("create_backup - {red}Error{endc}") +#end define def restore_backup(local, ton, args): @@ -942,8 +948,12 @@ def restore_backup(local, ton, args): color_print("{red}Bad args. Usage:{endc} restore_backup [-y]") return if '-y' not in args: - res = input(f'This action will overwrite existing configuration with contents of backup archive, please make sure that donor node is not in operation prior to this action. Proceed [y/n]') - if res.lower() != 'y': + try: + res = input(f'This action will overwrite existing configuration with contents of backup archive, please make sure that donor node is not in operation prior to this action. Proceed [y/n]') + if res.lower() != 'y': + print('aborted.') + return + except KeyboardInterrupt: print('aborted.') return else: @@ -958,6 +968,7 @@ def restore_backup(local, ton, args): local.exit() else: color_print("restore_backup - {red}Error{endc}") +#end define def Xrestart(inputArgs): From 0987a2169d989e21f1a806c43cca884392fac6e7 Mon Sep 17 00:00:00 2001 From: yungwine Date: Thu, 12 Sep 2024 12:27:05 +0900 Subject: [PATCH 06/56] fix backups --- mytonctrl/mytonctrl.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/mytonctrl/mytonctrl.py b/mytonctrl/mytonctrl.py index 239d8eb5..12d8f9e6 100755 --- a/mytonctrl/mytonctrl.py +++ b/mytonctrl/mytonctrl.py @@ -922,12 +922,8 @@ def create_backup(local, ton, args): color_print("{red}Bad args. Usage:{endc} create_backup [path_to_archive] [-y]") return if '-y' not in args: - try: - res = input(f'Node and Mytoncore services will be stopped for few seconds while backup is created, Proceed [y/n]?') - if res.lower() != 'y': - print('aborted.') - return - except KeyboardInterrupt: + res = input(f'Node and Mytoncore services will be stopped for few seconds while backup is created, Proceed [y/n]?') + if res.lower() != 'y': print('aborted.') return else: @@ -948,12 +944,8 @@ def restore_backup(local, ton, args): color_print("{red}Bad args. Usage:{endc} restore_backup [-y]") return if '-y' not in args: - try: - res = input(f'This action will overwrite existing configuration with contents of backup archive, please make sure that donor node is not in operation prior to this action. Proceed [y/n]') - if res.lower() != 'y': - print('aborted.') - return - except KeyboardInterrupt: + res = input(f'This action will overwrite existing configuration with contents of backup archive, please make sure that donor node is not in operation prior to this action. Proceed [y/n]') + if res.lower() != 'y': print('aborted.') return else: From 43e5b3e70abf1fabafef847376bb47d894646ccd Mon Sep 17 00:00:00 2001 From: yungwine Date: Thu, 12 Sep 2024 12:27:44 +0900 Subject: [PATCH 07/56] fix backups --- mytonctrl/mytonctrl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mytonctrl/mytonctrl.py b/mytonctrl/mytonctrl.py index 12d8f9e6..1ff10e7a 100755 --- a/mytonctrl/mytonctrl.py +++ b/mytonctrl/mytonctrl.py @@ -931,7 +931,7 @@ def create_backup(local, ton, args): command_args = ["-m", ton.local.buffer.my_work_dir] if len(args) == 1: command_args += ["-d", args[0]] - backup_script_path = pkg_resources.resource_filename('mytonctrl', 'scripts/backup.sh') + backup_script_path = pkg_resources.resource_filename('mytonctrl', 'scripts/create_backup.sh') if run_as_root(["bash", backup_script_path] + command_args) == 0: color_print("create_backup - {green}OK{endc}") else: From 2c28d4acf137b37e645bbda51812f448fbf52ded Mon Sep 17 00:00:00 2001 From: yungwine Date: Thu, 12 Sep 2024 12:29:29 +0900 Subject: [PATCH 08/56] rm verbosity from tar --- mytonctrl/scripts/create_backup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mytonctrl/scripts/create_backup.sh b/mytonctrl/scripts/create_backup.sh index 9738025f..52ac04e4 100644 --- a/mytonctrl/scripts/create_backup.sh +++ b/mytonctrl/scripts/create_backup.sh @@ -38,7 +38,7 @@ systemctl start mytoncore echo -e "${COLOR}[3/4]${ENDC} Started validator and mytoncore" -sudo tar -zcvf ${dest} -C ${tmp_dir} . +sudo tar -zcf ${dest} -C ${tmp_dir} . echo -e "${COLOR}[4/4]${ENDC} Backup successfully created in ${dest}!" echo -e "If you wish to use archive package to migrate node to different machine please make sure to stop validator and mytoncore on donor (this) host prior to migration." From bd9567deea165b55c0960abb3177e9a1d48da398 Mon Sep 17 00:00:00 2001 From: yungwine Date: Thu, 12 Sep 2024 12:36:32 +0900 Subject: [PATCH 09/56] fix backups --- mytonctrl/mytonctrl.py | 2 +- mytonctrl/scripts/create_backup.sh | 2 +- mytonctrl/scripts/restore_backup.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mytonctrl/mytonctrl.py b/mytonctrl/mytonctrl.py index 1ff10e7a..c3064ad9 100755 --- a/mytonctrl/mytonctrl.py +++ b/mytonctrl/mytonctrl.py @@ -955,7 +955,7 @@ def restore_backup(local, ton, args): command_args = ["-m", ton.local.buffer.my_work_dir] command_args += ["-n", args[0]] restore_script_path = pkg_resources.resource_filename('mytonctrl', 'scripts/restore_backup.sh') - if run_as_root(["bash", restore_script_path]) == 0: + if run_as_root(["bash", restore_script_path] + command_args) == 0: color_print("restore_backup - {green}OK{endc}") local.exit() else: diff --git a/mytonctrl/scripts/create_backup.sh b/mytonctrl/scripts/create_backup.sh index 52ac04e4..6e6e51ff 100644 --- a/mytonctrl/scripts/create_backup.sh +++ b/mytonctrl/scripts/create_backup.sh @@ -1,4 +1,4 @@ -dest="backup_$(hostname)_$(date +%s).tar.gz" +dest="mytonctrl_backup_$(hostname)_$(date +%s).tar.gz" mtc_dir="$HOME/.local/share/mytoncore" # Get arguments while getopts d:m: flag diff --git a/mytonctrl/scripts/restore_backup.sh b/mytonctrl/scripts/restore_backup.sh index 79d2a4c7..b44b68c3 100644 --- a/mytonctrl/scripts/restore_backup.sh +++ b/mytonctrl/scripts/restore_backup.sh @@ -34,7 +34,7 @@ cp -rfT ${tmp_dir}/mytoncore $mtc_dir echo -e "${COLOR}[2/3]${ENDC} Extracted files from archive" -rm /var/ton-work/db/dht-* +rm -r /var/ton-work/db/dht-* systemctl start validator systemctl start mytoncore From c5264a8bc06df2d814edf6a28f2518ac214017ac Mon Sep 17 00:00:00 2001 From: Leon <10451228+leonlarin@users.noreply.github.com> Date: Fri, 13 Sep 2024 10:27:46 +0100 Subject: [PATCH 10/56] feat: Update install.sh to allow passing in USER arg - Added an optional `-u` argument to install.sh to allow explicitly specifying the user thats passed to `mytoninstaller` and is used for version migration. - Cleaned up formatting --- scripts/install.sh | 58 +++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/scripts/install.sh b/scripts/install.sh index fe5ac3d6..dffe1134 100644 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -20,19 +20,20 @@ ton_node_version="master" # Default version show_help_and_exit() { - echo 'Supported arguments:' - echo ' -c PATH Provide custom config for toninstaller.sh' - echo ' -t Disable telemetry' - echo ' -i Ignore minimum requirements' - echo ' -d Use pre-packaged dump. Reduces duration of initial synchronization.' - echo ' -a Set MyTonCtrl git repo author' - echo ' -r Set MyTonCtrl git repo' - echo ' -b Set MyTonCtrl git repo branch' - echo ' -m MODE Install MyTonCtrl with specified mode (validator or liteserver)' - echo ' -n NETWORK Specify the network (mainnet or testnet)' - echo ' -v VERSION Specify the ton node version (commit, branch, or tag)' - echo ' -h Show this help' - exit + echo 'Supported arguments:' + echo ' -c PATH Provide custom config for toninstaller.sh' + echo ' -t Disable telemetry' + echo ' -i Ignore minimum requirements' + echo ' -d Use pre-packaged dump. Reduces duration of initial synchronization.' + echo ' -a Set MyTonCtrl git repo author' + echo ' -r Set MyTonCtrl git repo' + echo ' -b Set MyTonCtrl git repo branch' + echo ' -m MODE Install MyTonCtrl with specified mode (validator or liteserver)' + echo ' -n NETWORK Specify the network (mainnet or testnet)' + echo ' -v VERSION Specify the ton node version (commit, branch, or tag)' + echo ' -u USER Specify the user to be used for MyTonCtrl installation' + echo ' -h Show this help' + exit } if [[ "${1-}" =~ ^-*h(elp)?$ ]]; then @@ -47,7 +48,7 @@ dump=false cpu_required=16 mem_required=64000000 # 64GB in KB -while getopts ":c:tida:r:b:m:n:v:h" flag; do +while getopts ":c:tida:r:b:m:n:v:u:h" flag; do case "${flag}" in c) config=${OPTARG};; t) telemetry=false;; @@ -59,10 +60,11 @@ while getopts ":c:tida:r:b:m:n:v:h" flag; do m) mode=${OPTARG};; n) network=${OPTARG};; v) ton_node_version=${OPTARG};; + u) user=${OPTARG};; h) show_help_and_exit;; *) echo "Flag -${flag} is not recognized. Aborting" - exit 1 ;; + exit 1 ;; esac done @@ -90,8 +92,8 @@ memory=$(cat /proc/meminfo | grep MemTotal | awk '{print $2}') echo "This machine has ${cpus} CPUs and ${memory}KB of Memory" if [ "$ignore" = false ] && ([ "${cpus}" -lt "${cpu_required}" ] || [ "${memory}" -lt "${mem_required}" ]); then - echo "Insufficient resources. Requires a minimum of "${cpu_required}" processors and "${mem_required}" RAM." - exit 1 + echo "Insufficient resources. Requires a minimum of "${cpu_required}" processors and "${mem_required}" RAM." + exit 1 fi echo -e "${COLOR}[2/5]${ENDC} Checking for required TON components" @@ -100,9 +102,9 @@ BIN_DIR=/usr/bin # create dirs for OSX if [[ "$OSTYPE" =~ darwin.* ]]; then - SOURCES_DIR=/usr/local/src - BIN_DIR=/usr/local/bin - mkdir -p ${SOURCES_DIR} + SOURCES_DIR=/usr/local/src + BIN_DIR=/usr/local/bin + mkdir -p ${SOURCES_DIR} fi # check TON components @@ -111,9 +113,9 @@ file2=${BIN_DIR}/ton/lite-client/lite-client file3=${BIN_DIR}/ton/validator-engine-console/validator-engine-console if [ ! -f "${file1}" ] || [ ! -f "${file2}" ] || [ ! -f "${file3}" ]; then - echo "TON does not exists, building" - wget https://raw.githubusercontent.com/${author}/${repo}/${branch}/scripts/ton_installer.sh -O /tmp/ton_installer.sh - bash /tmp/ton_installer.sh -c ${config} -v ${ton_node_version} + echo "TON does not exists, building" + wget https://raw.githubusercontent.com/${author}/${repo}/${branch}/scripts/ton_installer.sh -O /tmp/ton_installer.sh + bash /tmp/ton_installer.sh -c ${config} -v ${ton_node_version} fi # Cloning mytonctrl @@ -134,10 +136,12 @@ pip3 install -U . # TODO: make installation from git directly echo -e "${COLOR}[4/5]${ENDC} Running mytoninstaller" # DEBUG -parent_name=$(ps -p $PPID -o comm=) -user=$(whoami) -if [ "$parent_name" = "sudo" ] || [ "$parent_name" = "su" ] || [ "$parent_name" = "python3" ]; then - user=$(logname) +if [ "${user}" = "" ]; then # no user + parent_name=$(ps -p $PPID -o comm=) + user=$(whoami) + if [ "$parent_name" = "sudo" ] || [ "$parent_name" = "su" ] || [ "$parent_name" = "python3" ]; then + user=$(logname) + fi fi echo "User: $user" python3 -m mytoninstaller -u ${user} -t ${telemetry} --dump ${dump} -m ${mode} From afabd526ea75f8b4e1fd394b8fb2608b727be89e Mon Sep 17 00:00:00 2001 From: yungwine Date: Tue, 17 Sep 2024 14:25:57 +0400 Subject: [PATCH 11/56] chown keyring in restore_backup --- mytonctrl/scripts/restore_backup.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mytonctrl/scripts/restore_backup.sh b/mytonctrl/scripts/restore_backup.sh index b44b68c3..60e8bf96 100644 --- a/mytonctrl/scripts/restore_backup.sh +++ b/mytonctrl/scripts/restore_backup.sh @@ -32,6 +32,8 @@ cp -rf ${tmp_dir}/keyring /var/ton-work/db/ cp -rf ${tmp_dir}/keys /var/ton-work cp -rfT ${tmp_dir}/mytoncore $mtc_dir +chown -R validator:validator /var/ton-work/db/keyring + echo -e "${COLOR}[2/3]${ENDC} Extracted files from archive" rm -r /var/ton-work/db/dht-* From 858eba27ca0020eec4d6800cd97cc13e4ddb4fc7 Mon Sep 17 00:00:00 2001 From: yungwine Date: Tue, 17 Sep 2024 22:16:23 +0400 Subject: [PATCH 12/56] replace ip in node config in restore_backup --- mytonctrl/mytonctrl.py | 9 ++++++--- mytonctrl/scripts/restore_backup.sh | 14 ++++++++++---- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/mytonctrl/mytonctrl.py b/mytonctrl/mytonctrl.py index c3064ad9..1fa59ec7 100755 --- a/mytonctrl/mytonctrl.py +++ b/mytonctrl/mytonctrl.py @@ -29,7 +29,7 @@ color_text, bcolors, Dict, - MyPyClass + MyPyClass, ip2int ) from mypyconsole.mypyconsole import MyPyConsole @@ -46,6 +46,8 @@ import sys, getopt, os +from mytoninstaller.config import get_own_ip + def Init(local, ton, console, argv): # Load translate table @@ -952,8 +954,9 @@ def restore_backup(local, ton, args): args.pop(args.index('-y')) print('Before proceeding, mtc will create a backup of current configuration.') create_backup(local, ton, ['-y']) - command_args = ["-m", ton.local.buffer.my_work_dir] - command_args += ["-n", args[0]] + ip = str(ip2int(get_own_ip())) + command_args = ["-m", ton.local.buffer.my_work_dir, "-n", args[0], "-i", ip] + restore_script_path = pkg_resources.resource_filename('mytonctrl', 'scripts/restore_backup.sh') if run_as_root(["bash", restore_script_path] + command_args) == 0: color_print("restore_backup - {green}OK{endc}") diff --git a/mytonctrl/scripts/restore_backup.sh b/mytonctrl/scripts/restore_backup.sh index 60e8bf96..fccac825 100644 --- a/mytonctrl/scripts/restore_backup.sh +++ b/mytonctrl/scripts/restore_backup.sh @@ -1,11 +1,13 @@ name="backup.tar.gz" mtc_dir="$HOME/.local/share/mytoncore" +ip=0 # Get arguments -while getopts n:m: flag +while getopts n:m:i: flag do case "${flag}" in n) name=${OPTARG};; m) mtc_dir=${OPTARG};; + i) ip=${OPTARG};; *) echo "Flag -${flag} is not recognized. Aborting" exit 1 ;; @@ -19,7 +21,7 @@ ENDC='\033[0m' systemctl stop validator systemctl stop mytoncore -echo -e "${COLOR}[1/3]${ENDC} Stopped validator and mytoncore" +echo -e "${COLOR}[1/4]${ENDC} Stopped validator and mytoncore" tmp_dir="/tmp/mytoncore/backup" @@ -34,11 +36,15 @@ cp -rfT ${tmp_dir}/mytoncore $mtc_dir chown -R validator:validator /var/ton-work/db/keyring -echo -e "${COLOR}[2/3]${ENDC} Extracted files from archive" +echo -e "${COLOR}[2/4]${ENDC} Extracted files from archive" rm -r /var/ton-work/db/dht-* +python3 -c "import json;path='/var/ton-work/db/config.json';f=open(path);d=json.load(f);f.close();d['addrs'][0]['ip']=int($ip);f=open(path, 'w');f.write(json.dumps(d, indent=4));f.close()" + +echo -e "${COLOR}[3/4]${ENDC} Deleted DHT files, replaced IP in node config" + systemctl start validator systemctl start mytoncore -echo -e "${COLOR}[3/3]${ENDC} Started validator and mytoncore" +echo -e "${COLOR}[4/4]${ENDC} Started validator and mytoncore" From caa91e07b8e86b97626276525a7a4106acf11adf Mon Sep 17 00:00:00 2001 From: yungwine Date: Wed, 18 Sep 2024 16:57:08 +0400 Subject: [PATCH 13/56] refactor checking local adnl con --- mytoncore/mytoncore.py | 18 ------------------ mytonctrl/mytonctrl.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/mytoncore/mytoncore.py b/mytoncore/mytoncore.py index fe2ffb37..9f65af15 100644 --- a/mytoncore/mytoncore.py +++ b/mytoncore/mytoncore.py @@ -2600,24 +2600,6 @@ def GetDbSize(self, exceptions="log"): return result #end define - def check_adnl(self): - telemetry = self.local.db.get("sendTelemetry", False) - check_adnl = self.local.db.get("checkAdnl", telemetry) - if not check_adnl: - return - url = 'http://45.129.96.53/adnl_check' - try: - data = self.get_local_adnl_data() - response = requests.post(url, json=data, timeout=5).json() - except Exception as e: - self.local.add_log(f'Failed to check adnl connection: {type(e)}: {e}', 'error') - return False - result = response.get("ok") - if not result: - self.local.add_log(f'Failed to check adnl connection to local node: {response.get("message")}', 'error') - return result - #end define - def get_local_adnl_data(self): def int2ip(dec): diff --git a/mytonctrl/mytonctrl.py b/mytonctrl/mytonctrl.py index ba606750..b58b3185 100755 --- a/mytonctrl/mytonctrl.py +++ b/mytonctrl/mytonctrl.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf_8 -*- import base64 +import random import subprocess import json import psutil @@ -10,6 +11,8 @@ from functools import partial +import requests + from mypylib.mypylib import ( int2ip, get_git_author_and_repo, @@ -484,6 +487,36 @@ def check_slashed(local, ton): print_warning(local, "slashed_warning") #end define +def check_adnl(local, ton): + telemetry = ton.local.db.get("sendTelemetry", False) + check_adnl = ton.local.db.get("checkAdnl", telemetry) + local.add_log('Checking ADNL connection to local node', 'info') + if not check_adnl: + return + hosts = ['45.129.96.53', '5.154.181.153', '2.56.126.137', '91.194.11.68', '45.12.134.214', '138.124.184.27', '103.106.3.171'] + hosts = random.choices(hosts, k=3) + data = ton.get_local_adnl_data() + error = '' + ok = True + for host in hosts: + url = f'http://{host}/adnl_check' + try: + response = requests.post(url, json=data, timeout=5).json() + except Exception as e: + ok = False + error = f'Failed to check ADNL connection to local node: {type(e)}: {e}' + continue + result = response.get("ok") + if result: + ok = True + break + if not result: + ok = False + error = f'Failed to check ADNL connection to local node: {response.get("message")}' + if not ok: + print_warning(local, error) +#end define + def warnings(local, ton): local.try_function(check_disk_usage, args=[local, ton]) local.try_function(check_sync, args=[local, ton]) From f7a5fe72493f2a4123c05705d4fb6ab0aa045b9c Mon Sep 17 00:00:00 2001 From: yungwine Date: Wed, 18 Sep 2024 17:08:22 +0400 Subject: [PATCH 14/56] fix adnl checking * add color warning * fix check adnl * fix random host choosing --- mytonctrl/mytonctrl.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mytonctrl/mytonctrl.py b/mytonctrl/mytonctrl.py index b58b3185..a4202c8c 100755 --- a/mytonctrl/mytonctrl.py +++ b/mytonctrl/mytonctrl.py @@ -221,7 +221,6 @@ def PreUp(local: MyPyClass, ton: MyTonCore): CheckMytonctrlUpdate(local) check_installer_user(local) check_vport(local, ton) - ton.check_adnl() warnings(local, ton) # CheckTonUpdate() #end define @@ -494,7 +493,7 @@ def check_adnl(local, ton): if not check_adnl: return hosts = ['45.129.96.53', '5.154.181.153', '2.56.126.137', '91.194.11.68', '45.12.134.214', '138.124.184.27', '103.106.3.171'] - hosts = random.choices(hosts, k=3) + hosts = random.sample(hosts, k=3) data = ton.get_local_adnl_data() error = '' ok = True @@ -504,7 +503,7 @@ def check_adnl(local, ton): response = requests.post(url, json=data, timeout=5).json() except Exception as e: ok = False - error = f'Failed to check ADNL connection to local node: {type(e)}: {e}' + error = f'{{red}}Failed to check ADNL connection to local node: {type(e)}: {e}{{endc}}' continue result = response.get("ok") if result: @@ -512,7 +511,7 @@ def check_adnl(local, ton): break if not result: ok = False - error = f'Failed to check ADNL connection to local node: {response.get("message")}' + error = f'{{red}}Failed to check ADNL connection to local node: {response.get("message")}{{endc}}' if not ok: print_warning(local, error) #end define @@ -520,6 +519,7 @@ def check_adnl(local, ton): def warnings(local, ton): local.try_function(check_disk_usage, args=[local, ton]) local.try_function(check_sync, args=[local, ton]) + local.try_function(check_adnl, args=[local, ton]) local.try_function(check_validator_balance, args=[local, ton]) local.try_function(check_vps, args=[local, ton]) local.try_function(check_tg_channel, args=[local, ton]) From 0f4e5caaf924b1060498f66bb5880049b7668e61 Mon Sep 17 00:00:00 2001 From: yungwine Date: Wed, 18 Sep 2024 17:02:43 +0400 Subject: [PATCH 15/56] show only masterchain efficiency for masterchain validator --- modules/validator.py | 8 ++++---- mytoncore/mytoncore.py | 7 ++++++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/modules/validator.py b/modules/validator.py index 7a6b9ad3..0315b99c 100644 --- a/modules/validator.py +++ b/modules/validator.py @@ -61,8 +61,8 @@ def check_efficiency(self, args): else: efficiency = 100 if validator.efficiency > 100 else validator.efficiency color_efficiency = GetColorInt(efficiency, 90, logic="more", ending="%") - created = validator.blocks_created - expected = validator.blocks_expected + created = validator.master_blocks_created + expected = validator.master_blocks_expected color_print(f"Previous round efficiency: {color_efficiency} {{yellow}}({created} blocks created / {round(expected, 1)} blocks expected){{endc}}") else: print("Couldn't find this validator in the previous round") @@ -81,8 +81,8 @@ def check_efficiency(self, args): else: efficiency = 100 if validator.efficiency > 100 else validator.efficiency color_efficiency = GetColorInt(efficiency, 90, logic="more", ending="%") - created = validator.blocks_created - expected = validator.blocks_expected + created = validator.master_blocks_created + expected = validator.master_blocks_expected color_print(f"Current round efficiency: {color_efficiency} {{yellow}}({created} blocks created / {round(expected, 1)} blocks expected){{endc}}") else: print("Couldn't find this validator in the current round") diff --git a/mytoncore/mytoncore.py b/mytoncore/mytoncore.py index fe2ffb37..3f29c094 100644 --- a/mytoncore/mytoncore.py +++ b/mytoncore/mytoncore.py @@ -2394,7 +2394,10 @@ def GetValidatorsLoad(self, start, end, saveCompFiles=False) -> dict: wr = 0 else: wr = workBlocksCreated / workBlocksExpected - r = (mr + wr) / 2 + if masterBlocksExpected > 0: # show only masterchain efficiency for masterchain validator + r = mr + else: + r = (mr + wr) / 2 efficiency = round(r * 100, 2) if efficiency > 10: online = True @@ -2467,6 +2470,8 @@ def GetValidatorsList(self, past=False, fast=False): validator["wr"] = validatorsLoad[vid]["wr"] validator["efficiency"] = validatorsLoad[vid]["efficiency"] validator["online"] = validatorsLoad[vid]["online"] + validator["master_blocks_created"] = validatorsLoad[vid]["masterBlocksCreated"] + validator["master_blocks_expected"] = validatorsLoad[vid]["masterBlocksExpected"] validator["blocks_created"] = validatorsLoad[vid]["masterBlocksCreated"] + validatorsLoad[vid]["workBlocksCreated"] validator["blocks_expected"] = validatorsLoad[vid]["masterBlocksExpected"] + validatorsLoad[vid]["workBlocksExpected"] validator["is_masterchain"] = False From 52c143a872712204365f0f61dfa6310c3c53042d Mon Sep 17 00:00:00 2001 From: yungwine Date: Thu, 19 Sep 2024 13:44:04 +0400 Subject: [PATCH 16/56] fix check_ef --- modules/validator.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/validator.py b/modules/validator.py index 0315b99c..8efbc904 100644 --- a/modules/validator.py +++ b/modules/validator.py @@ -63,6 +63,9 @@ def check_efficiency(self, args): color_efficiency = GetColorInt(efficiency, 90, logic="more", ending="%") created = validator.master_blocks_created expected = validator.master_blocks_expected + if created is None: # there is no updated prev round info in cache + created = validator.blocks_created + expected = validator.blocks_expected color_print(f"Previous round efficiency: {color_efficiency} {{yellow}}({created} blocks created / {round(expected, 1)} blocks expected){{endc}}") else: print("Couldn't find this validator in the previous round") From 13a6933f3cac51c946022d37cb2770b8293eb23c Mon Sep 17 00:00:00 2001 From: yungwine Date: Tue, 24 Sep 2024 13:42:26 +0400 Subject: [PATCH 17/56] improve backups --- mytonctrl/scripts/create_backup.sh | 5 ++++- mytonctrl/scripts/restore_backup.sh | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/mytonctrl/scripts/create_backup.sh b/mytonctrl/scripts/create_backup.sh index 6e6e51ff..9bab1355 100644 --- a/mytonctrl/scripts/create_backup.sh +++ b/mytonctrl/scripts/create_backup.sh @@ -1,5 +1,6 @@ dest="mytonctrl_backup_$(hostname)_$(date +%s).tar.gz" mtc_dir="$HOME/.local/share/mytoncore" +user=$(logname) # Get arguments while getopts d:m: flag do @@ -38,7 +39,9 @@ systemctl start mytoncore echo -e "${COLOR}[3/4]${ENDC} Started validator and mytoncore" -sudo tar -zcf ${dest} -C ${tmp_dir} . +tar -zcf $dest -C $tmp_dir . + +chown $user:$user $dest echo -e "${COLOR}[4/4]${ENDC} Backup successfully created in ${dest}!" echo -e "If you wish to use archive package to migrate node to different machine please make sure to stop validator and mytoncore on donor (this) host prior to migration." diff --git a/mytonctrl/scripts/restore_backup.sh b/mytonctrl/scripts/restore_backup.sh index fccac825..361c7ff9 100644 --- a/mytonctrl/scripts/restore_backup.sh +++ b/mytonctrl/scripts/restore_backup.sh @@ -29,6 +29,7 @@ rm -rf $tmp_dir mkdir $tmp_dir tar -xvzf $name -C $tmp_dir +rm -rf /var/ton-work/db/keyring cp -f ${tmp_dir}/config.json /var/ton-work/db/ cp -rf ${tmp_dir}/keyring /var/ton-work/db/ cp -rf ${tmp_dir}/keys /var/ton-work From 96d3d234c0bce6243cda06dfbe97c84309b8195b Mon Sep 17 00:00:00 2001 From: yungwine Date: Tue, 24 Sep 2024 14:15:14 +0400 Subject: [PATCH 18/56] fix enabling THA --- mytoncore/mytoncore.py | 5 ++- mytoninstaller/mytoninstaller.py | 4 +- .../scripts}/ton_http_api_installer.sh | 6 ++- mytoninstaller/scripts/tonhttpapiinstaller.sh | 38 ------------------- mytoninstaller/settings.py | 10 ++--- 5 files changed, 15 insertions(+), 48 deletions(-) rename {scripts => mytoninstaller/scripts}/ton_http_api_installer.sh (88%) delete mode 100755 mytoninstaller/scripts/tonhttpapiinstaller.sh diff --git a/mytoncore/mytoncore.py b/mytoncore/mytoncore.py index fe2ffb37..8d29160c 100644 --- a/mytoncore/mytoncore.py +++ b/mytoncore/mytoncore.py @@ -3052,6 +3052,9 @@ def check_enable_mode(self, name): if self.using_liteserver(): raise Exception(f'Cannot enable validator mode while liteserver mode is enabled. ' f'Use `disable_mode liteserver` first.') + if name == 'liquid-staking': + from mytoninstaller.settings import enable_ton_http_api + enable_ton_http_api(self.local) def enable_mode(self, name): if name not in MODES: @@ -3604,7 +3607,7 @@ def CalculateLoanAmount(self, min_loan, max_loan, max_interest): print(f"CalculateLoanAmount data: {data}") url = "http://127.0.0.1:8801/runGetMethod" - res = requests.post(url, json=data) + res = requests.post(url, json=data, timeout=3) res_data = res.json() if res_data.get("ok") is False: error = res_data.get("error") diff --git a/mytoninstaller/mytoninstaller.py b/mytoninstaller/mytoninstaller.py index c63111db..c5e60012 100644 --- a/mytoninstaller/mytoninstaller.py +++ b/mytoninstaller/mytoninstaller.py @@ -24,7 +24,7 @@ EnableLiteServer, EnableDhtServer, EnableJsonRpc, - EnableTonHttpApi, + enable_ton_http_api, DangerousRecoveryValidatorConfigFile, CreateSymlinks, enable_ls_proxy, @@ -223,7 +223,7 @@ def Event(local, name): if name == "enableJR": EnableJsonRpc(local) if name == "enableTHA": - EnableTonHttpApi(local) + enable_ton_http_api(local) if name == "enableLSP": enable_ls_proxy(local) if name == "enableTSP": diff --git a/scripts/ton_http_api_installer.sh b/mytoninstaller/scripts/ton_http_api_installer.sh similarity index 88% rename from scripts/ton_http_api_installer.sh rename to mytoninstaller/scripts/ton_http_api_installer.sh index 9327f870..039c3bd2 100644 --- a/scripts/ton_http_api_installer.sh +++ b/mytoninstaller/scripts/ton_http_api_installer.sh @@ -30,11 +30,15 @@ chown -R ${user}:${user} ${venv_path} echo -e "${COLOR}[3/4]${ENDC} Add to startup" venv_ton_http_api="${venv_path}/bin/ton-http-api" tonlib_path="/usr/bin/ton/tonlib/libtonlibjson.so" -ls_config="/usr/bin/ton/localhost.config.json" +ls_config="/usr/bin/ton/local.config.json" cmd="from sys import path; path.append('/usr/src/mytonctrl/'); from mypylib.mypylib import add2systemd; add2systemd(name='ton_http_api', user='${user}', start='${venv_ton_http_api} --host 127.0.0.1 --port 8801 --liteserver-config ${ls_config} --cdll-path ${tonlib_path} --tonlib-keystore /tmp/tonlib_keystore/')" python3 -c "${cmd}" +systemctl daemon-reload systemctl restart ton_http_api +echo -e "Requesting masterchain info from local ton http api" +curl http://127.0.0.1:8801/getMasterchainInfo + # Конец echo -e "${COLOR}[4/4]${ENDC} ton_http_api service installation complete" exit 0 diff --git a/mytoninstaller/scripts/tonhttpapiinstaller.sh b/mytoninstaller/scripts/tonhttpapiinstaller.sh deleted file mode 100755 index c22b98e0..00000000 --- a/mytoninstaller/scripts/tonhttpapiinstaller.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash -set -e - -# Проверить sudo -if [ "$(id -u)" != "0" ]; then - echo "Please run script as root" - exit 1 -fi - -# Get arguments -while getopts u: flag -do - case "${flag}" in - u) user=${OPTARG};; - esac -done - -# Цвета -COLOR='\033[92m' -ENDC='\033[0m' - -# Установка компонентов python3 -echo -e "${COLOR}[1/3]${ENDC} Installing required packages" -pip3 install -U ton-http-api - -# Установка модуля -echo -e "${COLOR}[2/3]${ENDC} Add to startup" -mkdir -p /var/ton-http-api/ton_keystore/ -chown -R $user /var/ton-http-api/ - -SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) -cmd="ton-http-api --port=8000 --logs-level=INFO --cdll-path=/usr/bin/ton/tonlib/libtonlibjson.so --liteserver-config /usr/bin/ton/local.config.json --tonlib-keystore=/var/ton-http-api/ton_keystore/ --parallel-requests-per-liteserver=1024" -${SCRIPT_DIR}/add2systemd.sh -n ton-http-api -s "${cmd}" -u ${user} -g ${user} -systemctl restart ton-http-api - -# Конец -echo -e "${COLOR}[3/3]${ENDC} TonHttpApi installation complete" -exit 0 diff --git a/mytoninstaller/settings.py b/mytoninstaller/settings.py index 45d167bb..b2f4ad1e 100644 --- a/mytoninstaller/settings.py +++ b/mytoninstaller/settings.py @@ -451,12 +451,10 @@ def EnableJsonRpc(local): color_print(text) #end define -def EnableTonHttpApi(local): - local.add_log("start EnablePytonv3 function", "debug") - user = local.buffer.user - - ton_http_api_installer_path = pkg_resources.resource_filename('mytoninstaller.scripts', 'tonhttpapiinstaller.sh') - exit_code = run_as_root(["bash", ton_http_api_installer_path, "-u", user]) +def enable_ton_http_api(local): + local.add_log("start EnableTonHttpApi function", "debug") + ton_http_api_installer_path = pkg_resources.resource_filename('mytoninstaller.scripts', 'ton_http_api_installer.sh') + exit_code = run_as_root(["bash", ton_http_api_installer_path]) if exit_code == 0: text = "EnableTonHttpApi - {green}OK{endc}" else: From c5e002683ac0c96c85ffb20d28ff30928fd474f0 Mon Sep 17 00:00:00 2001 From: yungwine Date: Tue, 24 Sep 2024 14:36:51 +0400 Subject: [PATCH 19/56] update THA installer script --- mytoninstaller/scripts/ton_http_api_installer.sh | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/mytoninstaller/scripts/ton_http_api_installer.sh b/mytoninstaller/scripts/ton_http_api_installer.sh index 039c3bd2..fa87c42c 100644 --- a/mytoninstaller/scripts/ton_http_api_installer.sh +++ b/mytoninstaller/scripts/ton_http_api_installer.sh @@ -1,7 +1,7 @@ #!/bin/bash set -e -# Проверить sudo +# check sudo if [ "$(id -u)" != "0" ]; then echo "Please run script as root" exit 1 @@ -11,34 +11,36 @@ fi COLOR='\033[92m' ENDC='\033[0m' -# Установка компонентов python3 +# install python3 packages pip3 install virtualenv -# Подготовить папку с виртуальным окружением +# prepare the virtual environment echo -e "${COLOR}[1/4]${ENDC} Preparing the virtual environment" venv_path="/opt/virtualenv/ton_http_api" virtualenv ${venv_path} -# Установка компонентов python3 +# install python3 packages echo -e "${COLOR}[2/4]${ENDC} Installing required packages" user=$(logname) venv_pip3="${venv_path}/bin/pip3" ${venv_pip3} install ton-http-api chown -R ${user}:${user} ${venv_path} -# Прописать автозагрузку +# add to startup echo -e "${COLOR}[3/4]${ENDC} Add to startup" venv_ton_http_api="${venv_path}/bin/ton-http-api" tonlib_path="/usr/bin/ton/tonlib/libtonlibjson.so" ls_config="/usr/bin/ton/local.config.json" -cmd="from sys import path; path.append('/usr/src/mytonctrl/'); from mypylib.mypylib import add2systemd; add2systemd(name='ton_http_api', user='${user}', start='${venv_ton_http_api} --host 127.0.0.1 --port 8801 --liteserver-config ${ls_config} --cdll-path ${tonlib_path} --tonlib-keystore /tmp/tonlib_keystore/')" +cmd="from sys import path; path.append('/usr/src/mytonctrl/'); from mypylib.mypylib import add2systemd; add2systemd(name='ton_http_api', user='${user}', start='${venv_ton_http_api} --logs-level=INFO --host 127.0.0.1 --port 8801 --liteserver-config ${ls_config} --cdll-path ${tonlib_path} --tonlib-keystore /tmp/tonlib_keystore/')" python3 -c "${cmd}" systemctl daemon-reload systemctl restart ton_http_api +# check connection echo -e "Requesting masterchain info from local ton http api" +sleep 5 curl http://127.0.0.1:8801/getMasterchainInfo -# Конец +# end echo -e "${COLOR}[4/4]${ENDC} ton_http_api service installation complete" exit 0 From 8146d4e76062b0c432053f35d6a07fb051e2c4ea Mon Sep 17 00:00:00 2001 From: yungwine Date: Wed, 25 Sep 2024 22:29:51 +0400 Subject: [PATCH 20/56] fix buffering funciton for GetValidatorsLoad --- mytoncore/mytoncore.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mytoncore/mytoncore.py b/mytoncore/mytoncore.py index 9f65af15..5c2679a6 100644 --- a/mytoncore/mytoncore.py +++ b/mytoncore/mytoncore.py @@ -2350,7 +2350,7 @@ def GetOnlineValidators(self): def GetValidatorsLoad(self, start, end, saveCompFiles=False) -> dict: # Get buffer - bname = f"validatorsLoad{start}{end}" + bname = f"validatorsLoad{start}{end}{saveCompFiles}" buff = self.GetFunctionBuffer(bname, timeout=60) if buff: return buff From 3b25c96b27ea784bbee4a61c287d0e70620d48b5 Mon Sep 17 00:00:00 2001 From: yungwine Date: Thu, 26 Sep 2024 17:26:48 +0400 Subject: [PATCH 21/56] fix DownloadDump --- mytoninstaller/settings.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/mytoninstaller/settings.py b/mytoninstaller/settings.py index f9ef4aaa..9d8db7be 100644 --- a/mytoninstaller/settings.py +++ b/mytoninstaller/settings.py @@ -9,10 +9,10 @@ import pkg_resources from mypylib.mypylib import ( - add2systemd, - get_dir_from_path, - run_as_root, - color_print, + add2systemd, + get_dir_from_path, + run_as_root, + color_print, ip2int, Dict ) @@ -110,17 +110,17 @@ def DownloadDump(local): # download dump using aria2c to a temporary file temp_file = "/tmp/latest.tar.lz" - cmd = "aria2c -x 8 -s 8 -c {url}/dumps/latest.tar.lz -o {temp_file}".format(url=url, temp_file=temp_file) + cmd = f"aria2c -x 8 -s 8 -c {url}/dumps/latest.tar.lz -d / -o {temp_file}" os.system(cmd) # process the downloaded file - cmd = "pv {temp_file} | plzip -d -n8 | tar -xC /var/ton-work/db" + cmd = f"pv {temp_file} | plzip -d -n8 | tar -xC /var/ton-work/db" os.system(cmd) # clean up the temporary file after processing if os.path.exists(temp_file): os.remove(temp_file) - local.add_log("Temporary file {temp_file} removed".format(temp_file=temp_file), "debug") + local.add_log(f"Temporary file {temp_file} removed", "debug") #end if #end define From 75fdd96ff52617fb4c0dfb737c17998749c11649 Mon Sep 17 00:00:00 2001 From: yungwine Date: Tue, 1 Oct 2024 12:10:11 +0400 Subject: [PATCH 22/56] improve WaitTransaction seqno fetching --- mytoncore/mytoncore.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mytoncore/mytoncore.py b/mytoncore/mytoncore.py index 9f65af15..e19ee8f6 100644 --- a/mytoncore/mytoncore.py +++ b/mytoncore/mytoncore.py @@ -1226,8 +1226,13 @@ def WaitTransaction(self, wallet, timeout=30): steps = timeout // timesleep for i in range(steps): time.sleep(timesleep) - seqno = self.GetSeqno(wallet) + try: + seqno = self.GetSeqno(wallet) + except: + self.local.add_log("WaitTransaction error: Can't get seqno", "warning") + continue if seqno != wallet.oldseqno: + self.local.add_log("WaitTransaction success", "info") return raise Exception("WaitTransaction error: time out") #end define From c89c21a6640e77011bc486795088a82df3ac5384 Mon Sep 17 00:00:00 2001 From: yungwine Date: Tue, 1 Oct 2024 12:53:36 +0400 Subject: [PATCH 23/56] do not repeat WithdrawFromPool on errors --- mytoncore/mytoncore.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mytoncore/mytoncore.py b/mytoncore/mytoncore.py index e19ee8f6..10754fe0 100644 --- a/mytoncore/mytoncore.py +++ b/mytoncore/mytoncore.py @@ -3275,9 +3275,8 @@ def PendWithdrawFromPool(self, poolAddr, amount): #end define def HandlePendingWithdraw(self, pendingWithdraws, poolAddr): - amount = pendingWithdraws.get(poolAddr) + amount = pendingWithdraws.pop(poolAddr) self.WithdrawFromPoolProcess(poolAddr, amount) - pendingWithdraws.pop(poolAddr) #end define def GetPendingWithdraws(self): From a7d37b50919fdf1e724b4a01367618f31f5d684b Mon Sep 17 00:00:00 2001 From: yungwine Date: Wed, 2 Oct 2024 13:11:06 +0400 Subject: [PATCH 24/56] allow fines 1% of stake for not working validators --- mytoncore/mytoncore.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/mytoncore/mytoncore.py b/mytoncore/mytoncore.py index 9f65af15..838faa9e 100644 --- a/mytoncore/mytoncore.py +++ b/mytoncore/mytoncore.py @@ -2305,6 +2305,7 @@ def get_valid_complaints(self, complaints: dict, election_id: int): continue exists = False + vload = None for item in validators_load.values(): if 'fileName' not in item: continue @@ -2314,14 +2315,14 @@ def get_valid_complaints(self, complaints: dict, election_id: int): pseudohash = pubkey + str(election_id) if pseudohash == complaint['pseudohash']: exists = True - vid = item['id'] + vload = item break if not exists: self.local.add_log(f"complaint {complaint['hash_hex']} declined: complaint info was not found, probably it's wrong", "info") continue - if vid >= config32['mainValidators']: + if vload["id"] >= config32['mainValidators']: self.local.add_log(f"complaint {complaint['hash_hex']} declined: complaint created for non masterchain validator", "info") continue @@ -2330,8 +2331,13 @@ def get_valid_complaints(self, complaints: dict, election_id: int): self.local.add_log(f"complaint {complaint['hash_hex']} declined: complaint fine value is {complaint['suggestedFine']} ton", "info") continue if complaint['suggestedFinePart'] != 0: # https://github.com/ton-blockchain/ton/blob/5847897b3758bc9ea85af38e7be8fc867e4c133a/lite-client/lite-client.cpp#L3709 - self.local.add_log(f"complaint {complaint['hash_hex']} declined: complaint fine part value is {complaint['suggestedFinePart']} ton", "info") - continue + if vload["id"] < config32['mainValidators'] and vload["masterBlocksCreated"] + vload["workBlocksCreated"] == 0: # masterchain validator that created 0 blocks + if complaint['suggestedFinePart'] != 42949672: # (1LL << 32) / 100 + self.local.add_log(f"complaint {complaint['hash_hex']} declined: complaint fine part value is {complaint['suggestedFinePart']} ton", "info") + continue + else: + self.local.add_log(f"complaint {complaint['hash_hex']} declined: complaint fine part value is {complaint['suggestedFinePart']} ton", "info") + continue result[complaint['pseudohash']] = complaint return result From 6ef922be48f5dc5c45511cb21866439eccf43f09 Mon Sep 17 00:00:00 2001 From: yungwine Date: Wed, 2 Oct 2024 13:45:54 +0400 Subject: [PATCH 25/56] update fine warnings --- modules/validator.py | 4 ++-- mytoncore/mytoncore.py | 1 + mytonctrl/mytonctrl.py | 15 +++++++++++++-- mytonctrl/resources/translate.json | 6 +++--- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/modules/validator.py b/modules/validator.py index 7a6b9ad3..05f6e98a 100644 --- a/modules/validator.py +++ b/modules/validator.py @@ -55,7 +55,7 @@ def check_efficiency(self, args): color_print(f"Previous round time: {{yellow}}from {start_time} to {end_time}{{endc}}") if validator: if validator.is_masterchain == False: - print("Validator index is greater than 100 in the previous round - no efficiency data.") + print(f"Validator index is greater than {config32['mainValidators']} in the previous round - no efficiency data.") elif validator.get('efficiency') is None: print('Failed to get efficiency for the previous round') else: @@ -72,7 +72,7 @@ def check_efficiency(self, args): color_print(f"Current round time: {{green}}from {start_time} to {end_time}{{endc}}") if validator: if validator.is_masterchain == False: - print("Validator index is greater than 100 in the current round - no efficiency data.") + print(f"Validator index is greater than {config34['mainValidators']} in the current round - no efficiency data.") elif (time.time() - config34.startWorkTime) / (config34.endWorkTime - config34.startWorkTime) < 0.8: print("The validation round has started recently, there is not enough data yet. " "The efficiency evaluation will become more accurate towards the end of the round.") diff --git a/mytoncore/mytoncore.py b/mytoncore/mytoncore.py index 838faa9e..60280f4c 100644 --- a/mytoncore/mytoncore.py +++ b/mytoncore/mytoncore.py @@ -2482,6 +2482,7 @@ def GetValidatorsList(self, past=False, fast=False): validator["efficiency"] = round(validator["wr"] * 100, 2) if saveElectionEntries and adnlAddr in saveElectionEntries: validator["walletAddr"] = saveElectionEntries[adnlAddr]["walletAddr"] + validator["stake"] = saveElectionEntries[adnlAddr].get("stake") #end for # Set buffer diff --git a/mytonctrl/mytonctrl.py b/mytonctrl/mytonctrl.py index 1020c5c9..1bc78e4d 100755 --- a/mytonctrl/mytonctrl.py +++ b/mytonctrl/mytonctrl.py @@ -483,11 +483,22 @@ def check_slashed(local, ton): config32 = ton.GetConfig32() save_complaints = ton.GetSaveComplaints() complaints = save_complaints.get(str(config32['startWorkTime'])) + from modules.validator import ValidatorModule + module = ValidatorModule(ton, local) + vl = ton.GetValidatorsList(past=True) + me = module.find_myself(vl) + if not me: # we were not a validator in the previous round + return if not complaints: return for c in complaints.values(): - if c["adnl"] == ton.GetAdnlAddr() and c["isPassed"]: - print_warning(local, "slashed_warning") + if c["adnl"] == me["adnlAddr"] and c["isPassed"]: + if me.get("stake"): + fine = f"""{round(c['suggestedFine'] + me["stake"] * (c['suggestedFinePart'] / (1<<32)))} TON""" + else: # unknown stake amount so just print percents + fine = f"""{round(c['suggestedFine'])} TON + {(c['suggestedFinePart'] / (1<<32)) * 100} % of stake""" + warning = local.translate("slashed_warning").format(fine) + print_warning(local, warning) #end define def check_adnl(local, ton): diff --git a/mytonctrl/resources/translate.json b/mytonctrl/resources/translate.json index 632158b4..19dfbeec 100644 --- a/mytonctrl/resources/translate.json +++ b/mytonctrl/resources/translate.json @@ -445,9 +445,9 @@ "zh_TW": "{red}錯誤 - 驗證器的 UDP 端口無法從外部訪問.{endc}" }, "slashed_warning": { - "en": "{red}You were fined by 101 TON for low efficiency in the previous round.{endc}", - "ru": "{red}Вы были оштрафованы на 101 TON за низкую эффективность в предыдущем раунде.{endc}", - "zh_TW": "{red}您因上一輪效率低而被罰款 101 TON。{endc}" + "en": "{red}You were fined by {0} for low efficiency in the previous round.{endc}", + "ru": "{red}Вы были оштрафованы на {0} за низкую эффективность в предыдущем раунде.{endc}", + "zh_TW": "{red}您因上一輪效率低而被罰款 {0}。{endc}" }, "add_custom_overlay_cmd": { "en": "Add custom overlay", From da653d8a7cd56108e61d6ebf87adca3c42949d61 Mon Sep 17 00:00:00 2001 From: yungwine Date: Thu, 3 Oct 2024 21:37:44 +0400 Subject: [PATCH 26/56] add AlertBot --- modules/__init__.py | 6 +- modules/alert_bot.py | 151 +++++++++++++++++++++++++++++++++++++++++ mytoncore/functions.py | 4 ++ mytoncore/mytoncore.py | 3 + mytoncore/utils.py | 4 ++ 5 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 modules/alert_bot.py diff --git a/modules/__init__.py b/modules/__init__.py index d639c4fb..afb61e14 100644 --- a/modules/__init__.py +++ b/modules/__init__.py @@ -8,6 +8,7 @@ from modules.validator import ValidatorModule from modules.controller import ControllerModule from modules.liteserver import LiteserverModule +from modules.alert_bot import AlertBotModule MODES = { @@ -15,7 +16,8 @@ 'nominator-pool': NominatorPoolModule, 'single-nominator': SingleNominatorModule, 'liquid-staking': ControllerModule, - 'liteserver': LiteserverModule + 'liteserver': LiteserverModule, + 'alert-bot': AlertBotModule } @@ -55,6 +57,8 @@ class Setting: 'defaultCustomOverlaysUrl': Setting(None, 'https://ton-blockchain.github.io/fallback_custom_overlays.json', 'Default custom overlays config url'), 'debug': Setting(None, False, 'Debug mtc console mode. Prints Traceback on errors'), 'subscribe_tg_channel': Setting('validator', False, 'Disables warning about subscribing to the `TON STATUS` channel'), + 'BotToken': Setting('alert-bot', None, 'Alerting Telegram bot token'), + 'ChatId': Setting('alert-bot', None, 'Alerting Telegram chat id') } diff --git a/modules/alert_bot.py b/modules/alert_bot.py new file mode 100644 index 00000000..6118ffb1 --- /dev/null +++ b/modules/alert_bot.py @@ -0,0 +1,151 @@ +import dataclasses +import time + +from modules.module import MtcModule +from mytoncore import get_hostname +from mytonctrl.utils import timestamp2utcdatetime + + +class Alert(dataclasses.dataclass): + severity: str + text: str + timeout: int + + +HOUR = 3600 + + +ALERTS = { + "low_wallet_balance": Alert( + "medium", + "Validator wallet {wallet} balance is low: {balance} TON.", + 18*HOUR + ), + "db_usage_80": Alert( + "high", + """TON DB usage > 80%. Clean the TON database: + https://docs.ton.org/participate/nodes/node-maintenance-and-security#database-grooming + or (and) set node\'s archive ttl to lower value.""", + 24*HOUR + ), + "db_usage_95": Alert( + "critical", + """TON DB usage > 95%. Disk is almost full, clean the TON database immediately: + https://docs.ton.org/participate/nodes/node-maintenance-and-security#database-grooming + or (and) set node\'s archive ttl to lower value.""", + 6*HOUR + ), + "low_efficiency": Alert( + "high", + """Validator efficiency is low: {efficiency}%.""", + 12*HOUR + ), + "zero_block_created": Alert( + "critical", + "No blocks created for the last 6 hours.", + 6*HOUR + ), + "out_of_sync": Alert( + "critical", + "Node is out of sync on {sync} sec.", + 0 + ) +} + + +class AlertBotModule(MtcModule): + + description = 'Telegram bot alerts' + default_value = False + + def __init__(self, ton, local, *args, **kwargs): + super().__init__(ton, local, *args, **kwargs) + self.inited = False + self.hostname = None + self.bot = None + self.token = self.ton.local.db.get("BotToken") + self.chat_id = self.ton.local.db.get("ChatId") + + def send_message(self, text: str): + if self.bot is not None: + self.bot.send_message(self.chat_id, text) + else: + raise Exception("send_message error: bot is not initialized") + + def send_alert(self, alert_name: str, *args, **kwargs): + last_sent = self.get_alert_sent(alert_name) + time_ = timestamp2utcdatetime(int(time.time())) + alert = ALERTS.get(alert_name) + if alert is None: + raise Exception(f"Alert {alert_name} not found") + text = f''' +MyTonCtrl Alert {alert_name} + +Hostname: {self.hostname} +Time: {time_} ({int(time.time())}) +Severity: {alert.severity} +Next alert of this type in: {alert.timeout} sec + +Alert text: +
{alert.text.format(*args, **kwargs)}
+''' + if time.time() - last_sent > alert.timeout: + self.send_message(text) + self.set_alert_sent(alert_name) + + def init(self): + if not self.ton.get_mode_value('alert-bot'): + return + if self.token is None or self.chat_id is None: + raise Exception("BotToken or ChatId is not set") + import telebot + self.bot = telebot.TeleBot(self.token, parse_mode="HTML") + self.hostname = get_hostname() + self.inited = True + + def set_alert_sent(self, alert_name: str): + self.ton.local.db['alerts'][alert_name] = int(time.time()) + + def get_alert_sent(self, alert_name: str): + return self.ton.local.db['alerts'].get(alert_name, 0) + + def check_db_usage(self): + usage = self.ton.GetDbUsage() + if usage > 95: + self.send_alert("db_usage_95") + elif usage > 80: + self.send_alert("db_usage_80") + + def check_validator_wallet_balance(self): + validator_wallet = self.ton.GetValidatorWallet() + validator_account = self.ton.GetAccount(validator_wallet.addrB64) + if validator_account.balance < 50: + self.send_alert("low_wallet_balance", wallet=validator_wallet.addrB64, balance=validator_account.balance) + + def check_efficiency(self): + from modules.validator import ValidatorModule + validator = ValidatorModule(self.ton, self.local).find_myself(self.ton.GetValidatorsList()) + if validator is None or validator.is_masterchain is False or validator.efficiency is None: + return + config34 = self.ton.GetConfig34() + if (time.time() - config34.startWorkTime) / (config34.endWorkTime - config34.startWorkTime) < 0.8: + return # less than 80% of round passed + if validator.efficiency < 90: + self.send_alert("low_efficiency", efficiency=validator.efficiency) + + def check_sync(self): + validator_status = self.ton.GetValidatorStatus() + if not validator_status.is_working or validator_status.out_of_sync >= 20: + self.send_alert("out_of_sync", sync=validator_status.out_of_sync) + + def check_status(self): + if not self.inited: + self.init() + + self.local.try_function(self.check_db_usage) + self.local.try_function(self.check_validator_wallet_balance) + self.local.try_function(self.check_efficiency) # todo: alert if validator is going to be slashed + self.local.try_function(self.check_sync) + + def add_console_commands(self, console): + ... diff --git a/mytoncore/functions.py b/mytoncore/functions.py index 686fb657..148c07d1 100755 --- a/mytoncore/functions.py +++ b/mytoncore/functions.py @@ -569,6 +569,10 @@ def General(local): from modules.custom_overlays import CustomOverlayModule local.start_cycle(CustomOverlayModule(ton, local).custom_overlays, sec=60, args=()) + if ton.get_mode_value('alert-bot'): + from modules.alert_bot import AlertBotModule + local.start_cycle(AlertBotModule(ton, local).check_status, sec=1000, args=()) + thr_sleep() # end define diff --git a/mytoncore/mytoncore.py b/mytoncore/mytoncore.py index 87ab360f..e90fe6bd 100644 --- a/mytoncore/mytoncore.py +++ b/mytoncore/mytoncore.py @@ -3054,6 +3054,9 @@ def check_enable_mode(self, name): if name == 'liquid-staking': from mytoninstaller.settings import enable_ton_http_api enable_ton_http_api(self.local) + if name == 'alert-bot': + args = ["pip", "install", "pytelegrambotapi==4.23.0"] + subprocess.run(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=10) def enable_mode(self, name): if name not in MODES: diff --git a/mytoncore/utils.py b/mytoncore/utils.py index 0a8bdc91..a31e299c 100644 --- a/mytoncore/utils.py +++ b/mytoncore/utils.py @@ -1,6 +1,7 @@ import base64 import json import re +import subprocess def str2b64(s): @@ -97,3 +98,6 @@ def parse_db_stats(path: str): result[s[0]] = {k: float(v) for k, v in items} return result # end define + +def get_hostname(): + return subprocess.run(["hostname", "-f"], stdout=subprocess.PIPE).stdout.decode().strip() From 0b7d6f3b9b5b2156459327f5a7f71b22cc855599 Mon Sep 17 00:00:00 2001 From: yungwine Date: Thu, 3 Oct 2024 21:42:57 +0400 Subject: [PATCH 27/56] fix dataclass --- modules/alert_bot.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/alert_bot.py b/modules/alert_bot.py index 6118ffb1..198a79b9 100644 --- a/modules/alert_bot.py +++ b/modules/alert_bot.py @@ -6,7 +6,8 @@ from mytonctrl.utils import timestamp2utcdatetime -class Alert(dataclasses.dataclass): +@dataclasses.dataclass +class Alert: severity: str text: str timeout: int From 1a1d069afb640bdba04af588c8d7f1a3ac4545cc Mon Sep 17 00:00:00 2001 From: yungwine Date: Thu, 3 Oct 2024 21:47:29 +0400 Subject: [PATCH 28/56] fix alerts in db --- modules/alert_bot.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/alert_bot.py b/modules/alert_bot.py index 198a79b9..f7d43e06 100644 --- a/modules/alert_bot.py +++ b/modules/alert_bot.py @@ -105,9 +105,13 @@ def init(self): self.inited = True def set_alert_sent(self, alert_name: str): + if 'alerts' not in self.ton.local.db: + self.ton.local.db['alerts'] = {} self.ton.local.db['alerts'][alert_name] = int(time.time()) def get_alert_sent(self, alert_name: str): + if 'alerts' not in self.ton.local.db: + return 0 return self.ton.local.db['alerts'].get(alert_name, 0) def check_db_usage(self): From 3f0c559a27f522012cb06b77d1887435a004fcbc Mon Sep 17 00:00:00 2001 From: yungwine Date: Thu, 3 Oct 2024 21:53:26 +0400 Subject: [PATCH 29/56] add service_down alert --- modules/alert_bot.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/modules/alert_bot.py b/modules/alert_bot.py index f7d43e06..3c180a62 100644 --- a/modules/alert_bot.py +++ b/modules/alert_bot.py @@ -50,7 +50,12 @@ class Alert: "critical", "Node is out of sync on {sync} sec.", 0 - ) + ), + "service_down": Alert( + "critical", + "validator.service is down.", + 0 + ), } @@ -129,7 +134,7 @@ def check_validator_wallet_balance(self): def check_efficiency(self): from modules.validator import ValidatorModule - validator = ValidatorModule(self.ton, self.local).find_myself(self.ton.GetValidatorsList()) + validator = ValidatorModule(self.ton, self.local).find_myself(self.ton.GetValidatorsList(fast=True)) if validator is None or validator.is_masterchain is False or validator.efficiency is None: return config34 = self.ton.GetConfig34() @@ -138,9 +143,14 @@ def check_efficiency(self): if validator.efficiency < 90: self.send_alert("low_efficiency", efficiency=validator.efficiency) + def check_validator_working(self): + validator_status = self.ton.GetValidatorStatus() + if not validator_status.is_working: + self.send_alert("service_down") + def check_sync(self): validator_status = self.ton.GetValidatorStatus() - if not validator_status.is_working or validator_status.out_of_sync >= 20: + if validator_status.is_working and validator_status.out_of_sync >= 20: self.send_alert("out_of_sync", sync=validator_status.out_of_sync) def check_status(self): @@ -150,6 +160,8 @@ def check_status(self): self.local.try_function(self.check_db_usage) self.local.try_function(self.check_validator_wallet_balance) self.local.try_function(self.check_efficiency) # todo: alert if validator is going to be slashed + self.local.try_function(self.check_validator_working) + self.local.try_function(self.check_validator_working) self.local.try_function(self.check_sync) def add_console_commands(self, console): From 4d6354575711461151746827399039c5aeac47eb Mon Sep 17 00:00:00 2001 From: yungwine Date: Thu, 3 Oct 2024 21:55:43 +0400 Subject: [PATCH 30/56] fix alerts timeout --- modules/alert_bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/alert_bot.py b/modules/alert_bot.py index 3c180a62..c73f774f 100644 --- a/modules/alert_bot.py +++ b/modules/alert_bot.py @@ -90,7 +90,7 @@ def send_alert(self, alert_name: str, *args, **kwargs): Hostname: {self.hostname} Time: {time_} ({int(time.time())}) Severity: {alert.severity} -Next alert of this type in: {alert.timeout} sec +Next alert of this type not earlier than: {max(alert.timeout, 1000)} sec Alert text:
{alert.text.format(*args, **kwargs)}
From 029acf05c1b7a7f988428f3077c68c922bc14f85 Mon Sep 17 00:00:00 2001 From: yungwine Date: Thu, 3 Oct 2024 21:57:33 +0400 Subject: [PATCH 31/56] fix double service_down alert --- modules/alert_bot.py | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/alert_bot.py b/modules/alert_bot.py index c73f774f..b0b45384 100644 --- a/modules/alert_bot.py +++ b/modules/alert_bot.py @@ -161,7 +161,6 @@ def check_status(self): self.local.try_function(self.check_validator_wallet_balance) self.local.try_function(self.check_efficiency) # todo: alert if validator is going to be slashed self.local.try_function(self.check_validator_working) - self.local.try_function(self.check_validator_working) self.local.try_function(self.check_sync) def add_console_commands(self, console): From 1ee5e9b427dd13d8efe91ba3b7895e3230739ac6 Mon Sep 17 00:00:00 2001 From: yungwine Date: Mon, 7 Oct 2024 11:20:06 +0400 Subject: [PATCH 32/56] update alerts text --- modules/alert_bot.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/modules/alert_bot.py b/modules/alert_bot.py index b0b45384..88062309 100644 --- a/modules/alert_bot.py +++ b/modules/alert_bot.py @@ -18,7 +18,7 @@ class Alert: ALERTS = { "low_wallet_balance": Alert( - "medium", + "low", "Validator wallet {wallet} balance is low: {balance} TON.", 18*HOUR ), @@ -43,7 +43,7 @@ class Alert: ), "zero_block_created": Alert( "critical", - "No blocks created for the last 6 hours.", + "Validator has not created any blocks in the last 6 hours.", 6*HOUR ), "out_of_sync": Alert( @@ -56,6 +56,11 @@ class Alert: "validator.service is down.", 0 ), + "adnl_connection_failed": Alert( + "high", + "ADNL connection to node failed", + 3*HOUR + ), } @@ -85,7 +90,7 @@ def send_alert(self, alert_name: str, *args, **kwargs): if alert is None: raise Exception(f"Alert {alert_name} not found") text = f''' -MyTonCtrl Alert {alert_name} +❗️ MyTonCtrl Alert {alert_name} ❗️ Hostname: {self.hostname} Time: {time_} ({int(time.time())}) From 02ca414e7f7fff9f3317612fcc348ef7ae346a80 Mon Sep 17 00:00:00 2001 From: yungwine Date: Tue, 8 Oct 2024 12:48:02 +0400 Subject: [PATCH 33/56] add zero_blocks_created alert --- modules/alert_bot.py | 30 +++++++++++++++++++++++------- mytoncore/mytoncore.py | 16 +++++++++------- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/modules/alert_bot.py b/modules/alert_bot.py index 88062309..2e031357 100644 --- a/modules/alert_bot.py +++ b/modules/alert_bot.py @@ -41,11 +41,6 @@ class Alert: """Validator efficiency is low: {efficiency}%.""", 12*HOUR ), - "zero_block_created": Alert( - "critical", - "Validator has not created any blocks in the last 6 hours.", - 6*HOUR - ), "out_of_sync": Alert( "critical", "Node is out of sync on {sync} sec.", @@ -61,6 +56,11 @@ class Alert: "ADNL connection to node failed", 3*HOUR ), + "zero_block_created": Alert( + "critical", + "Validator has not created any blocks in the last 6 hours.", + 6 * HOUR + ), } @@ -71,6 +71,7 @@ class AlertBotModule(MtcModule): def __init__(self, ton, local, *args, **kwargs): super().__init__(ton, local, *args, **kwargs) + self.validator_module = None self.inited = False self.hostname = None self.bot = None @@ -109,6 +110,8 @@ def init(self): return if self.token is None or self.chat_id is None: raise Exception("BotToken or ChatId is not set") + from modules.validator import ValidatorModule + self.validator_module = ValidatorModule(self.ton, self.local) import telebot self.bot = telebot.TeleBot(self.token, parse_mode="HTML") self.hostname = get_hostname() @@ -132,14 +135,17 @@ def check_db_usage(self): self.send_alert("db_usage_80") def check_validator_wallet_balance(self): + if not self.ton.using_validator(): + return validator_wallet = self.ton.GetValidatorWallet() validator_account = self.ton.GetAccount(validator_wallet.addrB64) if validator_account.balance < 50: self.send_alert("low_wallet_balance", wallet=validator_wallet.addrB64, balance=validator_account.balance) def check_efficiency(self): - from modules.validator import ValidatorModule - validator = ValidatorModule(self.ton, self.local).find_myself(self.ton.GetValidatorsList(fast=True)) + if not self.ton.using_validator(): + return + validator = self.validator_module.find_myself(self.ton.GetValidatorsList(fast=True)) if validator is None or validator.is_masterchain is False or validator.efficiency is None: return config34 = self.ton.GetConfig34() @@ -158,6 +164,15 @@ def check_sync(self): if validator_status.is_working and validator_status.out_of_sync >= 20: self.send_alert("out_of_sync", sync=validator_status.out_of_sync) + def check_zero_blocks_created(self): + if not self.ton.using_validator(): + return + validators = self.ton.GetValidatorsList(start=-6*HOUR, end=-60) + validator = self.validator_module.find_myself(validators) + if validator is None or validator.blocks_created > 0: + return + self.send_alert("zero_block_created") + def check_status(self): if not self.inited: self.init() @@ -166,6 +181,7 @@ def check_status(self): self.local.try_function(self.check_validator_wallet_balance) self.local.try_function(self.check_efficiency) # todo: alert if validator is going to be slashed self.local.try_function(self.check_validator_working) + self.local.try_function(self.check_zero_blocks_created) self.local.try_function(self.check_sync) def add_console_commands(self, console): diff --git a/mytoncore/mytoncore.py b/mytoncore/mytoncore.py index e90fe6bd..8e691687 100644 --- a/mytoncore/mytoncore.py +++ b/mytoncore/mytoncore.py @@ -2444,7 +2444,7 @@ def GetValidatorsLoad(self, start, end, saveCompFiles=False) -> dict: return data #end define - def GetValidatorsList(self, past=False, fast=False): + def GetValidatorsList(self, past=False, fast=False, start=None, end=None): # Get buffer bname = "validatorsList" + str(past) buff = self.GetFunctionBuffer(bname, timeout=60) @@ -2452,13 +2452,15 @@ def GetValidatorsList(self, past=False, fast=False): return buff #end if - timestamp = get_timestamp() - end = timestamp - 60 config = self.GetConfig34() - if fast: - start = end - 1000 - else: - start = config.get("startWorkTime") + if start is None: + if fast: + start = end - 1000 + else: + start = config.get("startWorkTime") + if end is None: + timestamp = get_timestamp() + end = timestamp - 60 if past: config = self.GetConfig32() start = config.get("startWorkTime") From 67d962083e8993d6ec097a0677fa010cee220e9b Mon Sep 17 00:00:00 2001 From: Maksim Kurbatov <94808996+yungwine@users.noreply.github.com> Date: Tue, 8 Oct 2024 19:12:24 +0400 Subject: [PATCH 34/56] Revert "update complaints validation and warnings" --- modules/validator.py | 4 ++-- mytoncore/mytoncore.py | 15 ++++----------- mytonctrl/mytonctrl.py | 15 ++------------- mytonctrl/resources/translate.json | 6 +++--- 4 files changed, 11 insertions(+), 29 deletions(-) diff --git a/modules/validator.py b/modules/validator.py index 3ce5cd6f..8efbc904 100644 --- a/modules/validator.py +++ b/modules/validator.py @@ -55,7 +55,7 @@ def check_efficiency(self, args): color_print(f"Previous round time: {{yellow}}from {start_time} to {end_time}{{endc}}") if validator: if validator.is_masterchain == False: - print(f"Validator index is greater than {config32['mainValidators']} in the previous round - no efficiency data.") + print("Validator index is greater than 100 in the previous round - no efficiency data.") elif validator.get('efficiency') is None: print('Failed to get efficiency for the previous round') else: @@ -75,7 +75,7 @@ def check_efficiency(self, args): color_print(f"Current round time: {{green}}from {start_time} to {end_time}{{endc}}") if validator: if validator.is_masterchain == False: - print(f"Validator index is greater than {config34['mainValidators']} in the current round - no efficiency data.") + print("Validator index is greater than 100 in the current round - no efficiency data.") elif (time.time() - config34.startWorkTime) / (config34.endWorkTime - config34.startWorkTime) < 0.8: print("The validation round has started recently, there is not enough data yet. " "The efficiency evaluation will become more accurate towards the end of the round.") diff --git a/mytoncore/mytoncore.py b/mytoncore/mytoncore.py index 87ab360f..3f3db256 100644 --- a/mytoncore/mytoncore.py +++ b/mytoncore/mytoncore.py @@ -2310,7 +2310,6 @@ def get_valid_complaints(self, complaints: dict, election_id: int): continue exists = False - vload = None for item in validators_load.values(): if 'fileName' not in item: continue @@ -2320,14 +2319,14 @@ def get_valid_complaints(self, complaints: dict, election_id: int): pseudohash = pubkey + str(election_id) if pseudohash == complaint['pseudohash']: exists = True - vload = item + vid = item['id'] break if not exists: self.local.add_log(f"complaint {complaint['hash_hex']} declined: complaint info was not found, probably it's wrong", "info") continue - if vload["id"] >= config32['mainValidators']: + if vid >= config32['mainValidators']: self.local.add_log(f"complaint {complaint['hash_hex']} declined: complaint created for non masterchain validator", "info") continue @@ -2336,13 +2335,8 @@ def get_valid_complaints(self, complaints: dict, election_id: int): self.local.add_log(f"complaint {complaint['hash_hex']} declined: complaint fine value is {complaint['suggestedFine']} ton", "info") continue if complaint['suggestedFinePart'] != 0: # https://github.com/ton-blockchain/ton/blob/5847897b3758bc9ea85af38e7be8fc867e4c133a/lite-client/lite-client.cpp#L3709 - if vload["id"] < config32['mainValidators'] and vload["masterBlocksCreated"] + vload["workBlocksCreated"] == 0: # masterchain validator that created 0 blocks - if complaint['suggestedFinePart'] != 42949672: # (1LL << 32) / 100 - self.local.add_log(f"complaint {complaint['hash_hex']} declined: complaint fine part value is {complaint['suggestedFinePart']} ton", "info") - continue - else: - self.local.add_log(f"complaint {complaint['hash_hex']} declined: complaint fine part value is {complaint['suggestedFinePart']} ton", "info") - continue + self.local.add_log(f"complaint {complaint['hash_hex']} declined: complaint fine part value is {complaint['suggestedFinePart']} ton", "info") + continue result[complaint['pseudohash']] = complaint return result @@ -2492,7 +2486,6 @@ def GetValidatorsList(self, past=False, fast=False): validator["efficiency"] = round(validator["wr"] * 100, 2) if saveElectionEntries and adnlAddr in saveElectionEntries: validator["walletAddr"] = saveElectionEntries[adnlAddr]["walletAddr"] - validator["stake"] = saveElectionEntries[adnlAddr].get("stake") #end for # Set buffer diff --git a/mytonctrl/mytonctrl.py b/mytonctrl/mytonctrl.py index 1bc78e4d..1020c5c9 100755 --- a/mytonctrl/mytonctrl.py +++ b/mytonctrl/mytonctrl.py @@ -483,22 +483,11 @@ def check_slashed(local, ton): config32 = ton.GetConfig32() save_complaints = ton.GetSaveComplaints() complaints = save_complaints.get(str(config32['startWorkTime'])) - from modules.validator import ValidatorModule - module = ValidatorModule(ton, local) - vl = ton.GetValidatorsList(past=True) - me = module.find_myself(vl) - if not me: # we were not a validator in the previous round - return if not complaints: return for c in complaints.values(): - if c["adnl"] == me["adnlAddr"] and c["isPassed"]: - if me.get("stake"): - fine = f"""{round(c['suggestedFine'] + me["stake"] * (c['suggestedFinePart'] / (1<<32)))} TON""" - else: # unknown stake amount so just print percents - fine = f"""{round(c['suggestedFine'])} TON + {(c['suggestedFinePart'] / (1<<32)) * 100} % of stake""" - warning = local.translate("slashed_warning").format(fine) - print_warning(local, warning) + if c["adnl"] == ton.GetAdnlAddr() and c["isPassed"]: + print_warning(local, "slashed_warning") #end define def check_adnl(local, ton): diff --git a/mytonctrl/resources/translate.json b/mytonctrl/resources/translate.json index 19dfbeec..632158b4 100644 --- a/mytonctrl/resources/translate.json +++ b/mytonctrl/resources/translate.json @@ -445,9 +445,9 @@ "zh_TW": "{red}錯誤 - 驗證器的 UDP 端口無法從外部訪問.{endc}" }, "slashed_warning": { - "en": "{red}You were fined by {0} for low efficiency in the previous round.{endc}", - "ru": "{red}Вы были оштрафованы на {0} за низкую эффективность в предыдущем раунде.{endc}", - "zh_TW": "{red}您因上一輪效率低而被罰款 {0}。{endc}" + "en": "{red}You were fined by 101 TON for low efficiency in the previous round.{endc}", + "ru": "{red}Вы были оштрафованы на 101 TON за низкую эффективность в предыдущем раунде.{endc}", + "zh_TW": "{red}您因上一輪效率低而被罰款 101 TON。{endc}" }, "add_custom_overlay_cmd": { "en": "Add custom overlay", From 306f7b3ddb9bfa998d0abbf4fb3260eea2e7ec8f Mon Sep 17 00:00:00 2001 From: Maksim Kurbatov <94808996+yungwine@users.noreply.github.com> Date: Tue, 8 Oct 2024 19:13:39 +0400 Subject: [PATCH 35/56] fix fine warning for testnet --- modules/validator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/validator.py b/modules/validator.py index 8efbc904..3f4e56d5 100644 --- a/modules/validator.py +++ b/modules/validator.py @@ -55,7 +55,7 @@ def check_efficiency(self, args): color_print(f"Previous round time: {{yellow}}from {start_time} to {end_time}{{endc}}") if validator: if validator.is_masterchain == False: - print("Validator index is greater than 100 in the previous round - no efficiency data.") + print(f"Validator index is greater than {config32['mainValidators']} in the previous round - no efficiency data.") elif validator.get('efficiency') is None: print('Failed to get efficiency for the previous round') else: @@ -75,7 +75,7 @@ def check_efficiency(self, args): color_print(f"Current round time: {{green}}from {start_time} to {end_time}{{endc}}") if validator: if validator.is_masterchain == False: - print("Validator index is greater than 100 in the current round - no efficiency data.") + print(f"Validator index is greater than {config34['mainValidators']} in the previous round - no efficiency data.") elif (time.time() - config34.startWorkTime) / (config34.endWorkTime - config34.startWorkTime) < 0.8: print("The validation round has started recently, there is not enough data yet. " "The efficiency evaluation will become more accurate towards the end of the round.") From 4a45167d35bae7f98ffae823e7a22ce727779091 Mon Sep 17 00:00:00 2001 From: Maksim Kurbatov <94808996+yungwine@users.noreply.github.com> Date: Tue, 8 Oct 2024 19:14:39 +0400 Subject: [PATCH 36/56] fix typo --- modules/validator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/validator.py b/modules/validator.py index 3f4e56d5..3ce5cd6f 100644 --- a/modules/validator.py +++ b/modules/validator.py @@ -75,7 +75,7 @@ def check_efficiency(self, args): color_print(f"Current round time: {{green}}from {start_time} to {end_time}{{endc}}") if validator: if validator.is_masterchain == False: - print(f"Validator index is greater than {config34['mainValidators']} in the previous round - no efficiency data.") + print(f"Validator index is greater than {config34['mainValidators']} in the current round - no efficiency data.") elif (time.time() - config34.startWorkTime) / (config34.endWorkTime - config34.startWorkTime) < 0.8: print("The validation round has started recently, there is not enough data yet. " "The efficiency evaluation will become more accurate towards the end of the round.") From e0bb941200059c897143978aef71ca92bc487c9d Mon Sep 17 00:00:00 2001 From: yungwine Date: Tue, 8 Oct 2024 19:56:21 +0400 Subject: [PATCH 37/56] add alert if validator was slashed --- modules/alert_bot.py | 13 +++++++++++++ modules/validator.py | 11 +++++++++++ mytonctrl/mytonctrl.py | 15 ++++++++------- mytonctrl/resources/translate.json | 6 +++--- 4 files changed, 35 insertions(+), 10 deletions(-) diff --git a/modules/alert_bot.py b/modules/alert_bot.py index 2e031357..a513d545 100644 --- a/modules/alert_bot.py +++ b/modules/alert_bot.py @@ -61,6 +61,11 @@ class Alert: "Validator has not created any blocks in the last 6 hours.", 6 * HOUR ), + "validator_slashed": Alert( + "high", + "Validator has been slashed in previous round for {amount} TON", + 0 + ), } @@ -173,6 +178,13 @@ def check_zero_blocks_created(self): return self.send_alert("zero_block_created") + def check_slashed(self): + if not self.ton.using_validator(): + return + c = self.validator_module.get_my_complaint() + if c is not None: + self.send_alert("validator_slashed", amount=int(c['suggestedFine'])) + def check_status(self): if not self.inited: self.init() @@ -183,6 +195,7 @@ def check_status(self): self.local.try_function(self.check_validator_working) self.local.try_function(self.check_zero_blocks_created) self.local.try_function(self.check_sync) + self.local.try_function(self.check_slashed) def add_console_commands(self, console): ... diff --git a/modules/validator.py b/modules/validator.py index 3ce5cd6f..39317924 100644 --- a/modules/validator.py +++ b/modules/validator.py @@ -91,6 +91,17 @@ def check_efficiency(self, args): print("Couldn't find this validator in the current round") # end define + def get_my_complaint(self): + config32 = self.ton.GetConfig32() + save_complaints = self.ton.GetSaveComplaints() + complaints = save_complaints.get(str(config32['startWorkTime'])) + if not complaints: + return + for c in complaints.values(): + if c["adnl"] == self.ton.GetAdnlAddr() and c["isPassed"]: + return c + # end define + def add_console_commands(self, console): console.AddItem("vo", self.vote_offer, self.local.translate("vo_cmd")) console.AddItem("ve", self.vote_election_entry, self.local.translate("ve_cmd")) diff --git a/mytonctrl/mytonctrl.py b/mytonctrl/mytonctrl.py index 1020c5c9..07753126 100755 --- a/mytonctrl/mytonctrl.py +++ b/mytonctrl/mytonctrl.py @@ -480,14 +480,15 @@ def check_tg_channel(local, ton): #end difine def check_slashed(local, ton): - config32 = ton.GetConfig32() - save_complaints = ton.GetSaveComplaints() - complaints = save_complaints.get(str(config32['startWorkTime'])) - if not complaints: + validator_status = ton.GetValidatorStatus() + if not ton.using_validator() or not validator_status.is_working or validator_status.out_of_sync >= 20: return - for c in complaints.values(): - if c["adnl"] == ton.GetAdnlAddr() and c["isPassed"]: - print_warning(local, "slashed_warning") + from modules import ValidatorModule + validator_module = ValidatorModule(ton, local) + c = validator_module.get_my_complaint() + if c: + warning = local.translate("slashed_warning").format(int(c['suggestedFine'])) + print_warning(local, warning) #end define def check_adnl(local, ton): diff --git a/mytonctrl/resources/translate.json b/mytonctrl/resources/translate.json index 632158b4..60e1501a 100644 --- a/mytonctrl/resources/translate.json +++ b/mytonctrl/resources/translate.json @@ -445,9 +445,9 @@ "zh_TW": "{red}錯誤 - 驗證器的 UDP 端口無法從外部訪問.{endc}" }, "slashed_warning": { - "en": "{red}You were fined by 101 TON for low efficiency in the previous round.{endc}", - "ru": "{red}Вы были оштрафованы на 101 TON за низкую эффективность в предыдущем раунде.{endc}", - "zh_TW": "{red}您因上一輪效率低而被罰款 101 TON。{endc}" + "en": "{red}You were fined by {0} for low efficiency in the previous round.{endc}", + "ru": "{red}Вы были оштрафованы на {0} за низкую эффективность в предыдущем раунде.{endc}", + "zh_TW": "{red}您因上一輪效率低而被罰款 {0}。{endc}" }, "add_custom_overlay_cmd": { "en": "Add custom overlay", From d3c456ca33da2802e82ff36ac941a94da114e52d Mon Sep 17 00:00:00 2001 From: yungwine Date: Wed, 9 Oct 2024 13:35:48 +0400 Subject: [PATCH 38/56] rm telebot dependency for alert-bot mode --- modules/alert_bot.py | 20 ++++++++++++-------- mytoncore/mytoncore.py | 3 --- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/modules/alert_bot.py b/modules/alert_bot.py index a513d545..f3faca8f 100644 --- a/modules/alert_bot.py +++ b/modules/alert_bot.py @@ -1,5 +1,6 @@ import dataclasses import time +import requests from modules.module import MtcModule from mytoncore import get_hostname @@ -64,7 +65,7 @@ class Alert: "validator_slashed": Alert( "high", "Validator has been slashed in previous round for {amount} TON", - 0 + 10*HOUR ), } @@ -79,15 +80,20 @@ def __init__(self, ton, local, *args, **kwargs): self.validator_module = None self.inited = False self.hostname = None - self.bot = None self.token = self.ton.local.db.get("BotToken") self.chat_id = self.ton.local.db.get("ChatId") def send_message(self, text: str): - if self.bot is not None: - self.bot.send_message(self.chat_id, text) - else: - raise Exception("send_message error: bot is not initialized") + if self.token is None: + raise Exception("send_message error: token is not initialized") + request_url = f"https://api.telegram.org/bot{self.token}/sendMessage" + data = {'chat_id': self.chat_id, 'text': text, 'parse_mode': 'HTML'} + response = requests.post(request_url, data=data, timeout=3) + if response.status_code != 200: + raise Exception(f"send_message error: {response.text}") + response = response.json() + if not response['ok']: + raise Exception(f"send_message error: {response}") def send_alert(self, alert_name: str, *args, **kwargs): last_sent = self.get_alert_sent(alert_name) @@ -117,8 +123,6 @@ def init(self): raise Exception("BotToken or ChatId is not set") from modules.validator import ValidatorModule self.validator_module = ValidatorModule(self.ton, self.local) - import telebot - self.bot = telebot.TeleBot(self.token, parse_mode="HTML") self.hostname = get_hostname() self.inited = True diff --git a/mytoncore/mytoncore.py b/mytoncore/mytoncore.py index e6cfcd16..44975561 100644 --- a/mytoncore/mytoncore.py +++ b/mytoncore/mytoncore.py @@ -3049,9 +3049,6 @@ def check_enable_mode(self, name): if name == 'liquid-staking': from mytoninstaller.settings import enable_ton_http_api enable_ton_http_api(self.local) - if name == 'alert-bot': - args = ["pip", "install", "pytelegrambotapi==4.23.0"] - subprocess.run(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=10) def enable_mode(self, name): if name not in MODES: From 4df15f50630a8eaab6623b5ece3e658e8c7f0e27 Mon Sep 17 00:00:00 2001 From: yungwine Date: Wed, 9 Oct 2024 13:46:02 +0400 Subject: [PATCH 39/56] fix typo --- mytonctrl/resources/translate.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mytonctrl/resources/translate.json b/mytonctrl/resources/translate.json index 60e1501a..47be11b4 100644 --- a/mytonctrl/resources/translate.json +++ b/mytonctrl/resources/translate.json @@ -445,9 +445,9 @@ "zh_TW": "{red}錯誤 - 驗證器的 UDP 端口無法從外部訪問.{endc}" }, "slashed_warning": { - "en": "{red}You were fined by {0} for low efficiency in the previous round.{endc}", - "ru": "{red}Вы были оштрафованы на {0} за низкую эффективность в предыдущем раунде.{endc}", - "zh_TW": "{red}您因上一輪效率低而被罰款 {0}。{endc}" + "en": "{red}You were fined by {0} TON for low efficiency in the previous round.{endc}", + "ru": "{red}Вы были оштрафованы на {0} TON за низкую эффективность в предыдущем раунде.{endc}", + "zh_TW": "{red}您因上一輪效率低而被罰款 {0} TON。{endc}" }, "add_custom_overlay_cmd": { "en": "Add custom overlay", From b2e817ffacd31027115ec5cf49f375acbdc76c79 Mon Sep 17 00:00:00 2001 From: yungwine Date: Wed, 9 Oct 2024 20:55:13 +0400 Subject: [PATCH 40/56] fix GetValidatorsList --- mytoncore/mytoncore.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mytoncore/mytoncore.py b/mytoncore/mytoncore.py index 44975561..e7155502 100644 --- a/mytoncore/mytoncore.py +++ b/mytoncore/mytoncore.py @@ -2447,14 +2447,14 @@ def GetValidatorsList(self, past=False, fast=False, start=None, end=None): #end if config = self.GetConfig34() + if end is None: + timestamp = get_timestamp() + end = timestamp - 60 if start is None: if fast: start = end - 1000 else: start = config.get("startWorkTime") - if end is None: - timestamp = get_timestamp() - end = timestamp - 60 if past: config = self.GetConfig32() start = config.get("startWorkTime") From e12f9a08c7aad3df2358a433e80e3854194bb516 Mon Sep 17 00:00:00 2001 From: yungwine Date: Wed, 9 Oct 2024 21:14:47 +0400 Subject: [PATCH 41/56] fix buffering GetValidatorsList --- mytoncore/mytoncore.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mytoncore/mytoncore.py b/mytoncore/mytoncore.py index e7155502..88c3e873 100644 --- a/mytoncore/mytoncore.py +++ b/mytoncore/mytoncore.py @@ -2440,7 +2440,7 @@ def GetValidatorsLoad(self, start, end, saveCompFiles=False) -> dict: def GetValidatorsList(self, past=False, fast=False, start=None, end=None): # Get buffer - bname = "validatorsList" + str(past) + bname = "validatorsList" + str(past) + str(start) + str(end) buff = self.GetFunctionBuffer(bname, timeout=60) if buff: return buff From 101dd9c18120245a19376e66081399450f4b59ba Mon Sep 17 00:00:00 2001 From: yungwine Date: Wed, 9 Oct 2024 22:23:17 +0400 Subject: [PATCH 42/56] add constants to alerting --- modules/alert_bot.py | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/modules/alert_bot.py b/modules/alert_bot.py index f3faca8f..b01e706b 100644 --- a/modules/alert_bot.py +++ b/modules/alert_bot.py @@ -3,6 +3,7 @@ import requests from modules.module import MtcModule +from mypylib.mypylib import get_timestamp from mytoncore import get_hostname from mytonctrl.utils import timestamp2utcdatetime @@ -15,6 +16,8 @@ class Alert: HOUR = 3600 +VALIDATION_PERIOD = 65536 +FREEZE_PERIOD = 32768 ALERTS = { @@ -40,7 +43,7 @@ class Alert: "low_efficiency": Alert( "high", """Validator efficiency is low: {efficiency}%.""", - 12*HOUR + VALIDATION_PERIOD // 3 ), "out_of_sync": Alert( "critical", @@ -59,13 +62,13 @@ class Alert: ), "zero_block_created": Alert( "critical", - "Validator has not created any blocks in the last 6 hours.", - 6 * HOUR + "Validator has not created any blocks in the last {hours} hours.", + VALIDATION_PERIOD // 3 ), "validator_slashed": Alert( "high", "Validator has been slashed in previous round for {amount} TON", - 10*HOUR + FREEZE_PERIOD ), } @@ -116,6 +119,13 @@ def send_alert(self, alert_name: str, *args, **kwargs): self.send_message(text) self.set_alert_sent(alert_name) + def set_global_vars(self): + # set global vars for correct alerts timeouts for current network + config15 = self.ton.GetConfig15() + global VALIDATION_PERIOD, FREEZE_PERIOD + VALIDATION_PERIOD = config15["validatorsElectedFor"] + FREEZE_PERIOD = config15["stakeHeldFor"] + def init(self): if not self.ton.get_mode_value('alert-bot'): return @@ -124,6 +134,7 @@ def init(self): from modules.validator import ValidatorModule self.validator_module = ValidatorModule(self.ton, self.local) self.hostname = get_hostname() + self.set_global_vars() self.inited = True def set_alert_sent(self, alert_name: str): @@ -148,7 +159,7 @@ def check_validator_wallet_balance(self): return validator_wallet = self.ton.GetValidatorWallet() validator_account = self.ton.GetAccount(validator_wallet.addrB64) - if validator_account.balance < 50: + if validator_account.balance < 10: self.send_alert("low_wallet_balance", wallet=validator_wallet.addrB64, balance=validator_account.balance) def check_efficiency(self): @@ -176,11 +187,14 @@ def check_sync(self): def check_zero_blocks_created(self): if not self.ton.using_validator(): return - validators = self.ton.GetValidatorsList(start=-6*HOUR, end=-60) + ts = get_timestamp() + period = VALIDATION_PERIOD // 3 # 6h for mainnet, 40m for testnet + start, end = ts - period, ts - 60 + validators = self.ton.GetValidatorsList(start=start, end=end) validator = self.validator_module.find_myself(validators) if validator is None or validator.blocks_created > 0: return - self.send_alert("zero_block_created") + self.send_alert("zero_block_created", hours=period // 3600) def check_slashed(self): if not self.ton.using_validator(): From 8375ed072f2dc7bce9e104e1e4922d2af1297028 Mon Sep 17 00:00:00 2001 From: yungwine Date: Wed, 9 Oct 2024 22:29:36 +0400 Subject: [PATCH 43/56] fix check_zero_blocks_created --- modules/alert_bot.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/alert_bot.py b/modules/alert_bot.py index b01e706b..21ed63f1 100644 --- a/modules/alert_bot.py +++ b/modules/alert_bot.py @@ -190,6 +190,9 @@ def check_zero_blocks_created(self): ts = get_timestamp() period = VALIDATION_PERIOD // 3 # 6h for mainnet, 40m for testnet start, end = ts - period, ts - 60 + config34 = self.ton.GetConfig34() + if start < config34.startWorkTime: # round started recently + return validators = self.ton.GetValidatorsList(start=start, end=end) validator = self.validator_module.find_myself(validators) if validator is None or validator.blocks_created > 0: From de67b55d8e9afd63ef3ab4fd5aff31e67f4c0bf6 Mon Sep 17 00:00:00 2001 From: yungwine Date: Thu, 10 Oct 2024 15:15:30 +0400 Subject: [PATCH 44/56] return git_pool_data cmd --- modules/utilities.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/modules/utilities.py b/modules/utilities.py index 958a564a..f659b65a 100644 --- a/modules/utilities.py +++ b/modules/utilities.py @@ -335,6 +335,21 @@ def print_validator_list(self, args): print_table(table) # end define + def get_pool_data(self, args): + try: + pool_name = args[0] + except: + color_print("{red}Bad args. Usage:{endc} get_pool_data ") + return + if self.ton.IsAddr(pool_name): + pool_addr = pool_name + else: + pool = self.ton.GetLocalPool(pool_name) + pool_addr = pool.addrB64 + pool_data = self.ton.GetPoolData(pool_addr) + print(json.dumps(pool_data, indent=4)) + # end define + def add_console_commands(self, console): console.AddItem("vas", self.view_account_status, self.local.translate("vas_cmd")) console.AddItem("vah", self.view_account_history, self.local.translate("vah_cmd")) @@ -350,3 +365,4 @@ def add_console_commands(self, console): console.AddItem("vl", self.print_validator_list, self.local.translate("vl_cmd")) console.AddItem("cl", self.print_complaints_list, self.local.translate("cl_cmd")) + console.AddItem("get_pool_data", self.get_pool_data, self.local.translate("get_pool_data_cmd")) From 09e1e81260cf57305f64377b66f8225dbd157c65 Mon Sep 17 00:00:00 2001 From: yungwine Date: Thu, 10 Oct 2024 16:20:53 +0400 Subject: [PATCH 45/56] rm 'Next alert...' from alerting message --- modules/alert_bot.py | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/alert_bot.py b/modules/alert_bot.py index 21ed63f1..8a748276 100644 --- a/modules/alert_bot.py +++ b/modules/alert_bot.py @@ -110,7 +110,6 @@ def send_alert(self, alert_name: str, *args, **kwargs): Hostname: {self.hostname} Time: {time_} ({int(time.time())}) Severity: {alert.severity} -Next alert of this type not earlier than: {max(alert.timeout, 1000)} sec Alert text:
{alert.text.format(*args, **kwargs)}
From a27fcec182257a39bcd93b0a0336bcdbc11e6a2d Mon Sep 17 00:00:00 2001 From: yungwine Date: Thu, 10 Oct 2024 16:26:57 +0400 Subject: [PATCH 46/56] add check_adnl_connection_failed --- modules/alert_bot.py | 8 ++++++++ modules/utilities.py | 34 ++++++++++++++++++++++++++++++++-- mytonctrl/mytonctrl.py | 28 +++------------------------- 3 files changed, 43 insertions(+), 27 deletions(-) diff --git a/modules/alert_bot.py b/modules/alert_bot.py index 8a748276..339f0fcd 100644 --- a/modules/alert_bot.py +++ b/modules/alert_bot.py @@ -205,6 +205,13 @@ def check_slashed(self): if c is not None: self.send_alert("validator_slashed", amount=int(c['suggestedFine'])) + def check_adnl_connection_failed(self): + from modules.utilities import UtilitiesModule + utils_module = UtilitiesModule(self.ton, self.local) + ok, error = utils_module.check_adnl_connection() + if not ok: + self.send_alert("adnl_connection_failed") + def check_status(self): if not self.inited: self.init() @@ -216,6 +223,7 @@ def check_status(self): self.local.try_function(self.check_zero_blocks_created) self.local.try_function(self.check_sync) self.local.try_function(self.check_slashed) + self.local.try_function(self.check_adnl_connection_failed) def add_console_commands(self, console): ... diff --git a/modules/utilities.py b/modules/utilities.py index 958a564a..a19ea581 100644 --- a/modules/utilities.py +++ b/modules/utilities.py @@ -1,9 +1,10 @@ -import base64 import json -import os +import random import subprocess import time +import requests + from mypylib.mypylib import color_print, print_table, color_text, timeago, bcolors from modules.module import MtcModule @@ -335,6 +336,35 @@ def print_validator_list(self, args): print_table(table) # end define + def check_adnl_connection(self): + telemetry = self.ton.local.db.get("sendTelemetry", False) + check_adnl = self.ton.local.db.get("checkAdnl", telemetry) + if not check_adnl: + return True, '' + self.local.add_log('Checking ADNL connection to local node', 'info') + hosts = ['45.129.96.53', '5.154.181.153', '2.56.126.137', '91.194.11.68', '45.12.134.214', '138.124.184.27', + '103.106.3.171'] + hosts = random.sample(hosts, k=3) + data = self.ton.get_local_adnl_data() + error = '' + ok = True + for host in hosts: + url = f'http://{host}/adnl_check' + try: + response = requests.post(url, json=data, timeout=5).json() + except Exception as e: + ok = False + error = f'{{red}}Failed to check ADNL connection to local node: {type(e)}: {e}{{endc}}' + continue + result = response.get("ok") + if result: + ok = True + break + if not result: + ok = False + error = f'{{red}}Failed to check ADNL connection to local node: {response.get("message")}{{endc}}' + return ok, error + def add_console_commands(self, console): console.AddItem("vas", self.view_account_status, self.local.translate("vas_cmd")) console.AddItem("vah", self.view_account_history, self.local.translate("vah_cmd")) diff --git a/mytonctrl/mytonctrl.py b/mytonctrl/mytonctrl.py index 07753126..535de65a 100755 --- a/mytonctrl/mytonctrl.py +++ b/mytonctrl/mytonctrl.py @@ -492,31 +492,9 @@ def check_slashed(local, ton): #end define def check_adnl(local, ton): - telemetry = ton.local.db.get("sendTelemetry", False) - check_adnl = ton.local.db.get("checkAdnl", telemetry) - local.add_log('Checking ADNL connection to local node', 'info') - if not check_adnl: - return - hosts = ['45.129.96.53', '5.154.181.153', '2.56.126.137', '91.194.11.68', '45.12.134.214', '138.124.184.27', '103.106.3.171'] - hosts = random.sample(hosts, k=3) - data = ton.get_local_adnl_data() - error = '' - ok = True - for host in hosts: - url = f'http://{host}/adnl_check' - try: - response = requests.post(url, json=data, timeout=5).json() - except Exception as e: - ok = False - error = f'{{red}}Failed to check ADNL connection to local node: {type(e)}: {e}{{endc}}' - continue - result = response.get("ok") - if result: - ok = True - break - if not result: - ok = False - error = f'{{red}}Failed to check ADNL connection to local node: {response.get("message")}{{endc}}' + from modules.utilities import UtilitiesModule + utils_module = UtilitiesModule(ton, local) + ok, error = utils_module.check_adnl_connection() if not ok: print_warning(local, error) #end define From ffd8c4175908a11822eb54b8f0f7ca546ac08231 Mon Sep 17 00:00:00 2001 From: yungwine Date: Thu, 10 Oct 2024 16:28:31 +0400 Subject: [PATCH 47/56] round hours in zero_block_created alert --- modules/alert_bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/alert_bot.py b/modules/alert_bot.py index 339f0fcd..4deda90c 100644 --- a/modules/alert_bot.py +++ b/modules/alert_bot.py @@ -196,7 +196,7 @@ def check_zero_blocks_created(self): validator = self.validator_module.find_myself(validators) if validator is None or validator.blocks_created > 0: return - self.send_alert("zero_block_created", hours=period // 3600) + self.send_alert("zero_block_created", hours=round(period // 3600, 1)) def check_slashed(self): if not self.ton.using_validator(): From fdde23b1055384db5e5c86a7441b779d30651b44 Mon Sep 17 00:00:00 2001 From: yungwine Date: Fri, 11 Oct 2024 12:30:41 +0400 Subject: [PATCH 48/56] fine non-master vals that created zero blocks --- mytoncore/mytoncore.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/mytoncore/mytoncore.py b/mytoncore/mytoncore.py index 3f3db256..127db692 100644 --- a/mytoncore/mytoncore.py +++ b/mytoncore/mytoncore.py @@ -1624,7 +1624,7 @@ def CreateWallet(self, name, workchain=0, version="v1", **kwargs): if os.path.isfile(wallet_path + ".pk") and "v3" not in version: self.local.add_log("CreateWallet error: Wallet already exists: " + name, "warning") else: - fift_args = self.get_new_wallet_fift_args(version, workchain=workchain, + fift_args = self.get_new_wallet_fift_args(version, workchain=workchain, wallet_path=wallet_path, subwallet=subwallet) result = self.fift.Run(fift_args) if "Creating new" not in result: @@ -1681,7 +1681,7 @@ def import_wallet_with_version(self, key, version, **kwargs): wallet_path = self.walletsDir + wallet_name with open(wallet_path + ".pk", 'wb') as file: file.write(pk_bytes) - fift_args = self.get_new_wallet_fift_args(version, workchain=workchain, + fift_args = self.get_new_wallet_fift_args(version, workchain=workchain, wallet_path=wallet_path, subwallet=subwallet) result = self.fift.Run(fift_args) if "Creating new" not in result: @@ -2310,6 +2310,7 @@ def get_valid_complaints(self, complaints: dict, election_id: int): continue exists = False + vload = None for item in validators_load.values(): if 'fileName' not in item: continue @@ -2319,15 +2320,16 @@ def get_valid_complaints(self, complaints: dict, election_id: int): pseudohash = pubkey + str(election_id) if pseudohash == complaint['pseudohash']: exists = True - vid = item['id'] + vload = item break if not exists: self.local.add_log(f"complaint {complaint['hash_hex']} declined: complaint info was not found, probably it's wrong", "info") continue - if vid >= config32['mainValidators']: - self.local.add_log(f"complaint {complaint['hash_hex']} declined: complaint created for non masterchain validator", "info") + if (vload["id"] >= config32['mainValidators'] and + vload["masterBlocksCreated"] + vload["workBlocksCreated"] > 0): + self.local.add_log(f"complaint {complaint['hash_hex']} declined: complaint created for non masterchain validator that created more than zero blocks", "info") continue # check complaint fine value @@ -2524,7 +2526,7 @@ def CheckValidators(self, start, end): pseudohash = pubkey + str(electionId) if pseudohash in valid_complaints or pseudohash in voted_complaints_pseudohashes: # do not create complaints that already created or voted by ourself continue - if item['id'] >= config['mainValidators']: # do not create complaints for non-masterchain validators + if item['id'] >= config['mainValidators'] and item["masterBlocksCreated"] + item["workBlocksCreated"] > 0: # create complaints for non-masterchain validators only if they created 0 blocks continue # Create complaint fileName = self.remove_proofs_from_complaint(fileName) From b47ab237d3cbfd9b4152e8d35e7c960055b1de2d Mon Sep 17 00:00:00 2001 From: yungwine Date: Mon, 14 Oct 2024 19:59:38 +0400 Subject: [PATCH 49/56] add en(dis)bling alerts --- modules/alert_bot.py | 53 ++++++++++++++++++++++++++---- mytoncore/mytoncore.py | 3 ++ mytonctrl/mytonctrl.py | 5 +++ mytonctrl/resources/translate.json | 15 +++++++++ 4 files changed, 69 insertions(+), 7 deletions(-) diff --git a/modules/alert_bot.py b/modules/alert_bot.py index 4deda90c..8756a9c9 100644 --- a/modules/alert_bot.py +++ b/modules/alert_bot.py @@ -3,7 +3,7 @@ import requests from modules.module import MtcModule -from mypylib.mypylib import get_timestamp +from mypylib.mypylib import get_timestamp, print_table, color_print from mytoncore import get_hostname from mytonctrl.utils import timestamp2utcdatetime @@ -99,6 +99,8 @@ def send_message(self, text: str): raise Exception(f"send_message error: {response}") def send_alert(self, alert_name: str, *args, **kwargs): + if not self.alert_is_enabled(alert_name): + return last_sent = self.get_alert_sent(alert_name) time_ = timestamp2utcdatetime(int(time.time())) alert = ALERTS.get(alert_name) @@ -136,15 +138,50 @@ def init(self): self.set_global_vars() self.inited = True - def set_alert_sent(self, alert_name: str): + def get_alert_from_db(self, alert_name: str): if 'alerts' not in self.ton.local.db: self.ton.local.db['alerts'] = {} - self.ton.local.db['alerts'][alert_name] = int(time.time()) + if alert_name not in self.ton.local.db['alerts']: + self.ton.local.db['alerts'][alert_name] = {'sent': 0, 'enabled': True} + return self.ton.local.db['alerts'][alert_name] + + def set_alert_sent(self, alert_name: str): + alert = self.get_alert_from_db(alert_name) + alert['sent'] = int(time.time()) def get_alert_sent(self, alert_name: str): - if 'alerts' not in self.ton.local.db: - return 0 - return self.ton.local.db['alerts'].get(alert_name, 0) + alert = self.get_alert_from_db(alert_name) + return alert.get('sent', 0) + + def alert_is_enabled(self, alert_name: str): + alert = self.get_alert_from_db(alert_name) + return alert.get('enabled', True) # default is True + + def set_alert_enabled(self, alert_name: str, enabled: bool): + alert = self.get_alert_from_db(alert_name) + alert['enabled'] = enabled + self.ton.local.save() + + def enable_alert(self, args): + if len(args) != 1: + raise Exception("Usage: enable_alert ") + alert_name = args[0] + self.set_alert_enabled(alert_name, True) + color_print("enable_alert - {green}OK{endc}") + + def disable_alert(self, args): + if len(args) != 1: + raise Exception("Usage: disable_alert ") + alert_name = args[0] + self.set_alert_enabled(alert_name, False) + color_print("disable_alert - {green}OK{endc}") + + def print_alerts(self, args): + table = [['Name', 'Enabled', 'Last sent']] + for alert_name in ALERTS: + alert = self.get_alert_from_db(alert_name) + table.append([alert_name, alert['enabled'], alert['sent']]) + print_table(table) def check_db_usage(self): usage = self.ton.GetDbUsage() @@ -226,4 +263,6 @@ def check_status(self): self.local.try_function(self.check_adnl_connection_failed) def add_console_commands(self, console): - ... + console.AddItem("enable_alert", self.enable_alert, self.local.translate("enable_alert_cmd")) + console.AddItem("disable_alert", self.disable_alert, self.local.translate("disable_alert_cmd")) + console.AddItem("list_alerts", self.print_alerts, self.local.translate("list_alerts_cmd")) diff --git a/mytoncore/mytoncore.py b/mytoncore/mytoncore.py index 88c3e873..2b7e2119 100644 --- a/mytoncore/mytoncore.py +++ b/mytoncore/mytoncore.py @@ -3089,6 +3089,9 @@ def using_validator(self): def using_liteserver(self): return self.get_mode_value('liteserver') + def using_alert_bot(self): + return self.get_mode_value('alert-bot') + def Tlb2Json(self, text): # Заменить скобки start = 0 diff --git a/mytonctrl/mytonctrl.py b/mytonctrl/mytonctrl.py index 535de65a..44a868e5 100755 --- a/mytonctrl/mytonctrl.py +++ b/mytonctrl/mytonctrl.py @@ -135,6 +135,11 @@ def inject_globals(func): module = ControllerModule(ton, local) module.add_console_commands(console) + if ton.using_alert_bot(): + from modules.alert_bot import AlertBotModule + module = AlertBotModule(ton, local) + module.add_console_commands(console) + console.AddItem("cleanup", inject_globals(cleanup_validator_db), local.translate("cleanup_cmd")) console.AddItem("benchmark", inject_globals(run_benchmark), local.translate("benchmark_cmd")) # console.AddItem("activate_ton_storage_provider", inject_globals(activate_ton_storage_provider), local.translate("activate_ton_storage_provider_cmd")) diff --git a/mytonctrl/resources/translate.json b/mytonctrl/resources/translate.json index 47be11b4..2a5ae89e 100644 --- a/mytonctrl/resources/translate.json +++ b/mytonctrl/resources/translate.json @@ -464,6 +464,21 @@ "ru": "Удалить пользовательский оверлей", "zh_TW": "刪除自定義覆蓋" }, + "enable_alert_cmd": { + "en": "Enable specific Telegram Bot alert", + "ru": "Включить определенное оповещение через Telegram Bot", + "zh_TW": "啟用特定的 Telegram Bot 警報" + }, + "disable_alert_cmd": { + "en": "Disable specific Telegram Bot alert", + "ru": "Отключить определенное оповещение через Telegram Bot", + "zh_TW": "禁用特定的 Telegram Bot 警報" + }, + "list_alerts_cmd": { + "en": "List all available Telegram Bot alerts", + "ru": "Список всех доступных оповещений через Telegram Bot", + "zh_TW": "列出所有可用的 Telegram Bot 警報" + }, "cleanup_cmd": { "en": "Clean node old logs and temp files", "ru": "Очистить старые логи и временные файлы ноды", From bf84a530c0b94e6097d0b574cc886d9767ca8765 Mon Sep 17 00:00:00 2001 From: yungwine Date: Thu, 17 Oct 2024 21:40:44 +0400 Subject: [PATCH 50/56] add test_alert cmd --- modules/alert_bot.py | 6 ++++++ mytonctrl/resources/translate.json | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/modules/alert_bot.py b/modules/alert_bot.py index 8756a9c9..5ba4d99a 100644 --- a/modules/alert_bot.py +++ b/modules/alert_bot.py @@ -89,6 +89,8 @@ def __init__(self, ton, local, *args, **kwargs): def send_message(self, text: str): if self.token is None: raise Exception("send_message error: token is not initialized") + if self.chat_id is None: + raise Exception("send_message error: chat_id is not initialized") request_url = f"https://api.telegram.org/bot{self.token}/sendMessage" data = {'chat_id': self.chat_id, 'text': text, 'parse_mode': 'HTML'} response = requests.post(request_url, data=data, timeout=3) @@ -183,6 +185,9 @@ def print_alerts(self, args): table.append([alert_name, alert['enabled'], alert['sent']]) print_table(table) + def test_alert(self, args): + self.send_message('Test alert') + def check_db_usage(self): usage = self.ton.GetDbUsage() if usage > 95: @@ -266,3 +271,4 @@ def add_console_commands(self, console): console.AddItem("enable_alert", self.enable_alert, self.local.translate("enable_alert_cmd")) console.AddItem("disable_alert", self.disable_alert, self.local.translate("disable_alert_cmd")) console.AddItem("list_alerts", self.print_alerts, self.local.translate("list_alerts_cmd")) + console.AddItem("test_alert", self.test_alert, self.local.translate("test_alert_cmd")) diff --git a/mytonctrl/resources/translate.json b/mytonctrl/resources/translate.json index 2a5ae89e..929eea8a 100644 --- a/mytonctrl/resources/translate.json +++ b/mytonctrl/resources/translate.json @@ -479,6 +479,11 @@ "ru": "Список всех доступных оповещений через Telegram Bot", "zh_TW": "列出所有可用的 Telegram Bot 警報" }, + "test_alert_cmd": { + "en": "Send test alert via Telegram Bot", + "ru": "Отправить тестовое оповещение через Telegram Bot", + "zh_TW": "通過 Telegram Bot 發送測試警報" + }, "cleanup_cmd": { "en": "Clean node old logs and temp files", "ru": "Очистить старые логи и временные файлы ноды", From a4c9fb8a1e79ce45c1861e7769a97baecdb376fe Mon Sep 17 00:00:00 2001 From: Igroman787 <27614297+igroman787@users.noreply.github.com> Date: Mon, 21 Oct 2024 20:53:18 +0300 Subject: [PATCH 51/56] bugfix --- mytonctrl/mytonctrl.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mytonctrl/mytonctrl.py b/mytonctrl/mytonctrl.py index 44a868e5..4b3dd433 100755 --- a/mytonctrl/mytonctrl.py +++ b/mytonctrl/mytonctrl.py @@ -527,6 +527,9 @@ def mode_status(ton, args): table = [["Name", "Status", "Description"]] for mode_name in modes: mode = get_mode(mode_name) + if mode is None: + color_print(f"{{red}}Mode {mode_name} not found{{endc}}") + continue status = color_text('{green}enabled{endc}' if modes[mode_name] else '{red}disabled{endc}') table.append([mode_name, status, mode.description]) print_table(table) From c51e00af6f16495c4040528635598fd7fdaba5c6 Mon Sep 17 00:00:00 2001 From: yungwine Date: Tue, 22 Oct 2024 18:06:32 +0400 Subject: [PATCH 52/56] fix low_efficiency alert --- modules/alert_bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/alert_bot.py b/modules/alert_bot.py index 5ba4d99a..6487bdaf 100644 --- a/modules/alert_bot.py +++ b/modules/alert_bot.py @@ -206,7 +206,7 @@ def check_validator_wallet_balance(self): def check_efficiency(self): if not self.ton.using_validator(): return - validator = self.validator_module.find_myself(self.ton.GetValidatorsList(fast=True)) + validator = self.validator_module.find_myself(self.ton.GetValidatorsList()) if validator is None or validator.is_masterchain is False or validator.efficiency is None: return config34 = self.ton.GetConfig34() From 59f2f379cecfaa1715600a21236aa86e9e72698c Mon Sep 17 00:00:00 2001 From: yungwine Date: Tue, 22 Oct 2024 18:22:32 +0400 Subject: [PATCH 53/56] print efficiency for non-master validators if its 0 --- modules/alert_bot.py | 5 ++++- modules/validator.py | 8 ++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/modules/alert_bot.py b/modules/alert_bot.py index 6487bdaf..09ec0753 100644 --- a/modules/alert_bot.py +++ b/modules/alert_bot.py @@ -207,11 +207,14 @@ def check_efficiency(self): if not self.ton.using_validator(): return validator = self.validator_module.find_myself(self.ton.GetValidatorsList()) - if validator is None or validator.is_masterchain is False or validator.efficiency is None: + if validator is None or validator.efficiency is None: return config34 = self.ton.GetConfig34() if (time.time() - config34.startWorkTime) / (config34.endWorkTime - config34.startWorkTime) < 0.8: return # less than 80% of round passed + if validator.is_masterchain is False: + if validator.efficiency != 0: + return if validator.efficiency < 90: self.send_alert("low_efficiency", efficiency=validator.efficiency) diff --git a/modules/validator.py b/modules/validator.py index 39317924..a0fdfa9c 100644 --- a/modules/validator.py +++ b/modules/validator.py @@ -54,10 +54,10 @@ def check_efficiency(self, args): end_time = timestamp2utcdatetime(config32.endWorkTime) color_print(f"Previous round time: {{yellow}}from {start_time} to {end_time}{{endc}}") if validator: - if validator.is_masterchain == False: - print(f"Validator index is greater than {config32['mainValidators']} in the previous round - no efficiency data.") - elif validator.get('efficiency') is None: + if validator.get('efficiency') is None: print('Failed to get efficiency for the previous round') + elif validator.is_masterchain is False and validator.get('efficiency') != 0: + print(f"Validator index is greater than {config32['mainValidators']} in the previous round - no efficiency data.") else: efficiency = 100 if validator.efficiency > 100 else validator.efficiency color_efficiency = GetColorInt(efficiency, 90, logic="more", ending="%") @@ -74,7 +74,7 @@ def check_efficiency(self, args): end_time = timestamp2utcdatetime(int(get_timestamp())) color_print(f"Current round time: {{green}}from {start_time} to {end_time}{{endc}}") if validator: - if validator.is_masterchain == False: + if validator.is_masterchain is False and validator.efficiency != 0: print(f"Validator index is greater than {config34['mainValidators']} in the current round - no efficiency data.") elif (time.time() - config34.startWorkTime) / (config34.endWorkTime - config34.startWorkTime) < 0.8: print("The validation round has started recently, there is not enough data yet. " From f94a7dc72a9c72276a54a11082335af461cbbe02 Mon Sep 17 00:00:00 2001 From: yungwine Date: Thu, 24 Oct 2024 20:01:10 +0400 Subject: [PATCH 54/56] fix slashed_warning --- mytonctrl/resources/translate.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mytonctrl/resources/translate.json b/mytonctrl/resources/translate.json index 929eea8a..22915e59 100644 --- a/mytonctrl/resources/translate.json +++ b/mytonctrl/resources/translate.json @@ -445,9 +445,9 @@ "zh_TW": "{red}錯誤 - 驗證器的 UDP 端口無法從外部訪問.{endc}" }, "slashed_warning": { - "en": "{red}You were fined by {0} TON for low efficiency in the previous round.{endc}", - "ru": "{red}Вы были оштрафованы на {0} TON за низкую эффективность в предыдущем раунде.{endc}", - "zh_TW": "{red}您因上一輪效率低而被罰款 {0} TON。{endc}" + "en": "{{red}}You were fined by {0} TON for low efficiency in the previous round.{{endc}}", + "ru": "{{red}}Вы были оштрафованы на {0} TON за низкую эффективность в предыдущем раунде.{{endc}}", + "zh_TW": "{{red}}您因上一輪效率低而被罰款 {0} TON。{{endc}}" }, "add_custom_overlay_cmd": { "en": "Add custom overlay", From 79bf05b3809729bc22484d5f2e3f92204bc1f6f4 Mon Sep 17 00:00:00 2001 From: yungwine Date: Thu, 24 Oct 2024 20:03:25 +0400 Subject: [PATCH 55/56] update alerting thread time --- modules/alert_bot.py | 6 ++++++ mytoncore/functions.py | 5 ++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/modules/alert_bot.py b/modules/alert_bot.py index 09ec0753..c618dae0 100644 --- a/modules/alert_bot.py +++ b/modules/alert_bot.py @@ -85,6 +85,7 @@ def __init__(self, ton, local, *args, **kwargs): self.hostname = None self.token = self.ton.local.db.get("BotToken") self.chat_id = self.ton.local.db.get("ChatId") + self.last_db_check = None def send_message(self, text: str): if self.token is None: @@ -189,6 +190,9 @@ def test_alert(self, args): self.send_message('Test alert') def check_db_usage(self): + if time.time() - self.last_db_check < 600: + return + self.last_db_check = time.time() usage = self.ton.GetDbUsage() if usage > 95: self.send_alert("db_usage_95") @@ -258,6 +262,8 @@ def check_adnl_connection_failed(self): self.send_alert("adnl_connection_failed") def check_status(self): + if not self.ton.using_alert_bot(): + return if not self.inited: self.init() diff --git a/mytoncore/functions.py b/mytoncore/functions.py index 148c07d1..0dd136ff 100755 --- a/mytoncore/functions.py +++ b/mytoncore/functions.py @@ -569,9 +569,8 @@ def General(local): from modules.custom_overlays import CustomOverlayModule local.start_cycle(CustomOverlayModule(ton, local).custom_overlays, sec=60, args=()) - if ton.get_mode_value('alert-bot'): - from modules.alert_bot import AlertBotModule - local.start_cycle(AlertBotModule(ton, local).check_status, sec=1000, args=()) + from modules.alert_bot import AlertBotModule + local.start_cycle(AlertBotModule(ton, local).check_status, sec=60, args=()) thr_sleep() # end define From 2777801e06260dca0a1791b605321bdb10ddcfc8 Mon Sep 17 00:00:00 2001 From: Igroman787 <27614297+igroman787@users.noreply.github.com> Date: Sat, 26 Oct 2024 23:19:07 +0300 Subject: [PATCH 56/56] bugfix --- modules/alert_bot.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/alert_bot.py b/modules/alert_bot.py index c618dae0..e9dafed3 100644 --- a/modules/alert_bot.py +++ b/modules/alert_bot.py @@ -83,9 +83,9 @@ def __init__(self, ton, local, *args, **kwargs): self.validator_module = None self.inited = False self.hostname = None - self.token = self.ton.local.db.get("BotToken") - self.chat_id = self.ton.local.db.get("ChatId") - self.last_db_check = None + self.token = None + self.chat_id = None + self.last_db_check = 0 def send_message(self, text: str): if self.token is None: @@ -133,6 +133,8 @@ def set_global_vars(self): def init(self): if not self.ton.get_mode_value('alert-bot'): return + self.token = self.ton.local.db.get("BotToken") + self.chat_id = self.ton.local.db.get("ChatId") if self.token is None or self.chat_id is None: raise Exception("BotToken or ChatId is not set") from modules.validator import ValidatorModule