github.com/platonnetwork/platon-go@v0.7.6/cases/tests/lib/pip.py (about) 1 from environment.env import TestEnvironment 2 from environment.node import Node 3 from .config import PipConfig 4 from .economic import Economic 5 from .utils import int_to_bytes, get_blockhash, proposal_list_effective, proposal_effective, find_proposal 6 from typing import List 7 import time 8 9 10 class Pip: 11 """ 12 Used to initiate a pip transaction, 13 if you need to use the call method, please call pip 14 example: 15 >>>pip=Pip(env, node) 16 >>>pip.pip.getActiveVersion(...) 17 """ 18 cfg = PipConfig 19 20 def __init__(self, env: TestEnvironment, node: Node): 21 self.node = node 22 self.economic = Economic(env) 23 24 def submitText(self, verifier, pip_id, from_address, transaction_cfg=None): 25 """ 26 Submit a text proposal 27 :param verifier: The certified submitting the proposal 28 :param pip_id: PIPID 29 :param from_address: address for transaction 30 :param transaction_cfg: Transaction basic configuration 31 type: dict 32 example:cfg = { 33 "gas":100000000, 34 "gasPrice":2000000000000, 35 "nonce":1, 36 } 37 :return: if is need analyze return transaction result dict 38 if is not need analyze return transaction hash 39 """ 40 pri_key = self.economic.account.find_pri_key(from_address) 41 return self.pip.submitText(verifier, pip_id, pri_key, transaction_cfg) 42 43 def submitVersion(self, verifier, pip_id, new_version, end_voting_rounds, from_address, transaction_cfg=None): 44 """ 45 Submit an upgrade proposal 46 :param verifier: The certified submitting the proposal 47 :param pip_id: PIPID 48 :param new_version: upgraded version 49 :param end_voting_rounds: The number of voting consensus rounds. 50 Explanation: Assume that the transaction submitted by the proposal is rounded when the consensus round 51 number of the package is packed into the block, then the proposal voting block is high, 52 which is the 230th block height of the round of the round1 + endVotingRounds 53 (assuming a consensus round out of block 250, ppos The list is 20 blocks high in advance, 54 250, 20 are configurable), where 0 < endVotingRounds <= 4840 (about 2 weeks, the actual discussion 55 can be calculated according to the configuration), and is an integer) 56 :param from_address: address for transaction 57 :param transaction_cfg: Transaction basic configuration 58 type: dict 59 example:cfg = { 60 "gas":100000000, 61 "gasPrice":2000000000000, 62 "nonce":1, 63 } 64 :return: if is need analyze return transaction result dict 65 if is not need analyze return transaction hash 66 """ 67 pri_key = self.economic.account.find_pri_key(from_address) 68 return self.pip.submitVersion(verifier, pip_id, new_version, end_voting_rounds, pri_key, transaction_cfg) 69 70 def submitParam(self, verifier, pip_id, module, name, new_value, from_address, transaction_cfg=None): 71 """ 72 Submit an param proposal 73 :param verifier: The certified submitting the proposal 74 :param pip_id: PIPID 75 :param module: parameter module 76 :param name: parameter name 77 :param new_value: New parameter value 78 :param from_address: address for transaction 79 :param transaction_cfg: Transaction basic configuration 80 type: dict 81 example:cfg = { 82 "gas":100000000, 83 "gasPrice":2000000000000, 84 "nonce":1, 85 } 86 :return: if is need analyze return transaction result dict 87 if is not need analyze return transaction hash 88 """ 89 pri_key = self.economic.account.find_pri_key(from_address) 90 return self.pip.submitParam(verifier, pip_id, module, name, new_value, pri_key, transaction_cfg) 91 92 def submitCancel(self, verifier, pip_id, end_voting_rounds, tobe_canceled_proposal_id, from_address, transaction_cfg=None): 93 """ 94 Submit cancellation proposal 95 :param verifier: The certified submitting the proposal 96 :param pip_id: PIPID 97 :param end_voting_rounds: 98 The number of voting consensus rounds. Refer to the instructions for submitting the upgrade proposal. 99 At the same time, the value of this parameter in this interface 100 cannot be greater than the value in the corresponding upgrade proposal. 101 :param tobe_canceled_proposal_id: Upgrade proposal ID to be cancelled 102 :param from_address: address for transaction 103 :param transaction_cfg: Transaction basic configuration 104 type: dict 105 example:cfg = { 106 "gas":100000000, 107 "gasPrice":2000000000000, 108 "nonce":1, 109 } 110 :return: if is need analyze return transaction result dict 111 if is not need analyze return transaction hash 112 """ 113 pri_key = self.economic.account.find_pri_key(from_address) 114 return self.pip.submitCancel(verifier, pip_id, end_voting_rounds, tobe_canceled_proposal_id, pri_key, transaction_cfg) 115 116 def vote(self, verifier, proposal_id, option, from_address, program_version=None, version_sign=None, transaction_cfg=None): 117 """ 118 Vote for proposal 119 :param verifier: The certified submitting the proposal 120 :param proposal_id: Proposal ID 121 :param option: Voting option 122 :param program_version: Node code version, obtained by rpc getProgramVersion interface 123 :param version_sign: Code version signature, obtained by rpc getProgramVersion interface 124 :param from_address: address for transaction 125 :param transaction_cfg: Transaction basic configuration 126 type: dict 127 example:cfg = { 128 "gas":100000000, 129 "gasPrice":2000000000000, 130 "nonce":1, 131 } 132 :return: if is need analyze return transaction result dict 133 if is not need analyze return transaction hash 134 """ 135 pri_key = self.economic.account.find_pri_key(from_address) 136 if program_version is None: 137 program_version = self.node.program_version 138 if version_sign is None: 139 version_sign = self.node.program_version_sign 140 return self.pip.vote(verifier, proposal_id, option, program_version, version_sign, pri_key, transaction_cfg) 141 142 def declareVersion(self, active_node, from_address, program_version=None, version_sign=None, transaction_cfg=None): 143 """ 144 Version statement 145 :param active_node: The declared node can only be a verifier/candidate 146 :param program_version: The declared version, obtained by rpc's getProgramVersion interface 147 :param version_sign: The signed version signature, obtained by rpc's getProgramVersion interface 148 :param from_address: address transaction 149 :param transaction_cfg: Transaction basic configuration 150 type: dict 151 example:cfg = { 152 "gas":100000000, 153 "gasPrice":2000000000000, 154 "nonce":1, 155 } 156 :return: if is need analyze return transaction result dict 157 if is not need analyze return transaction hash 158 """ 159 pri_key = self.economic.account.find_pri_key(from_address) 160 if program_version is None: 161 program_version = self.node.program_version 162 if version_sign is None: 163 version_sign = self.node.program_version_sign 164 return self.pip.declareVersion(active_node, program_version, version_sign, pri_key, transaction_cfg) 165 166 @property 167 def pip(self): 168 """ 169 use sdk pip 170 :return: 171 """ 172 return self.node.pip 173 174 def get_status_of_proposal(self, proposal_id): 175 """ 176 Get proposal voting results 177 :param proposal_id: 178 :return: 179 """ 180 result = self.pip.getTallyResult(proposal_id) 181 data = result.get('Ret') 182 # data = json.loads(data) 183 if not data: 184 raise Exception('Failed to query proposal result based on given proposal id') 185 return data.get('status') 186 187 def get_accu_verifiers_of_proposal(self, proposal_id): 188 """ 189 Get the total number of certifiers who have voted for the entire voting period 190 :param proposal_id: 191 :return: 192 """ 193 result = self.pip.getTallyResult(proposal_id) 194 resultinfo = result.get('Ret') 195 # resultinfo = json.loads(resultinfo) 196 if not resultinfo: 197 raise Exception('Failed to query proposal result based on given proposal id') 198 return resultinfo.get('accuVerifiers') 199 200 def get_yeas_of_proposal(self, proposal_id): 201 """ 202 Get the number of people who voted for the entire voting period 203 :param proposal_id: 204 :return: 205 """ 206 result = self.pip.getTallyResult(proposal_id) 207 data = result.get('Ret') 208 # data = json.loads(data) 209 if not data: 210 raise Exception('Failed to query proposal result based on given proposal id') 211 return data.get('yeas') 212 213 def get_nays_of_proposal(self, proposal_id): 214 """ 215 Get the number of votes against the entire voting period 216 :param proposal_id: 217 :return: 218 """ 219 result = self.pip.getTallyResult(proposal_id) 220 data = result.get('Ret') 221 # data = json.loads(data) 222 if not data: 223 raise Exception('Failed to query proposal result based on given proposal id') 224 return data.get('nays') 225 226 def get_abstentions_of_proposal(self, proposal_id): 227 """ 228 Obtain the number of abstentions during the entire voting period 229 :param proposal_id: 230 :return: 231 """ 232 result = self.pip.getTallyResult(proposal_id) 233 data = result.get('Ret') 234 # data = json.loads(data) 235 if not data: 236 raise Exception('Failed to query proposal result based on given proposal id') 237 return data.get('abstentions') 238 239 def get_canceledby_of_proposal(self, proposal_id): 240 """ 241 Obtain the number of abstentions during the entire voting period 242 :param proposal_id: 243 :return: 244 """ 245 result = self.pip.getTallyResult(proposal_id) 246 data = result.get('Ret') 247 # data = json.loads(data) 248 if not data: 249 raise Exception('Failed to query proposal result based on given proposal id') 250 return data.get('canceledBy') 251 252 @property 253 def chain_version(self): 254 """ 255 Get the version number on the chain 256 :return: 257 """ 258 result = self.pip.getActiveVersion() 259 return int(result.get('Ret')) 260 261 def get_version_small_version(self, flag=3): 262 """ 263 Determine if the minor version of the incoming version number is 0 264 :param flag: 265 :return: 266 """ 267 flag = int(flag) 268 if flag > 3 or flag < 1: 269 raise Exception("Incorrect parameters passed in") 270 version = int(self.chain_version) 271 version_byte = int_to_bytes(version) 272 return version_byte[flag] 273 274 def get_accuverifiers_count(self, proposal_id, blocknumber=None): 275 """ 276 Get proposal real-time votes 277 :param proposal_id: 278 :param blocknumber: 279 :return: 280 """ 281 if blocknumber is None: 282 blocknumber = self.node.block_number 283 blockhash = get_blockhash(self.node, blocknumber) 284 result = self.pip.getAccuVerifiersCount(proposal_id, blockhash) 285 voteinfo = result.get('Ret') 286 # vote_result = eval(voteinfo) 287 return voteinfo 288 289 def get_rate_of_voting(self, proposal_id): 290 """ 291 Calculate the voting rate of the upgrade proposal 292 :param proposal_id: 293 :return: 294 """ 295 result = self.pip.getTallyResult(proposal_id).get('Ret') 296 # result = json.loads(result) 297 if not result: 298 raise Exception('Failed to query proposal result based on given proposal id') 299 yeas = result.get('yeas') 300 accu_verifiers = result.get('accuVerifiers') 301 return yeas / accu_verifiers 302 303 def get_effect_proposal_info_of_preactive(self): 304 """ 305 Get pre-valid proposal information on the chain 306 :return: 307 """ 308 result = self.pip.listProposal().get('Ret') 309 # result = json.loads(result) 310 for pid_list in result: 311 if pid_list.get('ProposalType') == 2: 312 if self.get_status_of_proposal(pid_list.get('ProposalID')) == 4: 313 return pid_list 314 raise Exception('There is no pre-validation upgrade proposal') 315 316 def get_effect_proposal_info_of_vote(self, proposaltype=cfg.version_proposal): 317 """ 318 Get pre-valid proposal information on the chain 319 :return: 320 """ 321 if not self.is_exist_effective_proposal_for_vote(self.cfg.text_proposal) and proposaltype == self.cfg.text_proposal: 322 return None 323 324 if not self.is_exist_effective_proposal_for_vote(self.cfg.version_proposal) and proposaltype == self.cfg.version_proposal: 325 return None 326 327 if not self.is_exist_effective_proposal_for_vote(self.cfg.cancel_proposal) and proposaltype == self.cfg.cancel_proposal: 328 return None 329 330 if not self.is_exist_effective_proposal_for_vote(self.cfg.param_proposal) and proposaltype == self.cfg.param_proposal: 331 return None 332 333 proposal_info = self.pip.listProposal().get('Ret') 334 # proposal_info = json.loads(proposal_info) 335 proposal_list_text = [] 336 proposal_list_version = [] 337 proposal_list_param = [] 338 proposal_list_cancel = [] 339 for pid_list in proposal_info: 340 if pid_list.get('ProposalType') == self.cfg.text_proposal: 341 proposal_list_text.append(pid_list) 342 343 elif pid_list.get('ProposalType') == self.cfg.version_proposal: 344 proposal_list_version.append(pid_list) 345 346 elif pid_list.get('ProposalType') == self.cfg.cancel_proposal: 347 proposal_list_cancel.append(pid_list) 348 349 elif pid_list.get('ProposalType') == self.cfg.param_proposal: 350 proposal_list_param.append(pid_list) 351 else: 352 raise Exception("Unknown proposal type") 353 # Current block height 354 block_number = self.node.eth.blockNumber 355 if proposaltype == self.cfg.text_proposal: 356 return find_proposal(proposal_list_text, block_number) 357 358 elif proposaltype == self.cfg.version_proposal: 359 return find_proposal(proposal_list_version, block_number) 360 361 elif proposaltype == self.cfg.cancel_proposal: 362 return find_proposal(proposal_list_cancel, block_number) 363 364 elif proposaltype == self.cfg.param_proposal: 365 return find_proposal(proposal_list_param, block_number) 366 else: 367 raise Exception("listProposal interface gets the wrong proposal type") 368 369 def get_proposal_info_list(self): 370 """ 371 Get a list of proposals 372 :return: 373 """ 374 proposal_info_list = self.pip.listProposal().get('Ret') 375 version_proposal_list, text_proposal_list, cancel_proposal_list, param_proposal_list = [], [], [], [] 376 if proposal_info_list != 'Object not found': 377 # proposal_info_list = json.loads(proposal_info_list) 378 for proposal_info in proposal_info_list: 379 if proposal_info.get('ProposalType') == self.cfg.version_proposal: 380 version_proposal_list.append(proposal_info) 381 elif proposal_info.get('ProposalType') == self.cfg.text_proposal: 382 text_proposal_list.append(proposal_info) 383 elif proposal_info.get('ProposalType') == self.cfg.cancel_proposal: 384 cancel_proposal_list.append(proposal_info) 385 elif proposal_info.get('ProposalType') == self.cfg.param_proposal: 386 param_proposal_list.append(proposal_info) 387 else: 388 raise Exception('listProposal interface gets the wrong proposal type') 389 return version_proposal_list, text_proposal_list, cancel_proposal_list, param_proposal_list 390 391 def is_exist_effective_proposal(self, proposal_type=None): 392 """ 393 Determine if there is a valid upgrade proposal on the chain - to determine if a proposal can be initiated 394 :param proposal_type: 2 is the upgrade proposal 1. Text proposal 4. Cancel the proposal 395 :return: 396 """ 397 if proposal_type is None: 398 proposal_type = self.cfg.version_proposal 399 version_proposal_list, text_proposal_list, cancel_proposal_list, param_proposal_list = self.get_proposal_info_list() 400 block_number = self.node.eth.blockNumber 401 if proposal_type == self.cfg.version_proposal: 402 for version_proposal in version_proposal_list: 403 if proposal_effective(version_proposal, block_number): 404 return True 405 else: 406 status = self.get_status_of_proposal(version_proposal["ProposalID"].strip()) 407 if status == 4: 408 return True 409 410 elif proposal_type == self.cfg.text_proposal: 411 return proposal_list_effective(text_proposal_list, block_number) 412 413 elif proposal_type == self.cfg.cancel_proposal: 414 return proposal_list_effective(cancel_proposal_list, block_number) 415 416 elif proposal_type == self.cfg.param_proposal: 417 return proposal_list_effective(param_proposal_list, block_number) 418 else: 419 raise Exception("Incoming type error") 420 return False 421 422 def is_exist_effective_proposal_for_vote(self, proposal_type=None): 423 """ 424 Is there a valid proposal for voting? 425 :param proposal_type: 426 :return: 427 """ 428 if proposal_type is None: 429 proposal_type = self.cfg.version_proposal 430 version_proposal_list, text_proposal_list, cancel_proposal_list, param_proposal_list = self.get_proposal_info_list() 431 block_number = self.node.eth.blockNumber 432 if proposal_type == self.cfg.version_proposal: 433 return proposal_list_effective(version_proposal_list, block_number) 434 435 elif proposal_type == self.cfg.text_proposal: 436 return proposal_list_effective(text_proposal_list, block_number) 437 438 elif proposal_type == self.cfg.cancel_proposal: 439 return proposal_list_effective(cancel_proposal_list, block_number) 440 441 elif proposal_type == self.cfg.param_proposal: 442 return proposal_list_effective(param_proposal_list, block_number) 443 else: 444 raise Exception("Incoming type error") 445 446 def get_candidate_list_not_verifier(self): 447 """ 448 获取当前结算周期非验证人的候选人列表 449 :return: 450 """ 451 candidate_list = self.node.ppos.getCandidateList().get('Ret') 452 verifier_list = self.node.ppos.getVerifierList().get('Ret') 453 if verifier_list == "Getting verifierList is failed:The validator is not exist" or verifier_list == []: 454 time.sleep(10) 455 verifier_list = self.node.ppos.getVerifierList().get('Ret') 456 candidate_no_verify_list = [] 457 verifier_node_list = [node_info.get("NodeId") for node_info in verifier_list] 458 for node_info in candidate_list: 459 node_id = node_info.get("NodeId") 460 if node_id not in verifier_node_list: 461 candidate_no_verify_list.append(node_id) 462 return candidate_no_verify_list 463 464 def get_version(self, version=None): 465 # todo implement 466 pass 467 468 469 def get_pip_obj(nodeid, pip_obj_list: List[Pip]) -> Pip: 470 """ 471 Get the pip object according to the node id 472 :param nodeid: 473 :param pip_obj_list: 474 :return: 475 """ 476 for pip_obj in pip_obj_list: 477 if nodeid == pip_obj.node.node_id: 478 return pip_obj 479 480 481 def get_pip_obj_list(nodeid_list, pip_obj_list: List[Pip]) -> List[Pip]: 482 """ 483 Get a list of pip objects based on the node id list 484 :param nodeid_list: 485 :param pip_obj_list: 486 :return: 487 """ 488 new_pip_obj_list = [] 489 for nodeid in nodeid_list: 490 new_pip_obj_list.append(get_pip_obj(nodeid, pip_obj_list)) 491 return new_pip_obj_list