Skip to content

Commit 46a6fb9

Browse files
committed
add collators settings to validator
1 parent 9013d6e commit 46a6fb9

File tree

2 files changed

+153
-1
lines changed

2 files changed

+153
-1
lines changed

modules/validator.py

Lines changed: 142 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
import json
12
import time
23

34
from modules.btc_teleport import BtcTeleportModule
45
from mypylib.mypylib import color_print, get_timestamp
56
from modules.module import MtcModule
6-
from mytonctrl.utils import timestamp2utcdatetime, GetColorInt
7+
from mytoncore import hex_shard_to_int, hex2b64
8+
from mytonctrl.utils import timestamp2utcdatetime, GetColorInt, pop_arg_from_args, is_hex
79

810

911
class ValidatorModule(MtcModule):
@@ -114,8 +116,147 @@ def check_enable(cls, ton: "MyTonCore"):
114116
f'Use `disable_mode liteserver` first.')
115117
BtcTeleportModule(ton, ton.local).init()
116118

119+
@staticmethod
120+
def _parse_collators_list(output: str) -> dict:
121+
result = {'shards': []}
122+
lines = output.strip().split('\n')
123+
current_shard = None
124+
for line in lines:
125+
line = line.strip()
126+
if line.startswith('Shard ('):
127+
shard_id = line.split('Shard (')[1].replace(',', ':').replace(')', '')
128+
current_shard = {
129+
'shard_id': hex_shard_to_int(shard_id),
130+
'self_collate': None,
131+
'select_mode': None,
132+
'collators': []
133+
}
134+
result['shards'].append(current_shard)
135+
elif line.startswith('Self collate = ') and current_shard:
136+
current_shard['self_collate'] = line.split('Self collate = ')[1] == 'true'
137+
elif line.startswith('Select mode = ') and current_shard:
138+
current_shard['select_mode'] = line.split('Select mode = ')[1]
139+
elif line.startswith('Collator ') and current_shard:
140+
collator_id = line.split('Collator ')[1]
141+
current_shard['collators'].append({'adnl_id': collator_id})
142+
return result
143+
144+
def get_collators_list(self):
145+
result = self.ton.validatorConsole.Run('show-collators-list')
146+
if 'collators list is empty' in result:
147+
return {}
148+
return self._parse_collators_list(result)
149+
150+
def set_collators_list(self, collators_list: dict):
151+
fname = self.ton.tempDir + '/collators_list.json'
152+
with open(fname, 'w') as f:
153+
f.write(json.dumps(collators_list))
154+
result = self.ton.validatorConsole.Run(f'set-collators-list {fname}')
155+
if 'success' not in result:
156+
raise Exception(f'Failed to set collators list: {result}')
157+
158+
def add_collator(self, args: list):
159+
if len(args) < 2:
160+
color_print("{red}Bad args. Usage:{endc} add_shard_collators <shard> <adnl> [--self-collate <true/false>] [--select-mode <random/ordered/round_robin>]")
161+
return
162+
shard = args[0]
163+
shard_id = hex_shard_to_int(shard)
164+
adnl = args[1]
165+
if is_hex(adnl):
166+
adnl = hex2b64(adnl)
167+
self_collate = pop_arg_from_args(args, '--self-collate') == 'true' if '--self-collate' in args else None
168+
select_mode = pop_arg_from_args(args, '--select-mode')
169+
if select_mode not in [None, 'random', 'ordered', 'round_robin']:
170+
color_print("{red}Bad args. Select mode must be one of: random, ordered, round_robin{endc}")
171+
return
172+
173+
collators_list = self.get_collators_list()
174+
if 'shards' not in collators_list:
175+
collators_list['shards'] = []
176+
177+
shard_exists = False
178+
for sh in collators_list['shards']:
179+
if sh['shard_id'] == shard_id:
180+
if any(c['adnl_id'] == adnl for c in sh['collators']):
181+
raise Exception(f"Сollator {adnl} already exists in this shard {shard_id}.")
182+
sh['collators'].append({'adnl_id': adnl})
183+
shard_exists = True
184+
if self_collate is not None:
185+
sh['self_collate'] = self_collate
186+
if select_mode is not None:
187+
sh['select_mode'] = select_mode
188+
if not shard_exists:
189+
self_collate = self_collate if self_collate is not None else True
190+
select_mode = select_mode or 'random'
191+
self.local.add_log(f'Adding new shard {shard_id} to collators list. self_collate: {self_collate}, select_mode: {select_mode}', 'info')
192+
collators_list['shards'].append({
193+
'shard_id': shard_id,
194+
'self_collate': self_collate,
195+
'select_mode': select_mode,
196+
'collators': [{'adnl_id': adnl}]
197+
})
198+
self.set_collators_list(collators_list)
199+
color_print("add_collator - {green}OK{endc}")
200+
201+
def delete_collator(self, args: list):
202+
if len(args) < 1:
203+
color_print("{red}Bad args. Usage:{endc} delete_collator [shard] <adnl>")
204+
return
205+
206+
shard_id = None
207+
if ':' in args[0]:
208+
shard_id = hex_shard_to_int(args[0])
209+
args.pop(0)
210+
adnl = args[0]
211+
if is_hex(adnl):
212+
adnl = hex2b64(adnl)
213+
214+
collators_list = self.get_collators_list()
215+
if 'shards' not in collators_list or not collators_list['shards']:
216+
color_print("{red}No collators found.{endc}")
217+
return
218+
219+
deleted = False
220+
for sh in collators_list['shards']:
221+
if shard_id is None or sh['shard_id'] == shard_id:
222+
for c in sh['collators']:
223+
if c['adnl_id'] == adnl:
224+
sh['collators'].remove(c)
225+
self.local.add_log(f'Removing collator {adnl} from shard {sh["shard_id"]}', 'info')
226+
if not sh['collators']:
227+
collators_list['shards'].remove(sh)
228+
self.local.add_log(f'Removing shard {sh["shard_id"]} from collators list because it has no collators left', 'info')
229+
deleted = True
230+
if deleted:
231+
self.set_collators_list(collators_list)
232+
color_print("delete_collator - {green}OK{endc}")
233+
234+
def print_collators(self, args: list):
235+
if '--json' in args:
236+
print(json.dumps(self.get_collators_list(), indent=2))
237+
else:
238+
result = self.ton.validatorConsole.Run('show-collators-list')
239+
result = result.split('conn ready')[1].strip()
240+
if 'collators list is empty' in result:
241+
print("No collators found")
242+
return
243+
print(result)
244+
245+
def reset_collators(self, args: list):
246+
if not self.get_collators_list():
247+
color_print("{red}No collators to reset.{endc}")
248+
return
249+
result = self.ton.validatorConsole.Run('clear-collators-list')
250+
if 'success' not in result:
251+
raise Exception(f'Failed to reset collators list: {result}')
252+
color_print("reset_collators - {green}OK{endc}")
253+
117254
def add_console_commands(self, console):
118255
console.AddItem("vo", self.vote_offer, self.local.translate("vo_cmd"))
119256
console.AddItem("ve", self.vote_election_entry, self.local.translate("ve_cmd"))
120257
console.AddItem("vc", self.vote_complaint, self.local.translate("vc_cmd"))
121258
console.AddItem("check_ef", self.check_efficiency, self.local.translate("check_ef_cmd"))
259+
console.AddItem("add_collator", self.add_collator, self.local.translate("add_collator_cmd"))
260+
console.AddItem("delete_collator", self.delete_collator, self.local.translate("delete_collator_cmd"))
261+
console.AddItem("print_collators", self.print_collators, self.local.translate("print_collators_cmd"))
262+
console.AddItem("reset_collators", self.reset_collators, self.local.translate("reset_collators_cmd"))

mytoncore/utils.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,14 @@ def parse_db_stats(path: str):
101101

102102
def get_hostname():
103103
return subprocess.run(["hostname"], stdout=subprocess.PIPE).stdout.decode().strip()
104+
105+
def hex_shard_to_int(shard_id_str: str) -> dict:
106+
try:
107+
wc, shard_hex = shard_id_str.split(':')
108+
wc = int(wc)
109+
shard = int(shard_hex, 16)
110+
if shard >= 2 ** 63:
111+
shard -= 2 ** 64
112+
return {"workchain": wc, "shard": shard}
113+
except (ValueError, IndexError):
114+
raise Exception(f'Invalid shard ID "{shard_id_str}"')

0 commit comments

Comments
 (0)