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