Skip to content

Commit 7caf031

Browse files
authored
Merge pull request #403 from ton-blockchain/dev
Dev
2 parents e0ead70 + 5177df7 commit 7caf031

22 files changed

+325
-179
lines changed

modules/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ class Setting:
5858
'debug': Setting(None, False, 'Debug mtc console mode. Prints Traceback on errors'),
5959
'subscribe_tg_channel': Setting('validator', False, 'Disables warning about subscribing to the `TON STATUS` channel'),
6060
'BotToken': Setting('alert-bot', None, 'Alerting Telegram bot token'),
61-
'ChatId': Setting('alert-bot', None, 'Alerting Telegram chat id')
61+
'ChatId': Setting('alert-bot', None, 'Alerting Telegram chat id'),
62+
'auto_backup': Setting('validator', None, 'Make validator backup every election'),
63+
'auto_backup_path': Setting('validator', '/tmp/mytoncore/auto_backups/', 'Path to store auto-backups'),
6264
}
6365

6466

modules/alert_bot.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,12 @@ class Alert:
4848
"out_of_sync": Alert(
4949
"critical",
5050
"Node is out of sync on {sync} sec.",
51-
0
51+
300
5252
),
5353
"service_down": Alert(
5454
"critical",
5555
"validator.service is down.",
56-
0
56+
300
5757
),
5858
"adnl_connection_failed": Alert(
5959
"high",
@@ -83,6 +83,7 @@ def __init__(self, ton, local, *args, **kwargs):
8383
self.validator_module = None
8484
self.inited = False
8585
self.hostname = None
86+
self.ip = None
8687
self.token = None
8788
self.chat_id = None
8889
self.last_db_check = 0
@@ -113,6 +114,7 @@ def send_alert(self, alert_name: str, *args, **kwargs):
113114
❗️ <b>MyTonCtrl Alert {alert_name}</b> ❗️
114115
115116
Hostname: <code>{self.hostname}</code>
117+
Node IP: <code>{self.ip}</code>
116118
Time: <code>{time_}</code> (<code>{int(time.time())}</code>)
117119
Severity: <code>{alert.severity}</code>
118120
@@ -140,6 +142,7 @@ def init(self):
140142
from modules.validator import ValidatorModule
141143
self.validator_module = ValidatorModule(self.ton, self.local)
142144
self.hostname = get_hostname()
145+
self.ip = self.ton.get_validator_engine_ip()
143146
self.set_global_vars()
144147
self.inited = True
145148

@@ -189,6 +192,8 @@ def print_alerts(self, args):
189192
print_table(table)
190193

191194
def test_alert(self, args):
195+
if not self.inited:
196+
self.init()
192197
self.send_message('Test alert')
193198

194199
def check_db_usage(self):
@@ -204,6 +209,9 @@ def check_db_usage(self):
204209
def check_validator_wallet_balance(self):
205210
if not self.ton.using_validator():
206211
return
212+
validator_status = self.ton.GetValidatorStatus()
213+
if not validator_status.is_working or validator_status.out_of_sync >= 20:
214+
return
207215
validator_wallet = self.ton.GetValidatorWallet()
208216
validator_account = self.ton.GetAccount(validator_wallet.addrB64)
209217
if validator_account.balance < 10:

modules/backups.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import os
2+
import shutil
3+
import subprocess
4+
import time
5+
6+
import pkg_resources
7+
8+
from modules.module import MtcModule
9+
from mypylib.mypylib import color_print, ip2int, run_as_root, parse
10+
from mytoninstaller.config import get_own_ip
11+
12+
13+
class BackupModule(MtcModule):
14+
15+
def create_keyring(self, dir_name):
16+
keyring_dir = dir_name + '/keyring'
17+
self.ton.validatorConsole.Run(f'exportallprivatekeys {keyring_dir}')
18+
19+
def create_tmp_ton_dir(self):
20+
result = self.ton.validatorConsole.Run("getconfig")
21+
text = parse(result, "---------", "--------")
22+
dir_name = self.ton.tempDir + f'/ton_backup_{int(time.time() * 1000)}'
23+
dir_name_db = dir_name + '/db'
24+
os.makedirs(dir_name_db)
25+
with open(dir_name_db + '/config.json', 'w') as f:
26+
f.write(text)
27+
self.create_keyring(dir_name_db)
28+
return dir_name
29+
30+
def create_backup(self, args):
31+
if len(args) > 1:
32+
color_print("{red}Bad args. Usage:{endc} create_backup [filename]")
33+
return
34+
tmp_dir = self.create_tmp_ton_dir()
35+
command_args = ["-m", self.ton.local.buffer.my_work_dir, "-t", tmp_dir]
36+
if len(args) == 1:
37+
command_args += ["-d", args[0]]
38+
backup_script_path = pkg_resources.resource_filename('mytonctrl', 'scripts/create_backup.sh')
39+
process = subprocess.run(["bash", backup_script_path] + command_args, timeout=5)
40+
41+
if process.returncode == 0:
42+
color_print("create_backup - {green}OK{endc}")
43+
else:
44+
color_print("create_backup - {red}Error{endc}")
45+
shutil.rmtree(tmp_dir)
46+
return process.returncode
47+
# end define
48+
49+
def restore_backup(self, args):
50+
if len(args) == 0 or len(args) > 2:
51+
color_print("{red}Bad args. Usage:{endc} restore_backup <filename> [-y]")
52+
return
53+
if '-y' not in args:
54+
res = input(
55+
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]')
56+
if res.lower() != 'y':
57+
print('aborted.')
58+
return
59+
else:
60+
args.pop(args.index('-y'))
61+
print('Before proceeding, mtc will create a backup of current configuration.')
62+
try:
63+
self.create_backup([])
64+
except:
65+
color_print("{red}Could not create backup{endc}")
66+
67+
ip = str(ip2int(get_own_ip()))
68+
command_args = ["-m", self.ton.local.buffer.my_work_dir, "-n", args[0], "-i", ip]
69+
70+
restore_script_path = pkg_resources.resource_filename('mytonctrl', 'scripts/restore_backup.sh')
71+
if run_as_root(["bash", restore_script_path] + command_args) == 0:
72+
color_print("restore_backup - {green}OK{endc}")
73+
self.local.exit()
74+
else:
75+
color_print("restore_backup - {red}Error{endc}")
76+
# end define
77+
78+
def add_console_commands(self, console):
79+
console.AddItem("create_backup", self.create_backup, self.local.translate("create_backup_cmd"))
80+
console.AddItem("restore_backup", self.restore_backup, self.local.translate("restore_backup_cmd"))

modules/nominator_pool.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,45 @@ def update_validator_set(self, args):
8787
self.ton.PoolUpdateValidatorSet(pool_addr, wallet)
8888
color_print("UpdateValidatorSet - {green}OK{endc}")
8989

90+
def do_deposit_to_pool(self, pool_addr, amount):
91+
wallet = self.ton.GetValidatorWallet()
92+
bocPath = self.ton.local.buffer.my_temp_dir + wallet.name + "validator-deposit-query.boc"
93+
fiftScript = self.ton.contractsDir + "nominator-pool/func/validator-deposit.fif"
94+
args = [fiftScript, bocPath]
95+
result = self.ton.fift.Run(args)
96+
resultFilePath = self.ton.SignBocWithWallet(wallet, bocPath, pool_addr, amount)
97+
self.ton.SendFile(resultFilePath, wallet)
98+
99+
def deposit_to_pool(self, args):
100+
try:
101+
poll_addr = args[0]
102+
amount = float(args[1])
103+
except:
104+
color_print("{red}Bad args. Usage:{endc} deposit_to_pool <pool-addr> <amount>")
105+
return
106+
self.do_deposit_to_pool(poll_addr, amount)
107+
color_print("DepositToPool - {green}OK{endc}")
108+
109+
def do_withdraw_from_pool(self, pool_addr, amount):
110+
pool_data = self.ton.GetPoolData(pool_addr)
111+
if pool_data["state"] == 0:
112+
self.ton.WithdrawFromPoolProcess(pool_addr, amount)
113+
else:
114+
self.ton.PendWithdrawFromPool(pool_addr, amount)
115+
116+
def withdraw_from_pool(self, args):
117+
try:
118+
pool_addr = args[0]
119+
amount = float(args[1])
120+
except:
121+
color_print("{red}Bad args. Usage:{endc} withdraw_from_pool <pool-addr> <amount>")
122+
return
123+
self.do_withdraw_from_pool(pool_addr, amount)
124+
color_print("WithdrawFromPool - {green}OK{endc}")
125+
90126
def add_console_commands(self, console):
91127
console.AddItem("new_pool", self.new_pool, self.local.translate("new_pool_cmd"))
92128
console.AddItem("activate_pool", self.activate_pool, self.local.translate("activate_pool_cmd"))
93129
console.AddItem("update_validator_set", self.update_validator_set, self.local.translate("update_validator_set_cmd"))
130+
console.AddItem("withdraw_from_pool", self.withdraw_from_pool, self.local.translate("withdraw_from_pool_cmd"))
131+
console.AddItem("deposit_to_pool", self.deposit_to_pool, self.local.translate("deposit_to_pool_cmd"))

modules/pool.py

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -58,45 +58,7 @@ def check_download_pool_contract_scripts(self):
5858
if not os.path.isdir(contract_path):
5959
self.ton.DownloadContract("https://github.com/ton-blockchain/nominator-pool")
6060

61-
def do_deposit_to_pool(self, pool_addr, amount):
62-
wallet = self.ton.GetValidatorWallet()
63-
bocPath = self.ton.local.buffer.my_temp_dir + wallet.name + "validator-deposit-query.boc"
64-
fiftScript = self.ton.contractsDir + "nominator-pool/func/validator-deposit.fif"
65-
args = [fiftScript, bocPath]
66-
result = self.ton.fift.Run(args)
67-
resultFilePath = self.ton.SignBocWithWallet(wallet, bocPath, pool_addr, amount)
68-
self.ton.SendFile(resultFilePath, wallet)
69-
70-
def deposit_to_pool(self, args):
71-
try:
72-
poll_addr = args[0]
73-
amount = float(args[1])
74-
except:
75-
color_print("{red}Bad args. Usage:{endc} deposit_to_pool <pool-addr> <amount>")
76-
return
77-
self.do_deposit_to_pool(poll_addr, amount)
78-
color_print("DepositToPool - {green}OK{endc}")
79-
80-
def do_withdraw_from_pool(self, pool_addr, amount):
81-
pool_data = self.ton.GetPoolData(pool_addr)
82-
if pool_data["state"] == 0:
83-
self.ton.WithdrawFromPoolProcess(pool_addr, amount)
84-
else:
85-
self.ton.PendWithdrawFromPool(pool_addr, amount)
86-
87-
def withdraw_from_pool(self, args):
88-
try:
89-
pool_addr = args[0]
90-
amount = float(args[1])
91-
except:
92-
color_print("{red}Bad args. Usage:{endc} withdraw_from_pool <pool-addr> <amount>")
93-
return
94-
self.do_withdraw_from_pool(pool_addr, amount)
95-
color_print("WithdrawFromPool - {green}OK{endc}")
96-
9761
def add_console_commands(self, console):
9862
console.AddItem("pools_list", self.print_pools_list, self.local.translate("pools_list_cmd"))
9963
console.AddItem("delete_pool", self.delete_pool, self.local.translate("delete_pool_cmd"))
10064
console.AddItem("import_pool", self.import_pool, self.local.translate("import_pool_cmd"))
101-
console.AddItem("deposit_to_pool", self.deposit_to_pool, self.local.translate("deposit_to_pool_cmd"))
102-
console.AddItem("withdraw_from_pool", self.withdraw_from_pool, self.local.translate("withdraw_from_pool_cmd"))

modules/utilities.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,13 +313,14 @@ def print_validator_list(self, args):
313313
print(text)
314314
else:
315315
table = list()
316-
table += [["id", "ADNL", "Pubkey", "Wallet", "Efficiency", "Online"]]
316+
table += [["id", "ADNL", "Pubkey", "Wallet", "Stake", "Efficiency", "Online"]]
317317
for i, item in enumerate(data):
318318
adnl = item.get("adnlAddr")
319319
pubkey = item.get("pubkey")
320320
walletAddr = item.get("walletAddr")
321321
efficiency = item.get("efficiency")
322322
online = item.get("online")
323+
stake = item.get("stake")
323324
if "adnl" not in args:
324325
adnl = self.reduct(adnl)
325326
if "pubkey" not in args:
@@ -332,7 +333,7 @@ def print_validator_list(self, args):
332333
online = bcolors.green_text("true")
333334
if not online:
334335
online = bcolors.red_text("false")
335-
table += [[str(i), adnl, pubkey, walletAddr, efficiency, online]]
336+
table += [[str(i), adnl, pubkey, walletAddr, stake, efficiency, online]]
336337
print_table(table)
337338
# end define
338339

mytoncore/mytoncore.py

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
get_timestamp,
3131
timestamp2datetime,
3232
dec2hex,
33-
Dict
33+
Dict, int2ip
3434
)
3535

3636

@@ -115,6 +115,7 @@ def CheckConfigFile(self, fift, liteClient):
115115
self.local.add_log("Restoring the configuration file", "info")
116116
args = ["cp", backup_path, mconfig_path]
117117
subprocess.run(args)
118+
self.dbFile = mconfig_path
118119
self.Refresh()
119120
elif os.path.isfile(backup_path) == False:
120121
self.local.add_log("Create backup config file", "info")
@@ -1454,21 +1455,47 @@ def ElectionEntry(self, args=None):
14541455
self.local.add_log("ElectionEntry completed. Start work time: " + str(startWorkTime))
14551456

14561457
self.clear_tmp()
1458+
self.make_backup(startWorkTime)
14571459

14581460
#end define
14591461

1460-
def clear_tmp(self):
1462+
def clear_dir(self, dir_name):
14611463
start = time.time()
14621464
count = 0
14631465
week_ago = 60 * 60 * 24 * 7
1464-
dir = self.tempDir
1465-
for f in os.listdir(dir):
1466-
ts = os.path.getmtime(os.path.join(dir, f))
1466+
for f in os.listdir(dir_name):
1467+
ts = os.path.getmtime(os.path.join(dir_name, f))
14671468
if ts < time.time() - week_ago:
14681469
count += 1
1469-
os.remove(os.path.join(dir, f))
1470+
if os.path.isfile(os.path.join(dir_name, f)):
1471+
os.remove(os.path.join(dir_name, f))
1472+
self.local.add_log(f"Removed {count} old files from {dir_name} directory for {int(time.time() - start)} seconds", "info")
1473+
1474+
def clear_tmp(self):
1475+
self.clear_dir(self.tempDir)
14701476

1471-
self.local.add_log(f"Removed {count} old files from tmp dir for {int(time.time() - start)} seconds", "info")
1477+
def make_backup(self, election_id: str):
1478+
if not self.local.db.get("auto_backup"):
1479+
return
1480+
from modules.backups import BackupModule
1481+
module = BackupModule(self, self.local)
1482+
args = []
1483+
name = f"/mytonctrl_backup_elid{election_id}.zip"
1484+
backups_dir = self.tempDir + "/auto_backups"
1485+
if self.local.db.get("auto_backup_path"):
1486+
backups_dir = self.local.db.get("auto_backup_path")
1487+
os.makedirs(backups_dir, exist_ok=True)
1488+
args.append(backups_dir + name)
1489+
self.clear_dir(backups_dir)
1490+
exit_code = module.create_backup(args)
1491+
if exit_code != 0:
1492+
self.local.add_log(f"Backup failed with exit code {exit_code}", "error")
1493+
# try one more time
1494+
exit_code = module.create_backup(args)
1495+
if exit_code != 0:
1496+
self.local.add_log(f"Backup failed with exit code {exit_code}", "error")
1497+
if exit_code == 0:
1498+
self.local.add_log(f"Backup created successfully", "info")
14721499

14731500
def GetValidatorKeyByTime(self, startWorkTime, endWorkTime):
14741501
self.local.add_log("start GetValidatorKeyByTime function", "debug")
@@ -2490,6 +2517,8 @@ def GetValidatorsList(self, past=False, fast=False, start=None, end=None):
24902517
validator["efficiency"] = round(validator["wr"] * 100, 2)
24912518
if saveElectionEntries and adnlAddr in saveElectionEntries:
24922519
validator["walletAddr"] = saveElectionEntries[adnlAddr]["walletAddr"]
2520+
validator["stake"] = saveElectionEntries[adnlAddr].get("stake")
2521+
validator["stake"] = int(validator["stake"]) if validator["stake"] else None
24932522
#end for
24942523

24952524
# Set buffer
@@ -3791,6 +3820,13 @@ def GetNetworkName(self):
37913820
return "unknown"
37923821
#end define
37933822

3823+
def get_validator_engine_ip(self):
3824+
try:
3825+
config = self.GetValidatorConfig()
3826+
return int2ip(config['addrs'][0]['ip'])
3827+
except:
3828+
return None
3829+
37943830
def GetFunctionBuffer(self, name, timeout=10):
37953831
timestamp = get_timestamp()
37963832
buff = self.local.buffer.get(name)

0 commit comments

Comments
 (0)