class DashdInterface(WndUtils):
    def __init__(self,
                 config,
                 window,
                 connection=None,
                 on_connection_begin_callback=None,
                 on_connection_try_fail_callback=None,
                 on_connection_finished_callback=None):
        WndUtils.__init__(self, app_path=config.app_path)
        assert isinstance(config, AppConfig)

        self.config = config
        # conn configurations are used from the first item in the list; if one fails, then next is taken
        if connection:
            # this parameter is used for testing specific connection
            self.connections = [connection]
        else:
            # get connection list orderd by priority of use
            self.connections = self.config.get_ordered_conn_list()
        self.cur_conn_index = 0
        if self.connections:
            self.cur_conn_def = self.connections[self.cur_conn_index]
        else:
            self.cur_conn_def = None

        # below is the connection with which particular RPC call has started; if connection is switched because of
        # problems with some nodes, switching stops if we close round and return to the starting connection
        self.starting_conn = None

        self.ssh = None
        self.window = window
        self.active = False
        self.rpc_url = None
        self.proxy = None
        self.http_conn = None  # HTTPConnection object passed to the AuthServiceProxy (for convinient connection reset)
        self.on_connection_begin_callback = on_connection_begin_callback
        self.on_connection_try_fail_callback = on_connection_try_fail_callback
        self.on_connection_finished_callback = on_connection_finished_callback
        self.last_error_message = None

    def apply_new_cfg(self):
        """
        Called after any of connection config changed.
        """
        # get connection list orderd by priority of use
        self.disconnect()
        self.connections = self.config.get_ordered_conn_list()
        self.cur_conn_index = 0
        if not len(self.connections):
            raise Exception(
                'There is no connections to Dash network enabled in the configuration.'
            )
        self.cur_conn_def = self.connections[self.cur_conn_index]

    def disconnect(self):
        if self.active:
            if self.ssh:
                self.ssh.disconnect()
                del self.ssh
                self.ssh = None
            self.active = False

    def mark_call_begin(self):
        self.starting_conn = self.cur_conn_def

    def switch_to_next_config(self):
        """
        If there is another dashd config not used recently, switch to it. Called only when there was a problem
        with current connection config.
        :return: True if successfully switched ot False if there was no another config
        """
        if self.cur_conn_def:
            self.config.conn_cfg_failure(
                self.cur_conn_def)  # mark connection as defective
        if self.cur_conn_index < len(self.connections) - 1:
            idx = self.cur_conn_index + 1
        else:
            idx = 0

        conn = self.connections[idx]
        if conn != self.starting_conn:
            self.disconnect()
            self.cur_conn_index = idx
            self.cur_conn_def = conn
            if not self.open():
                return self.switch_to_next_config()
            else:
                return True
        else:
            return False

    def mark_cur_conn_cfg_is_ok(self):
        if self.cur_conn_def:
            self.config.conn_cfg_success(self.cur_conn_def)

    def open(self):
        """
        Opens connection to dash RPC. If it fails, then the next enabled conn config will be used, if any exists.
        :return: True if successfully connected, False if user cancelled the operation. If all of the attempts 
            fail, then appropriate exception will be raised.
        """
        try:
            if not self.cur_conn_def:
                raise Exception(
                    'There is no connections to Dash network enabled in the configuration.'
                )

            while True:
                try:
                    if self.open_internal():
                        break
                    else:
                        if not self.switch_to_next_config():
                            return False
                except UserCancelledConnection:
                    return False
                except (socket.gaierror, ConnectionRefusedError, TimeoutError,
                        socket.timeout) as e:
                    # exceptions raised by not likely functioning dashd node; try to switch to another node
                    # if there is any in the config
                    if not self.switch_to_next_config():
                        raise e  # couldn't use another conn config, raise exception
                    else:
                        break
        except Exception as e:
            self.last_error_message = str(e)
            raise

        return True

    def open_internal(self):
        """
        Try to establish connection to dash RPC daemon for current connection config.
        :return: True, if connection successfully establishes, False if user Cancels the operation (not always 
            cancelling will be possible - only when user is prompted for a password).
        """
        if not self.active:
            if self.cur_conn_def.use_ssh_tunnel:
                # RPC over SSH
                while True:
                    self.ssh = DashdSSH(
                        self.cur_conn_def.ssh_conn_cfg.host,
                        self.cur_conn_def.ssh_conn_cfg.port,
                        self.cur_conn_def.ssh_conn_cfg.username)
                    try:
                        logging.info('starting ssh.connect')
                        self.ssh.connect()
                        logging.info('finished ssh.connect')
                        break
                    except Exception as e:
                        logging.error('error in ssh.connect')
                        raise

                # configure SSH tunnel
                # get random local unprivileged port number to establish SSH tunnel
                success = False
                local_port = None
                for try_nr in range(1, 10):
                    try:
                        logging.info('beginning ssh.open_tunnel')
                        local_port = randint(2000, 50000)
                        self.ssh.open_tunnel(local_port,
                                             self.cur_conn_def.host,
                                             int(self.cur_conn_def.port))
                        success = True
                        break
                    except Exception as e:
                        logging.error('error in ssh.open_tunnel loop')
                        pass
                logging.info('finished ssh.open_tunnel loop')
                if not success:
                    logging.error('finished ssh.open_tunnel loop with error')
                    return False
                else:
                    rpc_user = self.cur_conn_def.username
                    rpc_password = self.cur_conn_def.password
                    rpc_host = '127.0.0.1'  # SSH tunnel on loopback
                    rpc_port = local_port
            else:
                # direct RPC
                rpc_host = self.cur_conn_def.host
                rpc_port = self.cur_conn_def.port
                rpc_user = self.cur_conn_def.username
                rpc_password = self.cur_conn_def.password

            if self.cur_conn_def.use_ssl:
                self.rpc_url = 'https://'
                self.http_conn = httplib.HTTPSConnection(
                    rpc_host,
                    rpc_port,
                    timeout=5,
                    context=ssl._create_unverified_context())
            else:
                self.rpc_url = 'http://'
                self.http_conn = httplib.HTTPConnection(rpc_host,
                                                        rpc_port,
                                                        timeout=5)

            logging.info('AuthServiceProxy begin')
            self.rpc_url += rpc_user + ':' + rpc_password + '@' + rpc_host + ':' + str(
                rpc_port)
            self.proxy = AuthServiceProxy(self.rpc_url,
                                          timeout=1000,
                                          connection=self.http_conn)
            logging.info('AuthServiceProxy end')

            try:
                if self.on_connection_begin_callback:
                    try:
                        # make the owner know, we are connecting
                        logging.info('on_connection_begin_callback begin')
                        self.on_connection_begin_callback()
                        logging.info('on_connection_begin_callback end')
                    except:
                        pass

                # check the connection
                logging.info('starting http_conn.connect()')
                self.http_conn.connect()
                logging.info('finished http_conn.connect()')

                if self.on_connection_finished_callback:
                    try:
                        # make the owner know, we successfully finished connection
                        self.on_connection_finished_callback()
                    except:
                        pass
            except:
                if self.on_connection_try_fail_callback:
                    try:
                        # make the owner know, connection attempt failed
                        self.on_connection_try_fail_callback()
                    except:
                        pass
                raise
            finally:
                logging.info('http_conn.close()')
                self.http_conn.close()
                # timeout hase been initially set to 5 seconds to perform 'quick' connection test
                self.http_conn.timeout = 20

            self.active = True
        return self.active

    def get_active_conn_description(self):
        if self.cur_conn_def:
            return self.cur_conn_def.get_description()
        else:
            return '???'

    @control_rpc_call
    def getblockcount(self):
        if self.open():
            return self.proxy.getblockcount()
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def getblockhash(self, block):
        if self.open():
            return self.proxy.getblockhash(block)
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def getinfo(self):
        if self.open():
            return self.proxy.getinfo()
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def issynchronized(self):
        if self.open():
            # if connecting to HTTP(S) proxy do not check if dash daemon is synchronized
            if self.cur_conn_def.is_http_proxy():
                return True
            else:
                syn = self.proxy.mnsync('status')
                return syn.get('IsSynced')
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def mnsync(self):
        if self.open():
            # if connecting to HTTP(S) proxy do not call this function - it will not be exposed
            if self.cur_conn_def.is_http_proxy():
                return {}
            else:
                return self.proxy.mnsync('status')
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def masternodebroadcast(self, what, hexto):
        if self.open():
            return self.proxy.masternodebroadcast(what, hexto)
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def get_masternodelist(self, *args):
        if self.open():
            return self.proxy.masternodelist(*args)
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def get_masternodeaddr(self):
        if self.open():
            return self.proxy.masternodelist('addr')
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def getaddressbalance(self, address):
        if self.open():
            return self.proxy.getaddressbalance({
                'addresses': [address]
            }).get('balance')
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def getaddressutxos(self, addresses):
        if self.open():
            return self.proxy.getaddressutxos({'addresses': addresses})
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def getrawtransaction(self, txid, verbose):
        if self.open():
            return self.proxy.getrawtransaction(txid, verbose)
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def getblockhash(self, blockid):
        if self.open():
            return self.proxy.getblockhash(blockid)
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def getblockheader(self, blockhash):
        if self.open():
            return self.proxy.getblockheader(blockhash)
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def validateaddress(self, address):
        if self.open():
            return self.proxy.validateaddress(address)
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def decoderawtransaction(self, tx):
        if self.open():
            return self.proxy.decoderawtransaction(tx)
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def sendrawtransaction(self, tx):
        if self.open():
            return self.proxy.sendrawtransaction(tx)
        else:
            raise Exception('Not connected')
Beispiel #2
0
class chainxdSSH(object):
    def __init__(self, host, port, username):
        self.host = host
        self.port = port
        self.username = username
        self.ssh = None
        self.channel = None
        self.fw_channel = None
        self.connected = False
        self.ssh_thread = None

    def __del__(self):
        self.disconnect()

    def remote_command(self, cmd):
        channel = None
        try:
            channel = self.ssh.get_transport().open_session()
            channel.exec_command(cmd)
            ret_code = channel.recv_exit_status()

            if ret_code == 0:
                for idx in range(1, 20):
                    if channel.recv_ready():
                        break
                    time.sleep(0.1)
                if not channel.recv_ready():
                    raise Exception('Data not ready')
                data = channel.recv(500)
                return data.decode().split('\n')
            else:
                for idx in range(1, 20):
                    if channel.recv_stderr_ready():
                        break
                    time.sleep(0.1)
                if channel.recv_stderr_ready():
                    data = channel.recv_stderr(500)
                    error = data.decode()
                    raise Exception(error)
                else:
                    raise UnknownError('Unknown error executing remote command: ' + cmd)
        finally:
            if channel:
                channel.close()

    def connect(self):
        import paramiko
        self.ssh = paramiko.SSHClient()
        self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        password = None
        pass_message = None

        while True:
            try:
                self.ssh.connect(self.host, port=int(self.port), username=self.username, password=password)
                self.connected = True
                if password:
                    SshPassCache.save_password(self.username, self.host, password)
                break
            except PasswordRequiredException as e:
                # private key with password protection is used; ask user for password
                pass_message = "Enter passphrase for <b>private key</b> or password for %s" % \
                               (self.username + '@' + self.host)
                while True:
                    password = SshPassCache.get_password(self.username, self.host, message=pass_message)
                    if password:
                        break
                        chain_port = self.remote_port

            except AuthenticationException as e:
                # This exception will be raised in the following cases:
                #  1. a private key with password protectection is used but the user enters incorrect password
                #  2. a private key exists but user's public key is not added to the server's allowed keys
                #  3. normal login to server is performed but the user enters bad password
                # So, in the first case, the second query for password will ask for normal password to server, not
                #  for a private key.

                WndUtils.errorMsg(message='Incorrect password, try again...')

                while True:
                    password = SshPassCache.get_password(self.username, self.host, message=pass_message)
                    if password:
                        break

            except SSHException as e:
                if e.args and e.args[0] == 'No authentication methods available':
                    while True:
                        password = SshPassCache.get_password(self.username, self.host)
                        if password:
                            break
                else:
                    raise
            except Exception as e:
                raise

    def open_tunnel(self, local_port, remote_ip, remote_port):
        if self.connected:
            ready_event = threading.Event()
            self.ssh_thread = SSHTunnelThread(local_port, remote_ip, remote_port, self.ssh.get_transport(), ready_event)
            self.ssh_thread.start()
            ready_event.wait(10)
            print('Started local port forwarding 127.0.0.1:%s -> %s:%s' %
                  (str(local_port), remote_ip, str(remote_port)))
        else:
            raise Exception('SSH not connected')

    

        self.config = config
        # conn configurations are used from the first item in the list; if one fails, then next is taken
        if connection:
            # this parameter is used for testing specific connection
            self.connections = [connection]
        else:
            # get connection list orderd by priority of use
            self.connections = self.config.get_ordered_conn_list()
        self.cur_conn_index = 0
        if self.connections:
            self.cur_conn_def = self.connections[self.cur_conn_index]
        else:
            self.cur_conn_def = None

        # below is the connection with which particular RPC call has started; if connection is switched because of
        # problems with some nodes, switching stops if we close round and return to the starting connection
        self.starting_conn = None

        self.ssh = None
        self.window = window
        self.active = False
        self.rpc_url = None
        self.proxy = None
        self.http_conn = None  # HTTPConnection object passed to the AuthServiceProxy (for convinient connection reset)
        self.on_connection_begin_callback = on_connection_begin_callback
        self.on_connection_try_fail_callback = on_connection_try_fail_callback
        self.on_connection_finished_callback = on_connection_finished_callback
        self.last_error_message = None

    def apply_new_cfg(self):
        """
        Called after any of connection config changed.
        """
        # get connection list orderd by priority of use
        self.disconnect()
        self.connections = self.config.get_ordered_conn_list()
        self.cur_conn_index = 0
        if not len(self.connections):
            raise Exception('There is no connections to chainx network enabled in the configuration.')
        self.cur_conn_def = self.connections[self.cur_conn_index]

    def disconnect(self):
        if self.active:
            if self.ssh:
                self.ssh.disconnect()
                del self.ssh
                self.ssh = None
            self.active = False

    def mark_call_begin(self):
        self.starting_conn = self.cur_conn_def

    def switch_to_next_config(self):
        """
        If there is another chainxd config not used recently, switch to it. Called only when there was a problem
        with current connection config.
        :return: True if successfully switched ot False if there was no another config
        """
        if self.cur_conn_def:
            self.config.conn_cfg_failure(self.cur_conn_def)  # mark connection as defective
        if self.cur_conn_index < len(self.connections)-1:
            idx = self.cur_conn_index + 1
        else:
            idx = 0

        conn = self.connections[idx]
        if conn != self.starting_conn:
            self.disconnect()
            self.cur_conn_index = idx
            self.cur_conn_def = conn
            if not self.open():
                return self.switch_to_next_config()
            else:
                return True
        else:
            return False

    def mark_cur_conn_cfg_is_ok(self):
        if self.cur_conn_def:
            self.config.conn_cfg_success(self.cur_conn_def)

    def open(self):
        """
        Opens connection to chainx RPC. If it fails, then the next enabled conn config will be used, if any exists.
        :return: True if successfully connected, False if user cancelled the operation. If all of the attempts 
            fail, then appropriate exception will be raised.
        """
        try:
            if not self.cur_conn_def:
                raise Exception('There is no connections to chainx network enabled in the configuration.')

            while True:
                try:
                    if self.open_internal():
                        break
                    else:
                        if not self.switch_to_next_config():
                            return False
                except UserCancelledConnection:
                    return False
                except (socket.gaierror, ConnectionRefusedError, TimeoutError, socket.timeout) as e:
                    # exceptions raised by not likely functioning chainxd node; try to switch to another node
                    # if there is any in the config
                    if not self.switch_to_next_config():
                        raise e  # couldn't use another conn config, raise exception
                    else:
                        break
        except Exception as e:
            self.last_error_message = str(e)
            raise

        return True

    def open_internal(self):
        """
        Try to establish connection to chainxd RPC daemon for current connection config.
        :return: True, if connection successfully establishes, False if user Cancels the operation (not always 
            cancelling will be possible - only when user is prompted for a password).
        """
        if not self.active:
            if self.cur_conn_def.use_ssh_tunnel:
                # RPC over SSH
                while True:
                    self.ssh = ChainxdSSH(self.cur_conn_def.ssh_conn_cfg.host, self.cur_conn_def.ssh_conn_cfg.port,
                                        self.cur_conn_def.ssh_conn_cfg.username)
                    try:
                        logging.info('starting ssh.connect')
                        self.ssh.connect()
                        logging.info('finished ssh.connect')
                        break
                    except Exception as e:
                        logging.error('error in ssh.connect')
                        raise

                # configure SSH tunnel
                # get random local unprivileged port number to establish SSH tunnel
                success = False
                local_port = None
                for try_nr in range(1, 10):
                    try:
                        logging.info('beginning ssh.open_tunnel')
                        local_port = randint(2000, 50000)
                        self.ssh.open_tunnel(local_port,
                                             self.cur_conn_def.host,
                                             int(self.cur_conn_def.port))
                        success = True
                        break
                    except Exception as e:
                        logging.error('error in ssh.open_tunnel loop')
                        pass
                logging.info('finished ssh.open_tunnel loop')
                if not success:
                    logging.error('finished ssh.open_tunnel loop with error')
                    return False
                else:
                    rpc_user = self.cur_conn_def.username
                    rpc_password = self.cur_conn_def.password
                    rpc_host = '127.0.0.1'  # SSH tunnel on loopback
                    rpc_port = local_port
            else:
                # direct RPC
                rpc_host = self.cur_conn_def.host
                rpc_port = self.cur_conn_def.port
                rpc_user = self.cur_conn_def.username
                rpc_password = self.cur_conn_def.password

            if self.cur_conn_def.use_ssl:
                self.rpc_url = 'https://'
                self.http_conn = httplib.HTTPSConnection(rpc_host, rpc_port, timeout=5, context=ssl._create_unverified_context())
            else:
                self.rpc_url = 'http://'
                self.http_conn = httplib.HTTPConnection(rpc_host, rpc_port, timeout=5)

            logging.info('AuthServiceProxy begin')
            self.rpc_url += rpc_user + ':' + rpc_password + '@' + rpc_host + ':' + str(rpc_port)
            self.proxy = AuthServiceProxy(self.rpc_url, timeout=1000, connection=self.http_conn)
            logging.info('AuthServiceProxy end')

            try:
                if self.on_connection_begin_callback:
                    try:
                        # make the owner know, we are connecting
                        logging.info('on_connection_begin_callback begin')
                        self.on_connection_begin_callback()
                        logging.info('on_connection_begin_callback end')
                    except:
                        pass

                # check the connection
                logging.info('starting http_conn.connect()')
                self.http_conn.connect()
                logging.info('finished http_conn.connect()')

                if self.on_connection_finished_callback:
                    try:
                        # make the owner know, we successfully finished connection
                        self.on_connection_finished_callback()
                    except:
                        pass
            except:
                if self.on_connection_try_fail_callback:
                    try:
                        # make the owner know, connection attempt failed
                        self.on_connection_try_fail_callback()
                    except:
                        pass
                raise
            finally:
                logging.info('http_conn.close()')
                self.http_conn.close()
                # timeout hase been initially set to 5 seconds to perform 'quick' connection test
                self.http_conn.timeout = 20

            self.active = True
        return self.active

    def get_active_conn_description(self):
        if self.cur_conn_def:
            return self.cur_conn_def.get_description()
        else:
            return '???'

    @control_rpc_call
    def getblockcount(self):
        if self.open():
            return self.proxy.getblockcount()
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def getblockhash(self, block):
        if self.open():
            return self.proxy.getblockhash(block)
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def getinfo(self):
        if self.open():
            return self.proxy.getinfo()
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def issynchronized(self):
        if self.open():
            # if connecting to HTTP(S) proxy do not check if chainxd daemon is synchronized
            if self.cur_conn_def.is_http_proxy():
                return True
            else:
                syn = self.proxy.mnsync('status')
                return syn.get('IsSynced')
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def mnsync(self):
        if self.open():
            # if connecting to HTTP(S) proxy do not call this function - it will not be exposed
            if self.cur_conn_def.is_http_proxy():
                return {}
            else:
                return self.proxy.mnsync('status')
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def masternodebroadcast(self, what, hexto):
        if self.open():
            return self.proxy.masternodebroadcast(what, hexto)
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def get_masternodelist(self, *args):
        if self.open():
            return self.proxy.masternodelist(*args)
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def get_masternodeaddr(self):
        if self.open():
            return self.proxy.masternodelist('addr')
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def getaddressbalance(self, address):
        if self.open():
            return self.proxy.getaddressbalance({'addresses': [address]}).get('balance')
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def getaddressutxos(self, addresses):
        if self.open():
            return self.proxy.getaddressutxos({'addresses': addresses})
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def getrawtransaction(self, txid, verbose):
        if self.open():
            return self.proxy.getrawtransaction(txid, verbose)
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def getblockhash(self, blockid):
        if self.open():
            return self.proxy.getblockhash(blockid)
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def getblockheader(self, blockhash):
        if self.open():
            return self.proxy.getblockheader(blockhash)
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def validateaddress(self, address):
        if self.open():
            return self.proxy.validateaddress(address)
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def decoderawtransaction(self, tx):
        if self.open():
            return self.proxy.decoderawtransaction(tx)
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def sendrawtransaction(self, tx):
        if self.open():
            return self.proxy.sendrawtransaction(tx)
        else:
            raise Exception('Not connected')
Beispiel #3
0
class RpcClient:
        
    def __init__(self):
        self.rpc_ip, self.rpc_port, self.rpc_user, self.rpc_passwd = readRPCfile()
        rpc_url = "http://%s:%s@%s:%d" % (self.rpc_user, self.rpc_passwd, self.rpc_ip, self.rpc_port)
        try:    
            self.conn = AuthServiceProxy(rpc_url, timeout=8)     
        except JSONRPCException as e:
            err_msg = 'remote or local PIVX-cli running?'
            printException(getCallerName(), getFunctionName(), err_msg, e)
        except Exception as e:
            err_msg = 'remote or local PIVX-cli running?'
            printException(getCallerName(), getFunctionName(), err_msg, e)
            
    
    
    
    def decodeRawTransaction(self, rawTx):
        try:
            return self.conn.decoderawtransaction(rawTx)    
        except Exception as e:
            err_msg = 'error in decodeRawTransaction'
            printException(getCallerName(), getFunctionName(), err_msg, e.args)
            
    
    
    
    def getAddressUtxos(self, addresses):
        try:
            return self.conn.getaddressutxos({'addresses': addresses})    
        except Exception as e:
            err_msg = "error in getAddressUtxos"
            if str(e.args[0]) != "Request-sent":
                printException(getCallerName(), getFunctionName(), err_msg, e.args)
            else:
                printException(getCallerName(), getFunctionName(), err_msg, e.args)
            raise e
    
    
    
    
    def getBlockCount(self):
        try:
            n = self.conn.getblockcount()
            return n
        except Exception as e:
            err_msg = 'remote or local PIVX-cli running?'
            if str(e.args[0]) != "Request-sent":
                printException(getCallerName(), getFunctionName(), err_msg, e.args)
            else:
                printException(getCallerName(), getFunctionName(), err_msg, e.args)
    
    
    
    
    def getBlockHash(self, blockNum):
        try:
            h = self.conn.getblockhash(blockNum)
            return h
        except Exception as e:
            err_msg = 'remote or local PIVX-cli running?'
            if str(e.args[0]) != "Request-sent":
                printException(getCallerName(), getFunctionName(), err_msg, e.args)
            else:
                printException(getCallerName(), getFunctionName(), err_msg, e.args)
    
    
    def getFeePerKb(self):
        try:
            # get transaction data from last 10 blocks
            feePerKb = float(self.conn.getfeeinfo(10)['feeperkb'])
            return (feePerKb if feePerKb > MINIMUM_FEE else MINIMUM_FEE)
        except Exception as e:
            err_msg = 'error in getFeePerKb'
            if str(e.args[0]) != "Request-sent":
                printException(getCallerName(), getFunctionName(), err_msg, e.args)
            else:
                printException(getCallerName(), getFunctionName(), err_msg, e.args)
    
    
    
    def getMNStatus(self, address):
        try:
            mnStatusList = self.conn.listmasternodes(address)
            if not mnStatusList:
                return None
            mnStatus = mnStatusList[0]
            mnStatus['mnCount'] = self.conn.getmasternodecount()['enabled']
            return mnStatus
        
        except Exception as e:
            err_msg = "error in getMNStatus"
            if str(e.args[0]) != "Request-sent":
                printException(getCallerName(), getFunctionName(), err_msg, e.args)
            else:
                printException(getCallerName(), getFunctionName(), err_msg, e.args)
    
    
    
    
    def getProtocolVersion(self):
        try:
            prot_version = self.conn.getinfo().get('protocolversion')
            return int(prot_version)
        
        except Exception as e:
            err_msg = 'error in getProtocolVersion'
            printException(getCallerName(), getFunctionName(), err_msg, e.args)
            return DEFAULT_PROTOCOL_VERSION    
     
            
    
    
    def getRawTransaction(self, txid):
        try:
            return self.conn.getrawtransaction(txid)
        except Exception as e:
            err_msg = "is Blockchain synced?"
            if str(e.args[0]) != "Request-sent":
                printException(getCallerName(), getFunctionName(), err_msg, e.args)
            return None
    
    
    
    
    def getStatus(self):
        status = False
        n = 0
        try:
            n = self.conn.getblockcount()
            if n > 0:
                status = True
                
        except Exception as e:
            # If loading block index set lastBlock=1
            if str(e.args[0]) == "Loading block index..." or str(e.args[0]) == "Verifying blocks...":
                printDbg(str(e.args[0]))
                n = 1
            #else:
                #err_msg = "Error while contacting RPC server"
                #printException(getCallerName(), getFunctionName(), err_msg, e.args)    
        return status, n
    
    
    
    
    def getStatusMess(self, status=None):
        if status == None:
            status = self.getStatus()    
        if status: 
            return "RPC status: CONNECTED!!!"
        else:
            return "RPC status: NOT CONNECTED. remote or local PIVX-cli running?"
    
    
    
    def isBlockchainSynced(self):
        try:
            return self.conn.mnsync('status').get("IsBlockchainSynced")
        
        except Exception as e:
            err_msg = "error in isBlockchainSynced"
            printException(getCallerName(), getFunctionName(), err_msg, e.args)
            return False
            
            
            
    def decodemasternodebroadcast(self, work):
        try:
            return self.conn.decodemasternodebroadcast(work.strip())
        
        except Exception as e:
            err_msg = "error in decodemasternodebroadcast"
            printException(getCallerName(), getFunctionName(), err_msg, e.args)
            return ""
            
            
    
    def relaymasternodebroadcast(self, work):
        try:
            return self.conn.relaymasternodebroadcast(work.strip())
        
        except Exception as e:
            err_msg = "error in relaymasternodebroadcast"
            printException(getCallerName(), getFunctionName(), err_msg, e.args)    
            return ""


    def sendRawTransaction(self, tx_hex):
        try:
            tx_id = self.conn.sendrawtransaction(tx_hex)
            return tx_id
        except Exception as e:
            err_msg = 'error in rpcClient.sendRawTransaction'
            if str(e.args[0]) != "Request-sent":
                printException(getCallerName(), getFunctionName(), err_msg, e.args)
            else:
                printException(getCallerName(), getFunctionName(), err_msg, e.args)
    
    
    
    
    def verifyMessage(self, pivxaddress, signature, message):
        try:
            return self.conn.verifymessage(pivxaddress, signature, message)
        
        except Exception as e:
            err_msg = "error in verifyMessage"
            printException(getCallerName(), getFunctionName(), err_msg, e.args)
            
Beispiel #4
0
class RpcClient:
    def __init__(self):
        # Lock for threads
        self.lock = threading.Lock()
        self.rpc_ip, self.rpc_port, self.rpc_user, self.rpc_passwd = readRPCfile(
        )
        rpc_url = "http://%s:%s@%s:%d" % (self.rpc_user, self.rpc_passwd,
                                          self.rpc_ip, self.rpc_port)
        try:
            self.lock.acquire()
            self.conn = AuthServiceProxy(rpc_url, timeout=120)
        except JSONRPCException as e:
            err_msg = 'remote or local PIVX-cli running?'
            printException(getCallerName(), getFunctionName(), err_msg, e)
        except Exception as e:
            err_msg = 'remote or local PIVX-cli running?'
            printException(getCallerName(), getFunctionName(), err_msg, e)
        finally:
            self.lock.release()

    def decodeRawTransaction(self, rawTx):
        try:
            self.lock.acquire()
            res = self.conn.decoderawtransaction(rawTx)
        except Exception as e:
            err_msg = 'error in decodeRawTransaction'
            printException(getCallerName(), getFunctionName(), err_msg, e.args)
            res = None
        finally:
            self.lock.release()

        return res

    def getAddressUtxos(self, addresses):
        try:
            self.lock.acquire()
            res = self.conn.getaddressutxos({'addresses': addresses})
        except Exception as e:
            err_msg = "error in getAddressUtxos"
            printException(getCallerName(), getFunctionName(), err_msg, e.args)
            res = None
        finally:
            self.lock.release()

        return res

    def getBlockCount(self):
        try:
            self.lock.acquire()
            n = self.conn.getblockcount()
        except Exception as e:
            err_msg = 'remote or local PIVX-cli running?'
            if str(e.args[0]) != "Request-sent":
                printException(getCallerName(), getFunctionName(), err_msg,
                               e.args)
            n = 0
        finally:
            self.lock.release()

        return n

    def getBlockHash(self, blockNum):
        try:
            self.lock.acquire()
            h = self.conn.getblockhash(blockNum)
        except Exception as e:
            err_msg = 'remote or local PIVX-cli running?'
            printException(getCallerName(), getFunctionName(), err_msg, e.args)
            h = None
        finally:
            self.lock.release()

        return h

    def getBudgetVotes(self, proposal):
        try:
            self.lock.acquire()
            votes = self.conn.getbudgetvotes(proposal)
        except Exception as e:
            err_msg = 'remote or local PIVX-cli running?'
            printException(getCallerName(), getFunctionName(), err_msg, e.args)
            votes = {}
        finally:
            self.lock.release()

        return votes

    def getFeePerKb(self):
        try:
            self.lock.acquire()
            # get transaction data from last 200 blocks
            feePerKb = float(self.conn.getfeeinfo(200)['feeperkb'])
            res = (feePerKb if feePerKb > MINIMUM_FEE else MINIMUM_FEE)
        except Exception as e:
            err_msg = 'error in getFeePerKb'
            printException(getCallerName(), getFunctionName(), err_msg, e.args)
            res = MINIMUM_FEE
        finally:
            self.lock.release()

        return res

    def getMNStatus(self, address):
        try:
            self.lock.acquire()
            mnStatusList = self.conn.listmasternodes(address)
            if not mnStatusList:
                return None
            mnStatus = mnStatusList[0]
            mnStatus['mnCount'] = self.conn.getmasternodecount()['enabled']
        except Exception as e:
            err_msg = "error in getMNStatus"
            printException(getCallerName(), getFunctionName(), err_msg, e.args)
            mnStatus = None
        finally:
            self.lock.release()

        return mnStatus

    def getMasternodeCount(self):
        try:
            self.lock.acquire()
            ans = self.conn.getmasternodecount()
        except Exception as e:
            err_msg = "error in getMasternodeCount"
            printException(getCallerName(), getFunctionName(), err_msg, e.args)
            ans = None
        finally:
            self.lock.release()

        return ans

    def getMasternodes(self):
        mnList = {}
        mnList['last_update'] = now()
        score = []
        try:
            self.lock.acquire()
            masternodes = self.conn.listmasternodes()
        except Exception as e:
            err_msg = "error in getMasternodes"
            printException(getCallerName(), getFunctionName(), err_msg, e.args)
            masternodes = []
        finally:
            self.lock.release()

        for mn in masternodes:

            if mn.get('status') == 'ENABLED':
                if mn.get('lastpaid') == 0:
                    mn['score'] = mn.get('activetime')
                else:
                    lastpaid_ago = now() - mn.get('lastpaid')
                    mn['score'] = min(lastpaid_ago, mn.get('activetime'))

            else:
                mn['score'] = 0

            score.append(mn)

        score.sort(key=lambda x: x['score'], reverse=True)

        for mn in masternodes:
            mn['queue_pos'] = score.index(mn)

        mnList['masternodes'] = masternodes

        return mnList

    def getNextSuperBlock(self):
        try:
            self.lock.acquire()
            n = self.conn.getnextsuperblock()
        except Exception as e:
            err_msg = 'remote or local PIVX-cli running?'
            if str(e.args[0]) != "Request-sent":
                printException(getCallerName(), getFunctionName(), err_msg,
                               e.args)
            n = 0
        finally:
            self.lock.release()

        return n

    def getProposals(self):
        proposals = []
        try:
            self.lock.acquire()
            data = self.conn.getbudgetinfo()
        except Exception as e:
            err_msg = "error getting proposals"
            printException(getCallerName(), getFunctionName(), err_msg, e.args)
            data = []
        finally:
            self.lock.release()

        for p in data:
            new_proposal = Proposal(p.get('Name'), p.get('URL'), p.get('Hash'),
                                    p.get('FeeHash'), p.get('BlockStart'),
                                    p.get('BlockEnd'),
                                    p.get('TotalPaymentCount'),
                                    p.get('RemainingPaymentCount'),
                                    p.get('PaymentAddress'), p.get('Yeas'),
                                    p.get('Nays'), p.get('Abstains'),
                                    float(p.get('TotalPayment')),
                                    float(p.get('MonthlyPayment')))
            proposals.append(new_proposal)

        return proposals

    def getProposalsProjection(self):
        proposals = []
        try:
            self.lock.acquire()
            data = self.conn.getbudgetprojection()
        except Exception as e:
            err_msg = "error getting proposals projection"
            printException(getCallerName(), getFunctionName(), err_msg, e.args)
            data = []
        finally:
            self.lock.release()

        for p in data:
            new_proposal = Proposal(p.get('Name'), p.get('URL'), p.get('Hash'),
                                    p.get('FeeHash'), p.get('BlockStart'),
                                    p.get('BlockEnd'),
                                    p.get('TotalPaymentCount'),
                                    p.get('RemainingPaymentCount'),
                                    p.get('PaymentAddress'), p.get('Yeas'),
                                    p.get('Nays'), p.get('Abstains'),
                                    p.get('TotalPayment'),
                                    p.get('MonthlyPayment'))
            new_proposal = {}
            new_proposal['Name'] = p.get('Name')
            new_proposal['Allotted'] = float(p.get("Alloted"))
            new_proposal['Votes'] = p.get('Yeas') - p.get('Nays')
            new_proposal['Total_Allotted'] = float(p.get('TotalBudgetAlloted'))
            proposals.append(new_proposal)

        return proposals

    def getProtocolVersion(self):
        try:
            self.lock.acquire()
            prot_version = self.conn.getinfo().get('protocolversion')
            res = int(prot_version)
        except Exception as e:
            err_msg = 'error in getProtocolVersion'
            printException(getCallerName(), getFunctionName(), err_msg, e.args)
            res = DEFAULT_PROTOCOL_VERSION
        finally:
            self.lock.release()

        return res

    def getRawTransaction(self, txid):
        try:
            self.lock.acquire()
            res = self.conn.getrawtransaction(txid)
        except Exception as e:
            err_msg = "is Blockchain synced?"
            printException(getCallerName(), getFunctionName(), err_msg, e.args)
            res = None
        finally:
            self.lock.release()

        return res

    def getStatus(self):
        status = False
        statusMess = "Unable to connect to a PIVX RPC server.\n"
        statusMess += "Either the local PIVX wallet is not open, or the remote RPC server is not responding."
        n = 0
        try:
            self.lock.acquire()
            n = self.conn.getblockcount()
            if n > 0:
                status = True
                statusMess = "Connected to PIVX RPC client"

        except Exception as e:
            # If loading block index set lastBlock=1
            if str(e.args[0]) == "Loading block index..." or str(
                    e.args[0]) == "Verifying blocks...":
                printDbg(str(e.args[0]))
                statusMess = "PIVX wallet is connected but still synchronizing / verifying blocks"
                n = 1
            elif str(e.args[0]) != "Request-sent" and str(
                    e.args[0]) != "10061":
                err_msg = "Error while contacting RPC server"
                printException(getCallerName(), getFunctionName(), err_msg,
                               e.args)

        finally:
            self.lock.release()

        return status, statusMess, n

    def isBlockchainSynced(self):
        try:
            self.lock.acquire()
            res = self.conn.mnsync('status').get("IsBlockchainSynced")
        except Exception as e:
            if str(e.args[0]) != "Request-sent":
                err_msg = "error in isBlockchainSynced"
                printException(getCallerName(), getFunctionName(), err_msg,
                               e.args)
            res = False
        finally:
            self.lock.release()

        return res

    def mnBudgetRawVote(self, mn_tx_hash, mn_tx_index, proposal_hash, vote,
                        time, vote_sig):
        try:
            self.lock.acquire()
            res = self.conn.mnbudgetrawvote(mn_tx_hash, mn_tx_index,
                                            proposal_hash, vote, time,
                                            vote_sig)
        except Exception as e:
            err_msg = "error in mnBudgetRawVote"
            printException(getCallerName(), getFunctionName(), err_msg, e.args)
            res = None
        finally:
            self.lock.release()

        return res

    def decodemasternodebroadcast(self, work):
        try:
            self.lock.acquire()
            res = self.conn.decodemasternodebroadcast(work.strip())
        except Exception as e:
            err_msg = "error in decodemasternodebroadcast"
            printException(getCallerName(), getFunctionName(), err_msg, e.args)
            res = ""
        finally:
            self.lock.release()

        return res

    def relaymasternodebroadcast(self, work):
        try:
            self.lock.acquire()
            res = self.conn.relaymasternodebroadcast(work.strip())
        except Exception as e:
            err_msg = "error in relaymasternodebroadcast"
            printException(getCallerName(), getFunctionName(), err_msg, e.args)
            res = ""
        finally:
            self.lock.release()

        return res

    def sendRawTransaction(self, tx_hex, use_swiftx):
        try:
            self.lock.acquire()
            tx_id = self.conn.sendrawtransaction(tx_hex, True,
                                                 bool(use_swiftx))
        except Exception as e:
            err_msg = 'error in rpcClient.sendRawTransaction'
            printException(getCallerName(), getFunctionName(), err_msg, e.args)
            tx_id = None
        finally:
            self.lock.release()

        return tx_id

    def verifyMessage(self, pivxaddress, signature, message):
        try:
            self.lock.acquire()
            res = self.conn.verifymessage(pivxaddress, signature, message)
        except Exception as e:
            err_msg = "error in verifyMessage"
            printException(getCallerName(), getFunctionName(), err_msg, e.args)
            res = False
        finally:
            self.lock.release()

        return res
class DashdInterface(WndUtils):
    def __init__(self, window,
                 on_connection_initiated_callback=None,
                 on_connection_failed_callback=None,
                 on_connection_successful_callback=None,
                 on_connection_disconnected_callback=None):
        WndUtils.__init__(self, app_config=None)

        self.config = None
        self.db_intf = None
        self.connections = []
        self.cur_conn_index = 0
        self.cur_conn_def = None

        # below is the connection with which particular RPC call has started; if connection is switched because of
        # problems with some nodes, switching stops if we close round and return to the starting connection
        self.starting_conn = None

        self.masternodes = []  # cached list of all masternodes (Masternode object)
        self.masternodes_by_ident = {}
        self.payment_queue = []

        self.ssh = None
        self.window = window
        self.active = False
        self.rpc_url = None
        self.proxy = None
        self.http_conn = None  # HTTPConnection object passed to the AuthServiceProxy (for convinient connection reset)
        self.on_connection_initiated_callback = on_connection_initiated_callback
        self.on_connection_failed_callback = on_connection_failed_callback
        self.on_connection_successful_callback = on_connection_successful_callback
        self.on_connection_disconnected_callback = on_connection_disconnected_callback
        self.last_error_message = None
        self.http_lock = threading.Lock()

    def initialize(self, config: AppConfig, connection=None, for_testing_connections_only=False):
        self.config = config
        self.app_config = config
        self.db_intf = self.config.db_intf

        # conn configurations are used from the first item in the list; if one fails, then next is taken
        if connection:
            # this parameter is used for testing specific connection
            self.connections = [connection]
        else:
            # get connection list orderd by priority of use
            self.connections = self.config.get_ordered_conn_list()

        self.cur_conn_index = 0
        if self.connections:
            self.cur_conn_def = self.connections[self.cur_conn_index]
        else:
            self.cur_conn_def = None

        if not for_testing_connections_only:
            self.load_data_from_db_cache()

    def load_data_from_db_cache(self):
        self.masternodes.clear()
        self.masternodes_by_ident.clear()
        cur = self.db_intf.get_cursor()
        cur2 = self.db_intf.get_cursor()
        db_modified = False
        try:
            tm_start = time.time()
            db_correction_duration = 0.0
            logging.debug("Reading masternodes' data from DB")
            cur.execute("SELECT id, ident, status, protocol, payee, last_seen, active_seconds,"
                        " last_paid_time, last_paid_block, IP from MASTERNODES where dmt_active=1")
            for row in cur.fetchall():
                db_id = row[0]
                ident = row[1]

                # correct duplicated masternodes issue
                mn_first = self.masternodes_by_ident.get(ident)
                if mn_first is not None:
                    continue

                # delete duplicated (caused by breaking the app while loading)
                tm_start_1 = time.time()
                cur2.execute('DELETE from MASTERNODES where ident=? and id<>?', (ident, db_id))
                if cur2.rowcount > 0:
                    db_modified = True
                db_correction_duration += (time.time() - tm_start_1)

                mn = Masternode()
                mn.db_id = db_id
                mn.ident = ident
                mn.status = row[2]
                mn.protocol = row[3]
                mn.payee = row[4]
                mn.lastseen = row[5]
                mn.activeseconds = row[6]
                mn.lastpaidtime = row[7]
                mn.lastpaidblock = row[8]
                mn.ip = row[9]
                self.masternodes.append(mn)
                self.masternodes_by_ident[mn.ident] = mn

            tm_diff = time.time() - tm_start
            logging.info('DB read time of %d MASTERNODES: %s s, db fix time: %s' %
                         (len(self.masternodes), str(tm_diff), str(db_correction_duration)))
            self.update_mn_queue_values()
        except Exception as e:
            logging.exception('SQLite initialization error')
        finally:
            if db_modified:
                self.db_intf.commit()
            self.db_intf.release_cursor()
            self.db_intf.release_cursor()

    def reload_configuration(self):
        """Called after modification of connections' configuration or changes having impact on the file name
        associated to database cache."""

        # get connection list orderd by priority of use
        self.disconnect()
        self.connections = self.config.get_ordered_conn_list()
        self.cur_conn_index = 0
        if len(self.connections):
            self.cur_conn_def = self.connections[self.cur_conn_index]
            self.load_data_from_db_cache()
        else:
            self.cur_conn_def = None

    def disconnect(self):
        if self.active:
            logging.debug('Disconnecting')
            if self.ssh:
                self.ssh.disconnect()
                del self.ssh
                self.ssh = None
            self.active = False
            if self.on_connection_disconnected_callback:
                self.on_connection_disconnected_callback()

    def mark_call_begin(self):
        self.starting_conn = self.cur_conn_def

    def switch_to_next_config(self):
        """
        If there is another dashd config not used recently, switch to it. Called only when there was a problem
        with current connection config.
        :return: True if successfully switched or False if there was no another config
        """
        if self.cur_conn_def:
            self.config.conn_cfg_failure(self.cur_conn_def)  # mark connection as defective
        if self.cur_conn_index < len(self.connections)-1:
            idx = self.cur_conn_index + 1
        else:
            idx = 0

        conn = self.connections[idx]
        if conn != self.starting_conn and conn != self.cur_conn_def:
            logging.debug("Trying to switch to another connection: %s" % conn.get_description())
            self.disconnect()
            self.cur_conn_index = idx
            self.cur_conn_def = conn
            if not self.open():
                return self.switch_to_next_config()
            else:
                return True
        else:
            logging.warning('Failed to connect: no another connection configurations.')
            return False

    def mark_cur_conn_cfg_is_ok(self):
        if self.cur_conn_def:
            self.config.conn_cfg_success(self.cur_conn_def)

    def open(self):
        """
        Opens connection to dash RPC. If it fails, then the next enabled conn config will be used, if any exists.
        :return: True if successfully connected, False if user cancelled the operation. If all of the attempts 
            fail, then appropriate exception will be raised.
        """
        try:
            if not self.cur_conn_def:
                raise Exception('There is no connections to NIX network enabled in the configuration.')

            while True:
                try:
                    if self.open_internal():
                        break
                    else:
                        if not self.switch_to_next_config():
                            return False
                except UserCancelledConnection:
                    return False
                except (socket.gaierror, ConnectionRefusedError, TimeoutError, socket.timeout,
                        NoValidConnectionsError) as e:
                    # exceptions raised by not likely functioning dashd node; try to switch to another node
                    # if there is any in the config
                    if not self.switch_to_next_config():
                        raise e  # couldn't use another conn config, raise exception
                    else:
                        break
        except Exception as e:
            self.last_error_message = str(e)
            raise

        return True

    def reset_connection(self):
        """
        Called when communication errors are detected while sending RPC commands. Here we are closing the SSH-tunnel
        (if used) and HTTP connection object to prepare for another try.
        :return:
        """
        if self.active:
            if self.http_conn:
                self.http_conn.close()
            if self.ssh:
                self.ssh.disconnect()
                self.active = False

    def open_internal(self):
        """
        Try to establish connection to dash RPC daemon for current connection config.
        :return: True, if connection successfully establishes, False if user Cancels the operation (not always 
            cancelling will be possible - only when user is prompted for a password).
        """
        if not self.active:
            logging.debug("Trying to open connection: %s" % self.cur_conn_def.get_description())
            try:
                # make the owner know, we are connecting
                if self.on_connection_initiated_callback:
                    self.on_connection_initiated_callback()
            except:
                pass

            if self.cur_conn_def.use_ssh_tunnel:
                # RPC over SSH
                if self.ssh is None:
                    self.ssh = DashdSSH(self.cur_conn_def.ssh_conn_cfg.host, self.cur_conn_def.ssh_conn_cfg.port,
                                        self.cur_conn_def.ssh_conn_cfg.username)
                try:
                    logging.debug('starting ssh.connect')
                    self.ssh.connect()
                    logging.debug('finished ssh.connect')
                except Exception as e:
                    logging.error('error in ssh.connect')
                    try:
                        # make the owner know, connection attempt failed
                        if self.on_connection_failed_callback:
                            self.on_connection_failed_callback()
                    except:
                        logging.exception('on_connection_try_fail_callback call exception')
                    raise

                # configure SSH tunnel
                # get random local unprivileged port number to establish SSH tunnel
                success = False
                local_port = None
                for try_nr in range(1, 10):
                    try:
                        logging.debug(f'beginning ssh.open_tunnel, try: {try_nr}')
                        local_port = randint(2000, 50000)
                        self.ssh.open_tunnel(local_port,
                                             self.cur_conn_def.host,
                                             int(self.cur_conn_def.port))
                        success = True
                        break
                    except Exception as e:
                        logging.exception('error in ssh.open_tunnel loop: ' + str(e))
                logging.debug('finished ssh.open_tunnel loop')
                if not success:
                    logging.error('finished ssh.open_tunnel loop with error')
                    return False
                else:
                    rpc_user = self.cur_conn_def.username
                    rpc_password = self.cur_conn_def.password
                    rpc_host = '127.0.0.1'  # SSH tunnel on loopback
                    rpc_port = local_port
            else:
                # direct RPC
                rpc_host = self.cur_conn_def.host
                rpc_port = self.cur_conn_def.port
                rpc_user = self.cur_conn_def.username
                rpc_password = self.cur_conn_def.password

            if self.cur_conn_def.use_ssl:
                self.rpc_url = 'https://'
                self.http_conn = httplib.HTTPSConnection(rpc_host, rpc_port, timeout=5, context=ssl._create_unverified_context())
            else:
                self.rpc_url = 'http://'
                self.http_conn = httplib.HTTPConnection(rpc_host, rpc_port, timeout=5)

            self.rpc_url += rpc_user + ':' + rpc_password + '@' + rpc_host + ':' + str(rpc_port)
            logging.debug('AuthServiceProxy configured to: %s' % self.rpc_url)
            self.proxy = AuthServiceProxy(self.rpc_url, timeout=1000, connection=self.http_conn)

            try:
                # check the connection
                self.http_conn.connect()
                logging.debug('Successfully connected AuthServiceProxy')

                try:
                    # make the owner know, we successfully finished connection
                    if self.on_connection_successful_callback:
                        self.on_connection_successful_callback()
                except:
                    logging.exception('on_connection_finished_callback call exception')
            except:
                logging.exception('Connection failed')

                try:
                    # make the owner know, connection attempt failed
                    if self.on_connection_failed_callback:
                        self.on_connection_failed_callback()

                    if self.ssh:
                        # if there is a ssh connection established earlier, disconnect it because apparently it isn't
                        # functioning
                        self.ssh.disconnect()
                except:
                    logging.exception('on_connection_try_fail_callback call exception')
                raise
            finally:
                logging.debug('http_conn.close()')
                self.http_conn.close()
                # timeout hase been initially set to 5 seconds to perform 'quick' connection test
                self.http_conn.timeout = 20

            self.active = True
        return self.active

    def get_active_conn_description(self):
        if self.cur_conn_def:
            return self.cur_conn_def.get_description()
        else:
            return '???'

    @control_rpc_call
    def getblockcount(self):
        if self.open():
            return self.proxy.getblockcount()
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def getinfo(self, verify_node: bool = True):
        if self.open():
            info = self.proxy.getnetworkinfo()
            if verify_node:
                node_under_testnet = info.get('testnet')
                if self.config.is_testnet() and not node_under_testnet:
                    raise Exception('This RPC node works under NIX MAINNET, but your current configuration is '
                                    'for TESTNET.')
                elif self.config.is_mainnet() and node_under_testnet:
                    raise Exception('This RPC node works under NIX TESTNET, but your current configuration is '
                                    'for MAINNET.')
            return info
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def issynchronized(self):
        if self.open():
            # if connecting to HTTP(S) proxy do not check if dash daemon is synchronized
            if self.cur_conn_def.is_http_proxy():
                return True
            else:
                syn = self.proxy.ghostsync('status')
                return syn.get('IsSynced')
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def ghostsync(self):
        if self.open():
            # if connecting to HTTP(S) proxy do not call this function - it will not be exposed
            if self.cur_conn_def.is_http_proxy():
                return {}
            else:
                return self.proxy.ghostsync('status')
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def ghostnodebroadcast(self, what, hexto):
        if self.open():
            # if what == 'relay':
            if False:
                # FIXME: relay does not report correct status without 3rd parameter due to bug in nixd
                return self.proxy.ghostnodebroadcast(what, hexto, "not-safe")
            else:
                return self.proxy.ghostnodebroadcast(what, hexto)
        else:
            raise Exception('Not connected')

    def update_mn_queue_values(self):
        """
        Updates masternode payment queue order values.
        """

        start_tm = time.time()
        self.payment_queue = []
        d = datetime.datetime.utcnow()
        now = int(time.mktime((d.year, d.month, d.day, d.hour, d.minute, d.second, 0, 0, 0)))

        for mn in self.masternodes:
            if mn.status == 'ENABLED':
                # estimate payment queue position: after loading all masternodes
                # queue_position will be used to sort mn list and count the real queue position
                if mn.lastpaidtime == 0:
                    mn.queue_position = mn.activeseconds
                else:
                    lastpaid_ago = now - mn.lastpaidtime
                    mn.queue_position = min(lastpaid_ago, mn.activeseconds)
                self.payment_queue.append(mn)
            else:
                mn.queue_position = None

        duration1 = time.time() - start_tm
        self.payment_queue.sort(key=lambda x: x.queue_position, reverse=True)
        duration2 = time.time() - start_tm

        for mn in self.masternodes:
            if mn.status == 'ENABLED':
                mn.queue_position = self.payment_queue.index(mn)
            else:
                mn.queue_position = None
        duration3 = time.time() - start_tm
        logging.info('Masternode queue build time1: %s, time2: %s, time3: %s' %
                     (str(duration1), str(duration2), str(duration3)))

    @control_rpc_call
    def get_ghostnodelist(self, *args, data_max_age=MASTERNODES_CACHE_VALID_SECONDS):
        """
        Returns masternode list, read from the Dash network or from the internal cache.
        :param args: arguments passed to the 'ghostnodelist' RPC call
        :param data_max_age: maximum age (in seconds) of the cached masternode data to used; if the
            cache is older than 'data_max_age', then an RPC call is performed to load newer masternode data;
            value of 0 forces reading of the new data from the network
        :return: list of Masternode objects, matching the 'args' arguments
        """
        def parse_mns(mns_raw):
            """
            Parses dictionary of strings returned from the RPC to Masternode object list.
            :param mns_raw: Dict of masternodes in format of RPC ghostnodelist command
            :return: list of Masternode object
            """
            tm_begin = time.time()
            ret_list = []
            for mn_id in mns_raw.keys():
                mn_raw = mns_raw.get(mn_id)
                mn_raw = mn_raw.strip()
                elems = mn_raw.split()
                if len(elems) >= 8:
                    mn = Masternode()
                    # (status, protocol, payee, lastseen, activeseconds, lastpaidtime, pastpaidblock, ip)
                    mn.status, mn.protocol, mn.payee, mn.lastseen, mn.activeseconds, mn.lastpaidtime, \
                        mn.lastpaidblock, mn.ip = elems

                    mn.lastseen = int(mn.lastseen)
                    mn.activeseconds = int(mn.activeseconds)
                    mn.lastpaidtime = int(mn.lastpaidtime)
                    mn.lastpaidblock = int(mn.lastpaidblock)
                    mn.ident = mn_id
                    ret_list.append(mn)
            duration = time.time() - tm_begin
            logging.info('Parse ghostnodelist time: ' + str(duration))
            return ret_list

        def update_masternode_data(existing_mn, new_data, cursor):
            # update cached masternode's properties
            existing_mn.modified = False
            existing_mn.monitor_changes = True
            existing_mn.ident = new_data.ident
            existing_mn.status = new_data.status
            existing_mn.protocol = new_data.protocol
            existing_mn.payee = new_data.payee
            existing_mn.lastseen = new_data.lastseen
            existing_mn.activeseconds = new_data.activeseconds
            existing_mn.lastpaidtime = new_data.lastpaidtime
            existing_mn.lastpaidblock = new_data.lastpaidblock
            existing_mn.ip = new_data.ip

            # ... and finally update MN db record
            if cursor and existing_mn.modified:
                cursor.execute("UPDATE MASTERNODES set ident=?, status=?, protocol=?, payee=?,"
                               " last_seen=?, active_seconds=?, last_paid_time=?, "
                               " last_paid_block=?, ip=?"
                               "WHERE id=?",
                               (new_data.ident, new_data.status, new_data.protocol, new_data.payee,
                                new_data.lastseen, new_data.activeseconds, new_data.lastpaidtime,
                                new_data.lastpaidblock, new_data.ip, existing_mn.db_id))

        if self.open():

            if len(args) == 1 and args[0] == 'full':
                last_read_time = app_cache.get_value(f'MasternodesLastReadTime_{self.app_config.dash_network}', 0, int)
                logging.info("MasternodesLastReadTime: %d" % last_read_time)

                if self.masternodes and data_max_age > 0 and \
                   int(time.time()) - last_read_time < data_max_age:
                    logging.info('Using cached ghostnodelist (data age: %s)' % str(int(time.time()) - last_read_time))
                    return self.masternodes
                else:
                    logging.info('Loading masternode list from NIX daemon...')
                    mns = self.proxy.ghostnodelist(*args)
                    mns = parse_mns(mns)
                    logging.info('Finished loading masternode list')

                    # mark already cached masternodes to identify those to delete
                    for mn in self.masternodes:
                        mn.marker = False

                    # save masternodes to the db cache
                    db_modified = False
                    cur = None
                    try:
                        if self.db_intf.db_active:
                            cur = self.db_intf.get_cursor()

                        for mn in mns:
                            # check if newly-read masternode already exists in the cache
                            existing_mn = self.masternodes_by_ident.get(mn.ident)
                            if not existing_mn:
                                mn.marker = True
                                self.masternodes.append(mn)
                                self.masternodes_by_ident[mn.ident] = mn

                                if self.db_intf.db_active:
                                    cur.execute("INSERT INTO MASTERNODES(ident, status, protocol, payee, last_seen,"
                                            " active_seconds, last_paid_time, last_paid_block, ip, dmt_active,"
                                            " dmt_create_time) "
                                            "VALUES (?,?,?,?,?,?,?,?,?,?,?)",
                                            (mn.ident, mn.status, mn.protocol, mn.payee, mn.lastseen,
                                             mn.activeseconds, mn.lastpaidtime, mn.lastpaidblock, mn.ip, 1,
                                             datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
                                    mn.db_id = cur.lastrowid
                                    db_modified = True
                            else:
                                existing_mn.marker = True
                                update_masternode_data(existing_mn, mn, cur)
                                db_modified = True

                        # remove from the cache masternodes that no longer exist
                        for mn_index in reversed(range(len(self.masternodes))):
                            mn = self.masternodes[mn_index]

                            if not mn.marker:
                                if self.db_intf.db_active:
                                    cur.execute("UPDATE MASTERNODES set dmt_active=0, dmt_deactivation_time=?"
                                                "WHERE ID=?",
                                                (datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
                                                mn.db_id))
                                    db_modified = True
                                self.masternodes_by_ident.pop(mn.ident,0)
                                del self.masternodes[mn_index]

                        app_cache.set_value(f'MasternodesLastReadTime_{self.app_config.dash_network}', int(time.time()))
                        self.update_mn_queue_values()
                    finally:
                        if db_modified:
                            self.db_intf.commit()
                        if cur is not None:
                            self.db_intf.release_cursor()

                    return self.masternodes
            else:
                mns = self.proxy.ghostnodelist(*args)
                mns = parse_mns(mns)
                return mns
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def getaddressbalance(self, addresses):
        if self.open():
            return self.proxy.getaddressbalance({'addresses': addresses})
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def getaddressutxos(self, addresses):
        if self.open():
            return self.proxy.getaddressutxos({'addresses': addresses})
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def getrawtransaction(self, txid, verbose):
        if self.open():
            return json_cache_wrapper(self.proxy.getrawtransaction, self, 'tx-' + str(verbose) + '-' + txid)(txid, verbose)
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def getblockhash(self, blockid):
        if self.open():
            return json_cache_wrapper(self.proxy.getblockhash, self, 'blockhash-' + str(blockid))(blockid)
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def getblockheader(self, blockhash):
        if self.open():
            return json_cache_wrapper(self.proxy.getblockheader, self, 'blockheader-' + str(blockhash))(blockhash)
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def validateaddress(self, address):
        if self.open():
            return self.proxy.validateaddress(address)
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def decoderawtransaction(self, rawtx):
        if self.open():
            return self.proxy.decoderawtransaction(rawtx)
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def sendrawtransaction(self, tx, use_instant_send):
        if self.open():
            return self.proxy.sendrawtransaction(tx, False)
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def getcurrentvotes(self, hash):
        if self.open():
            return self.proxy.getcurrentvotes(hash)
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def gobject(self, *args):
        if self.open():
            return self.proxy.gobject(*args)
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def masternode(self, *args):
        if self.open():
            return self.proxy.masternode(*args)
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def getgovernanceinfo(self):
        if self.open():
            return self.proxy.getgovernanceinfo()
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def getsuperblockbudget(self, block_index):
        if self.open():
            return self.proxy.getsuperblockbudget(block_index)
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def voteraw(self, masternode_tx_hash, masternode_tx_index, governance_hash, vote_signal, vote, sig_time, vote_sig):
        if self.open():
            return self.proxy.voteraw(masternode_tx_hash, masternode_tx_index, governance_hash, vote_signal, vote,
                                      sig_time, vote_sig)
        else:
            raise Exception('Not connected')
Beispiel #6
0
class DashdInterface(WndUtils):
    def __init__(self, config, window):
        WndUtils.__init__(self)
        assert isinstance(config, AppConfig)
        self.config = config
        self.last_connect_method = config.dashd_connect_method
        self.ssh = None
        self.window = window
        self.active = False
        self.rpc_url = None
        self.proxy = None
        self.http_conn = None  # HTTPConnection object passed to the AuthServiceProxy (for convinient connection reset)

    def disconnect(self):
        if self.active:
            if self.last_connect_method == 'rpc_ssh' and self.ssh:
                self.ssh.disconnect()
                del self.ssh
                self.ssh = None
            self.active = False

    def open(self):
        # TODO: support openning dialogs from inside a thread
        if not self.active:
            rpc_host = None
            rpc_port = None
            rpc_user = None
            rpc_password = None

            if self.config.dashd_connect_method == 'rpc_ssh':
                # RPC over SSH
                while True:
                    password = SshPassCache.get_password(self.window, self.config.ros_ssh_username,
                                                         self.config.ros_ssh_host)
                    if not password:
                        return False

                    self.ssh = DashdSSH(self.config.ros_ssh_host, self.config.ros_ssh_port,
                                        self.config.ros_ssh_username, password)
                    try:
                        self.ssh.connect()
                        SshPassCache.save_password(self.config.ros_ssh_username, self.config.ros_ssh_host,
                                                   password)
                        break
                    except AuthenticationException as e:
                        self.errorMsg(str(e))
                    except TimeoutError as e:
                        self.errorMsg(str(e))
                        return False
                    except Exception as e:
                        self.errorMsg(str(e))

                # configure SSH tunnel
                # get random local unprivileged port number to establish SSH tunnel
                success = False
                local_port = None
                for try_nr in range(1, 10):
                    try:
                        local_port = randint(2000, 50000)
                        self.ssh.open_tunnel(local_port, self.config.ros_rpc_bind_ip,
                                             int(self.config.ros_rpc_bind_port))
                        success = True
                        break
                    except Exception as e:
                        pass
                if not success:
                    return False
                else:
                    rpc_user = self.config.ros_rpc_username
                    rpc_password = self.config.ros_rpc_password
                    rpc_host = '127.0.0.1'  # SSH tunnel on loopback
                    rpc_port = local_port
            elif self.config.dashd_connect_method == 'rpc':
                # direct RPC
                rpc_host = self.config.rpc_ip
                rpc_port = self.config.rpc_port
                rpc_user = self.config.rpc_user
                rpc_password = self.config.rpc_password
            else:
                raise Exception('Invalid connection method')

            self.rpc_url = 'http://' + rpc_user + ':' + rpc_password + '@' + rpc_host + ':' + str(rpc_port)
            self.http_conn = httplib.HTTPConnection(rpc_host, rpc_port, timeout=1000)
            self.proxy = AuthServiceProxy(self.rpc_url, timeout=1000, connection=self.http_conn)
            self.active = True
        return self.active

    @control_rpc_call
    def getblockcount(self):
        if self.open():
            return self.proxy.getblockcount()
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def getblockhash(self, block):
        if self.open():
            return self.proxy.getblockhash(block)
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def getinfo(self):
        if self.open():
            return self.proxy.getinfo()
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def issynchronized(self):
        if self.open():
            syn = self.proxy.mnsync('status')
            return syn.get('IsSynced')
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def masternodebroadcast(self, what, hexto):
        if self.open():
            return self.proxy.masternodebroadcast(what, hexto)
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def get_masternodelist(self):
        if self.open():
            return self.proxy.masternodelist()
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def get_masternodeaddr(self):
        if self.open():
            return self.proxy.masternodelist('addr')
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def getaddressbalance(self, address):
        if self.open():
            return self.proxy.getaddressbalance({'addresses': [address]}).get('balance')
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def getaddressutxos(self, addresses):
        if self.open():
            return self.proxy.getaddressutxos({'addresses': addresses})
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def getrawtransaction(self, txid, verbose):
        if self.open():
            return self.proxy.getrawtransaction(txid, verbose)
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def getblockhash(self, blockid):
        if self.open():
            return self.proxy.getblockhash(blockid)
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def getblockheader(self, blockhash):
        if self.open():
            return self.proxy.getblockheader(blockhash)
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def validateaddress(self, address):
        if self.open():
            return self.proxy.validateaddress(address)
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def decoderawtransaction(self, tx):
        if self.open():
            return self.proxy.decoderawtransaction(tx)
        else:
            raise Exception('Not connected')

    @control_rpc_call
    def sendrawtransaction(self, tx):
        if self.open():
            return self.proxy.sendrawtransaction(tx)
        else:
            raise Exception('Not connected')
Beispiel #7
0
class RpcClient:
    def __init__(self):
        # Lock for threads
        self.lock = threading.Lock()
        self.rpc_ip, self.rpc_port, self.rpc_user, self.rpc_passwd = readRPCfile(
        )
        rpc_url = "http://%s:%s@%s:%d" % (self.rpc_user, self.rpc_passwd,
                                          self.rpc_ip, self.rpc_port)
        try:
            self.lock.acquire()
            self.conn = AuthServiceProxy(rpc_url, timeout=8)
        except JSONRPCException as e:
            err_msg = 'remote or local PIVX-cli running?'
            printException(getCallerName(), getFunctionName(), err_msg, e)
        except Exception as e:
            err_msg = 'remote or local PIVX-cli running?'
            printException(getCallerName(), getFunctionName(), err_msg, e)
        finally:
            self.lock.release()

    def decodeRawTransaction(self, rawTx):
        try:
            self.lock.acquire()
            res = self.conn.decoderawtransaction(rawTx)
        except Exception as e:
            err_msg = 'error in decodeRawTransaction'
            printException(getCallerName(), getFunctionName(), err_msg, e.args)
            res = None
        finally:
            self.lock.release()

        return res

    def getAddressUtxos(self, addresses):
        try:
            self.lock.acquire()
            res = self.conn.getaddressutxos({'addresses': addresses})
        except Exception as e:
            err_msg = "error in getAddressUtxos"
            printException(getCallerName(), getFunctionName(), err_msg, e.args)
            res = None
        finally:
            self.lock.release()

        return res

    def getBlockCount(self):
        try:
            self.lock.acquire()
            n = self.conn.getblockcount()
        except Exception as e:
            err_msg = 'remote or local PIVX-cli running?'
            if str(e.args[0]) != "Request-sent":
                printException(getCallerName(), getFunctionName(), err_msg,
                               e.args)
            n = 0
        finally:
            self.lock.release()

        return n

    def getBlockHash(self, blockNum):
        try:
            self.lock.acquire()
            h = self.conn.getblockhash(blockNum)
        except Exception as e:
            err_msg = 'remote or local PIVX-cli running?'
            printException(getCallerName(), getFunctionName(), err_msg, e.args)
            h = None
        finally:
            self.lock.release()

        return h

    def getFeePerKb(self):
        try:
            self.lock.acquire()
            # get transaction data from last 10 blocks
            feePerKb = float(self.conn.getfeeinfo(10)['feeperkb'])
            res = (feePerKb if feePerKb > MINIMUM_FEE else MINIMUM_FEE)
        except Exception as e:
            err_msg = 'error in getFeePerKb'
            printException(getCallerName(), getFunctionName(), err_msg, e.args)
            res = MINIMUM_FEE
        finally:
            self.lock.release()

        return res

    def getMNStatus(self, address):
        try:
            self.lock.acquire()
            mnStatusList = self.conn.listmasternodes(address)
            if not mnStatusList:
                return None
            mnStatus = mnStatusList[0]
            mnStatus['mnCount'] = self.conn.getmasternodecount()['enabled']
        except Exception as e:
            err_msg = "error in getMNStatus"
            printException(getCallerName(), getFunctionName(), err_msg, e.args)
            mnStatus = None
        finally:
            self.lock.release()

        return mnStatus

    def getMasternodes(self):
        mnList = {}
        mnList['last_update'] = now()
        score = []
        try:
            self.lock.acquire()
            masternodes = self.conn.listmasternodes()
        except Exception as e:
            err_msg = "error in getMasternodes"
            printException(getCallerName(), getFunctionName(), err_msg, e.args)
            masternodes = []
        finally:
            self.lock.release()

        for mn in masternodes:

            if mn.get('status') == 'ENABLED':
                if mn.get('lastpaid') == 0:
                    mn['score'] = mn.get('activetime')
                else:
                    lastpaid_ago = now() - mn.get('lastpaid')
                    mn['score'] = min(lastpaid_ago, mn.get('activetime'))

            else:
                mn['score'] = 0

            score.append(mn)

        score.sort(key=lambda x: x['score'], reverse=True)

        for mn in masternodes:
            mn['queue_pos'] = score.index(mn)

        mnList['masternodes'] = masternodes

        return mnList

    def getProtocolVersion(self):
        try:
            self.lock.acquire()
            prot_version = self.conn.getinfo().get('protocolversion')
            res = int(prot_version)
        except Exception as e:
            err_msg = 'error in getProtocolVersion'
            printException(getCallerName(), getFunctionName(), err_msg, e.args)
            res = DEFAULT_PROTOCOL_VERSION
        finally:
            self.lock.release()

        return res

    def getRawTransaction(self, txid):
        try:
            self.lock.acquire()
            res = self.conn.getrawtransaction(txid)
        except Exception as e:
            err_msg = "is Blockchain synced?"
            printException(getCallerName(), getFunctionName(), err_msg, e.args)
            res = None
        finally:
            self.lock.release()

        return res

    def getStatus(self):
        status = False
        n = 0
        try:
            self.lock.acquire()
            n = self.conn.getblockcount()
            if n > 0:
                status = True

        except Exception as e:
            # If loading block index set lastBlock=1
            if str(e.args[0]) == "Loading block index..." or str(
                    e.args[0]) == "Verifying blocks...":
                printDbg(str(e.args[0]))
                n = 1
            else:
                err_msg = "Error while contacting RPC server"
                printException(getCallerName(), getFunctionName(), err_msg,
                               e.args)

        finally:
            self.lock.release()

        return status, n

    def getStatusMess(self, status=None):
        if status == None:
            status = self.getStatus()
        if status:
            return "Connected to PIVX RPC client"
        else:
            return "Unable to connect to a PIVX RPC server, needed to broadcast messages to the network. Either the local PIVX wallet is not open, or the remote RPC server is not responding."

    def isBlockchainSynced(self):
        try:
            self.lock.acquire()
            res = self.conn.mnsync('status').get("IsBlockchainSynced")
        except Exception as e:
            if str(e.args[0]) != "Request-sent":
                err_msg = "error in isBlockchainSynced"
                printException(getCallerName(), getFunctionName(), err_msg,
                               e.args)
            res = False
        finally:
            self.lock.release()

        return res

    def decodemasternodebroadcast(self, work):
        try:
            self.lock.acquire()
            res = self.conn.decodemasternodebroadcast(work.strip())
        except Exception as e:
            err_msg = "error in decodemasternodebroadcast"
            printException(getCallerName(), getFunctionName(), err_msg, e.args)
            res = ""
        finally:
            self.lock.release()

        return res

    def relaymasternodebroadcast(self, work):
        try:
            self.lock.acquire()
            res = self.conn.relaymasternodebroadcast(work.strip())
        except Exception as e:
            err_msg = "error in relaymasternodebroadcast"
            printException(getCallerName(), getFunctionName(), err_msg, e.args)
            res = ""
        finally:
            self.lock.release()

        return res

    def sendRawTransaction(self, tx_hex):
        try:
            self.lock.acquire()
            tx_id = self.conn.sendrawtransaction(tx_hex)
        except Exception as e:
            err_msg = 'error in rpcClient.sendRawTransaction'
            printException(getCallerName(), getFunctionName(), err_msg, e.args)
            tx_id = None
        finally:
            self.lock.release()

        return tx_id

    def verifyMessage(self, pivxaddress, signature, message):
        try:
            self.lock.acquire()
            res = self.conn.verifymessage(pivxaddress, signature, message)
        except Exception as e:
            err_msg = "error in verifyMessage"
            printException(getCallerName(), getFunctionName(), err_msg, e.args)
            res = False
        finally:
            self.lock.release()

        return res
Beispiel #8
0
class RpcClient:
    @process_RPC_exceptions
    def __init__(self, rpc_protocol, rpc_host, rpc_user, rpc_password):
        # Lock for threads
        self.lock = threading.RLock()

        self.rpc_url = "%s://%s:%s@%s" % (rpc_protocol, rpc_user, rpc_password,
                                          rpc_host)

        host, port = rpc_host.split(":")
        if rpc_protocol == "https":
            self.httpConnection = httplib.HTTPSConnection(
                host,
                port,
                timeout=20,
                context=ssl._create_unverified_context())
        else:
            self.httpConnection = httplib.HTTPConnection(host,
                                                         port,
                                                         timeout=20)

        self.conn = AuthServiceProxy(self.rpc_url,
                                     timeout=1000,
                                     connection=self.httpConnection)
        self.httpConnection.connect()

    @process_RPC_exceptions
    def decodeRawTransaction(self, rawTx):
        res = None
        with self.lock:
            res = self.conn.decoderawtransaction(rawTx)

        return res

    @process_RPC_exceptions
    def getAddressUtxos(self, addresses):
        res = None
        with self.lock:
            res = self.conn.getaddressutxos({'addresses': addresses})

        return res

    @process_RPC_exceptions
    def getBlockCount(self):
        n = 0
        with self.lock:
            n = self.conn.getblockcount()

        return n

    @process_RPC_exceptions
    def getBlockHash(self, blockNum):
        h = None
        with self.lock:
            h = self.conn.getblockhash(blockNum)

        return h

    @process_RPC_exceptions
    def getBudgetVotes(self, proposal):
        votes = {}
        with self.lock:
            votes = self.conn.getbudgetvotes(proposal)

        return votes

    @process_RPC_exceptions
    def getFeePerKb(self):
        res = MINIMUM_FEE
        with self.lock:
            # get transaction data from last 200 blocks
            feePerKb = float(self.conn.getfeeinfo(200)['feeperkb'])
            res = (feePerKb if feePerKb > MINIMUM_FEE else MINIMUM_FEE)

        return res

    @process_RPC_exceptions
    def getMNStatus(self, address):
        mnStatus = None
        with self.lock:
            mnStatusList = self.conn.listmasternodes(address)
            if not mnStatusList:
                return None
            mnStatus = mnStatusList[0]
            mnStatus['mnCount'] = self.conn.getmasternodecount()['enabled']

        return mnStatus

    @process_RPC_exceptions
    def getMasternodeCount(self):
        ans = None
        with self.lock:
            ans = self.conn.getmasternodecount()

        return ans

    @process_RPC_exceptions
    def getMasternodes(self):
        mnList = {}
        score = []
        masternodes = []
        with self.lock:
            masternodes = self.conn.listmasternodes()

        for mn in masternodes:
            if mn.get('status') == 'ENABLED':
                # compute masternode score
                if mn.get('lastpaid') == 0:
                    mn['score'] = mn.get('activetime')
                else:
                    lastpaid_ago = now() - mn.get('lastpaid')
                    mn['score'] = min(lastpaid_ago, mn.get('activetime'))

            else:
                mn['score'] = 0

            score.append(mn)

        # sort masternodes by decreasing score
        score.sort(key=lambda x: x['score'], reverse=True)

        # save masternode position in the payment queue
        for mn in masternodes:
            mn['queue_pos'] = score.index(mn)

        mnList['masternodes'] = masternodes

        return mnList

    @process_RPC_exceptions
    def getNextSuperBlock(self):
        n = 0
        with self.lock:
            n = self.conn.getnextsuperblock()

        return n

    @process_RPC_exceptions
    def getProposals(self):
        proposals = []
        data = []
        with self.lock:
            # get proposals JSON data
            data = self.conn.getbudgetinfo()

        for p in data:
            # create proposal Object
            new_proposal = Proposal(p.get('Name'), p.get('URL'), p.get('Hash'),
                                    p.get('FeeHash'), p.get('BlockStart'),
                                    p.get('BlockEnd'),
                                    p.get('TotalPaymentCount'),
                                    p.get('RemainingPaymentCount'),
                                    p.get('PaymentAddress'), p.get('Yeas'),
                                    p.get('Nays'), p.get('Abstains'),
                                    float(p.get('TotalPayment')),
                                    float(p.get('MonthlyPayment')))
            # append object to list
            proposals.append(new_proposal)

        # return proposals list
        return proposals

    @process_RPC_exceptions
    def getProposalsProjection(self):
        data = []
        proposals = []
        with self.lock:
            # get budget projection JSON data
            data = self.conn.getbudgetprojection()

        for p in data:
            # create proposal-projection dictionary
            new_proposal = {}
            new_proposal['Name'] = p.get('Name')
            new_proposal['Allotted'] = float(p.get("Alloted"))
            new_proposal['Votes'] = p.get('Yeas') - p.get('Nays')
            new_proposal['Total_Allotted'] = float(p.get('TotalBudgetAlloted'))
            # append dictionary to list
            proposals.append(new_proposal)

        # return proposals list
        return proposals

    @process_RPC_exceptions
    def getProtocolVersion(self):
        res = DEFAULT_PROTOCOL_VERSION
        with self.lock:
            prot_version = self.conn.getinfo().get('protocolversion')
            res = int(prot_version)

        return res

    @process_RPC_exceptions
    def getRawTransaction(self, txid):
        res = None
        with self.lock:
            res = self.conn.getrawtransaction(txid)

        return res

    @process_RPC_exceptions
    def getStatus(self):
        status = False
        statusMess = "Unable to connect to a PIVX RPC server.\n"
        statusMess += "Either the local PIVX wallet is not open, or the remote RPC server is not responding."
        n = 0
        response_time = None
        with self.lock:
            n, response_time = timeThis(self.conn.getblockcount)
            if n is None:
                n = 0

        if n > 0:
            status = True
            statusMess = "Connected to PIVX Blockchain"

        return status, statusMess, n, response_time

    @process_RPC_exceptions
    def isBlockchainSynced(self):
        res = False
        response_time = None
        with self.lock:
            status, response_time = timeThis(self.conn.mnsync, 'status')
            if status is not None:
                res = status.get("IsBlockchainSynced")

        return res, response_time

    @process_RPC_exceptions
    def mnBudgetRawVote(self, mn_tx_hash, mn_tx_index, proposal_hash, vote,
                        time, vote_sig):
        res = None
        with self.lock:
            res = self.conn.mnbudgetrawvote(mn_tx_hash, mn_tx_index,
                                            proposal_hash, vote, time,
                                            vote_sig)

        return res

    @process_RPC_exceptions
    def decodemasternodebroadcast(self, work):
        res = ""
        with self.lock:
            res = self.conn.decodemasternodebroadcast(work.strip())

        return res

    @process_RPC_exceptions
    def relaymasternodebroadcast(self, work):
        res = ""
        with self.lock:
            res = self.conn.relaymasternodebroadcast(work.strip())

        return res

    @process_RPC_exceptions
    def sendRawTransaction(self, tx_hex, use_swiftx):
        tx_id = None
        with self.lock:
            tx_id = self.conn.sendrawtransaction(tx_hex, True,
                                                 bool(use_swiftx))

        return tx_id

    @process_RPC_exceptions
    def verifyMessage(self, pivxaddress, signature, message):
        res = False
        with self.lock:
            res = self.conn.verifymessage(pivxaddress, signature, message)

        return res