Skip to content

Commit 4ff8949

Browse files
authored
Merge pull request #239 from yungwine/mytonctrl2_dev
add custom overlays to mtc2.0
2 parents 99d8c92 + 981b5be commit 4ff8949

File tree

4 files changed

+204
-0
lines changed

4 files changed

+204
-0
lines changed

modules/custom_overlays.py

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
import json
2+
import requests
3+
4+
from mypylib.mypylib import color_print
5+
from modules.module import MtcModule
6+
from mytoncore.utils import hex2base64
7+
8+
9+
class CustomOverlayModule(MtcModule):
10+
11+
@staticmethod
12+
def parse_config(name: str, config: dict, vset: list = None):
13+
"""
14+
Converts config to validator-console friendly format
15+
:param name: custom overlay name
16+
:param config: config
17+
:param vset: list of validators adnl addresses, can be None if `@validators` not in config
18+
:return:
19+
"""
20+
result = {
21+
"name": name,
22+
"nodes": []
23+
}
24+
for k, v in config.items():
25+
if k == '@validators' and v:
26+
if vset is None:
27+
raise Exception("Validators set is not defined but @validators is in config")
28+
for v_adnl in vset:
29+
result["nodes"].append({
30+
"adnl_id": hex2base64(v_adnl),
31+
"msg_sender": False,
32+
})
33+
else:
34+
result["nodes"].append({
35+
"adnl_id": hex2base64(k),
36+
"msg_sender": v["msg_sender"],
37+
})
38+
if v["msg_sender"]:
39+
result["nodes"][-1]["msg_sender_priority"] = v["msg_sender_priority"]
40+
return result
41+
42+
def add_custom_overlay(self, args):
43+
if len(args) != 2:
44+
color_print("{red}Bad args. Usage:{endc} add_custom_overlay <name> <path_to_config>")
45+
return
46+
path = args[1]
47+
with open(path, 'r') as f:
48+
config = json.load(f)
49+
self.ton.set_custom_overlay(args[0], config)
50+
if '@validators' in config:
51+
print('Dynamic overlay will be added within 1 minute')
52+
else:
53+
result = self.add_custom_overlay_to_vc(self.parse_config(args[0], config))
54+
if not result:
55+
print('Failed to add overlay to validator console')
56+
color_print("add_custom_overlay - {red}ERROR{endc}")
57+
return
58+
color_print("add_custom_overlay - {green}OK{endc}")
59+
60+
def list_custom_overlays(self, args):
61+
if not self.ton.get_custom_overlays():
62+
color_print("{red}No custom overlays{endc}")
63+
return
64+
for k, v in self.ton.get_custom_overlays().items():
65+
color_print(f"Custom overlay {{bold}}{k}{{endc}}:")
66+
print(json.dumps(v, indent=4))
67+
68+
def delete_custom_overlay(self, args):
69+
if len(args) != 1:
70+
color_print("{red}Bad args. Usage:{endc} delete_custom_overlay <name>")
71+
return
72+
if '@validators' in self.ton.get_custom_overlays().get(args[0], {}):
73+
self.ton.delete_custom_overlay(args[0])
74+
print('Dynamic overlay will be deleted within 1 minute')
75+
else:
76+
self.ton.delete_custom_overlay(args[0])
77+
result = self.delete_custom_overlay_from_vc(args[0])
78+
if not result:
79+
print('Failed to delete overlay from validator console')
80+
color_print("delete_custom_overlay - {red}ERROR{endc}")
81+
return
82+
color_print("delete_custom_overlay - {green}OK{endc}")
83+
84+
def check_node_eligible_for_custom_overlay(self, config: dict):
85+
vconfig = self.ton.GetValidatorConfig()
86+
my_adnls = vconfig.adnl
87+
node_adnls = [i["adnl_id"] for i in config["nodes"]]
88+
for adnl in my_adnls:
89+
if adnl.id in node_adnls:
90+
return True
91+
return False
92+
93+
def delete_custom_overlay_from_vc(self, name: str):
94+
result = self.ton.validatorConsole.Run(f"delcustomoverlay {name}")
95+
return 'success' in result
96+
97+
def add_custom_overlay_to_vc(self, config: dict):
98+
if not self.check_node_eligible_for_custom_overlay(config):
99+
self.ton.local.add_log(f"Node has no adnl address required for custom overlay {config.get('name')}", "debug")
100+
return False
101+
self.ton.local.add_log(f"Adding custom overlay {config.get('name')}", "debug")
102+
path = self.ton.tempDir + f'/custom_overlay_{config["name"]}.json'
103+
with open(path, 'w') as f:
104+
json.dump(config, f)
105+
result = self.ton.validatorConsole.Run(f"addcustomoverlay {path}")
106+
return 'success' in result
107+
108+
def custom_overlays(self):
109+
config = self.get_default_custom_overlay()
110+
if config is not None:
111+
self.ton.set_custom_overlay('default', config)
112+
self.deploy_custom_overlays()
113+
114+
def deploy_custom_overlays(self):
115+
result = self.ton.validatorConsole.Run("showcustomoverlays")
116+
if 'unknown command' in result:
117+
return # node old version
118+
names = []
119+
for line in result.split('\n'):
120+
if line.startswith('Overlay'):
121+
names.append(line.split(' ')[1].replace('"', '').replace(':', ''))
122+
123+
config34 = self.ton.GetConfig34()
124+
current_el_id = config34['startWorkTime']
125+
current_vset = [i["adnlAddr"] for i in config34['validators']]
126+
127+
config36 = self.ton.GetConfig36()
128+
next_el_id = config36['startWorkTime'] if config36['validators'] else 0
129+
next_vset = [i["adnlAddr"] for i in config36['validators']]
130+
131+
for name in names:
132+
# check that overlay still exists in mtc db
133+
pure_name = name
134+
suffix = name.split('_')[-1]
135+
if suffix.startswith('elid') and suffix.split('elid')[-1].isdigit(): # probably election id
136+
pure_name = '_'.join(name.split('_')[:-1])
137+
el_id = int(suffix.split('elid')[-1])
138+
if el_id not in (current_el_id, next_el_id):
139+
self.ton.local.add_log(f"Overlay {name} is not in current or next election, deleting", "debug")
140+
self.delete_custom_overlay_from_vc(name) # delete overlay if election id is not in current or next election
141+
continue
142+
143+
if pure_name not in self.ton.get_custom_overlays():
144+
self.ton.local.add_log(f"Overlay {name} ({pure_name}) is not in mtc db, deleting", "debug")
145+
self.delete_custom_overlay_from_vc(name) # delete overlay if it's not in mtc db
146+
147+
for name, config in self.ton.get_custom_overlays().items():
148+
if name in names:
149+
continue
150+
if '@validators' in config:
151+
new_name = name + '_elid' + str(current_el_id)
152+
if new_name not in names:
153+
node_config = self.parse_config(new_name, config, current_vset)
154+
self.add_custom_overlay_to_vc(node_config)
155+
156+
if next_el_id != 0:
157+
new_name = name + '_elid' + str(next_el_id)
158+
if new_name not in names:
159+
node_config = self.parse_config(new_name, config, next_vset)
160+
self.add_custom_overlay_to_vc(node_config)
161+
else:
162+
node_config = self.parse_config(name, config)
163+
self.add_custom_overlay_to_vc(node_config)
164+
165+
def get_default_custom_overlay(self):
166+
if not self.ton.local.db.get('useDefaultCustomOverlays', True):
167+
return None
168+
network = self.ton.GetNetworkName()
169+
default_url = 'https://ton-blockchain.github.io/fallback_custom_overlays.json'
170+
url = self.ton.local.db.get('defaultCustomOverlaysUrl', default_url)
171+
resp = requests.get(url)
172+
if resp.status_code != 200:
173+
self.ton.local.add_log(f"Failed to get default custom overlays from {url}", "error")
174+
return None
175+
config = resp.json()
176+
return config.get(network)
177+
178+
def add_console_commands(self, console):
179+
console.AddItem("add_custom_overlay", self.add_custom_overlay, self.local.translate("add_custom_overlay_cmd"))
180+
console.AddItem("list_custom_overlays", self.list_custom_overlays, self.local.translate("list_custom_overlays_cmd"))
181+
console.AddItem("delete_custom_overlay", self.delete_custom_overlay, self.local.translate("delete_custom_overlay_cmd"))

mytoncore/functions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -690,6 +690,10 @@ def General(local):
690690
local.start_cycle(Telemetry, sec=60, args=(local, ton, ))
691691
local.start_cycle(OverlayTelemetry, sec=7200, args=(local, ton, ))
692692
local.start_cycle(ScanLiteServers, sec=60, args=(local, ton,))
693+
694+
from modules.custom_overlays import CustomOverlayModule
695+
local.start_cycle(CustomOverlayModule(ton, local).custom_overlays, sec=60, args=())
696+
693697
thr_sleep()
694698
# end define
695699

mytoncore/mytoncore.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3910,6 +3910,21 @@ def ControllerRecoverStake(self, controllerAddr):
39103910
self.local.add_log("ControllerRecoverStake completed")
39113911
#end define
39123912

3913+
def get_custom_overlays(self):
3914+
if 'custom_overlays' not in self.local.db:
3915+
self.local.db['custom_overlays'] = {}
3916+
return self.local.db['custom_overlays']
3917+
3918+
def set_custom_overlay(self, name: str, config: dict):
3919+
overlays = self.get_custom_overlays()
3920+
overlays[name] = config
3921+
self.local.save()
3922+
3923+
def delete_custom_overlay(self, name: str):
3924+
del self.local.db['custom_overlays'][name]
3925+
self.local.save()
3926+
3927+
39133928
def GetNetworkName(self):
39143929
data = self.local.read_db(self.liteClient.configPath)
39153930
mainnet_zero_state_root_hash = "F6OpKZKqvqeFp6CQmFomXNMfMj2EnaUSOXN+Mh+wVWk="

mytonctrl/mytonctrl.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ def inject_globals(func):
120120
#console.AddItem("ssoc", inject_globals(SignShardOverlayCert), local.translate("ssoc_cmd"))
121121
#console.AddItem("isoc", inject_globals(ImportShardOverlayCert), local.translate("isoc_cmd"))
122122

123+
from modules.custom_overlays import CustomOverlayModule
124+
module = CustomOverlayModule(ton, local)
125+
module.add_console_commands(console)
126+
123127
if ton.using_validator():
124128
from modules.validator import ValidatorModule
125129
module = ValidatorModule(ton, local)

0 commit comments

Comments
 (0)