|
| 1 | +import json |
1 | 2 | import time |
2 | 3 |
|
3 | 4 | from modules.btc_teleport import BtcTeleportModule |
4 | 5 | from mypylib.mypylib import color_print, get_timestamp |
5 | 6 | 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 |
7 | 9 |
|
8 | 10 |
|
9 | 11 | class ValidatorModule(MtcModule): |
@@ -114,8 +116,147 @@ def check_enable(cls, ton: "MyTonCore"): |
114 | 116 | f'Use `disable_mode liteserver` first.') |
115 | 117 | BtcTeleportModule(ton, ton.local).init() |
116 | 118 |
|
| 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 | + |
117 | 254 | def add_console_commands(self, console): |
118 | 255 | console.AddItem("vo", self.vote_offer, self.local.translate("vo_cmd")) |
119 | 256 | console.AddItem("ve", self.vote_election_entry, self.local.translate("ve_cmd")) |
120 | 257 | console.AddItem("vc", self.vote_complaint, self.local.translate("vc_cmd")) |
121 | 258 | 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")) |
0 commit comments