Skip to content
Snippets Groups Projects
Select Git revision
  • bcc0a50b49c8c8b9a5588685de2f7071d83af0aa
  • master default protected
  • fix_picked_up_file_in_runtime_release
  • network/gtest-1000 protected
  • upgradable-multisig
  • runtime/gtest-1000
  • network/gdev-800 protected
  • cgeek/issue-297-cpu
  • gdev-800-tests
  • update-docker-compose-rpc-squid-names
  • fix-252
  • 1000i100-test
  • hugo/tmp-0.9.1
  • network/gdev-803 protected
  • hugo/endpoint-gossip
  • network/gdev-802 protected
  • hugo/distance-precompute
  • network/gdev-900 protected
  • tuxmain/anonymous-tx
  • debug/podman
  • hugo/195-doc
  • gtest-1000-0.11.1 protected
  • gtest-1000-0.11.0 protected
  • gtest-1000 protected
  • gdev-900-0.10.1 protected
  • gdev-900-0.10.0 protected
  • gdev-900-0.9.2 protected
  • gdev-800-0.8.0 protected
  • gdev-900-0.9.1 protected
  • gdev-900-0.9.0 protected
  • gdev-803 protected
  • gdev-802 protected
  • runtime-801 protected
  • gdev-800 protected
  • runtime-800-bis protected
  • runtime-800 protected
  • runtime-800-backup protected
  • runtime-701 protected
  • runtime-700 protected
  • runtime-600 protected
  • runtime-500 protected
41 results

weights-benchmarking.md

Blame
    • Benjamin Gallois's avatar
      cca16dbb
      Eliminate all Instances of `Weight::zero()` (!197) · cca16dbb
      Benjamin Gallois authored and Hugo Trentesaux's avatar Hugo Trentesaux committed
      * refactor functions naming for pallet-membership
      
      * refactor functions naming for pallet-certification
      
      * fix tests with runtime-benchmark feature
      
      * fix compilation with runtime-benchmarks feature
      
      * update documentation
      
      * remove handlers weights for pallet_membership
      
      * remove handlers weights for pallet_identity
      
      * remove handlers weights for pallet_certification
      
      * add best practices for benchmarking
      
      * add hooks benchmark pallet_universal_dividend
      
      * add missing benchmarks pallet_universal_dividend
      
      * update weights
      
      * fix pallet_provide_randomness on_initialize weight
      
      * fix pallet_identity weight::zero
      
      * fix pallet_membership weight::zero
      
      * fix pallet_certification weight::zero
      
      * fix pallet_authority_members weight::zero
      
      * fix pallet_identity weight::zero
      
      * fix pallet_membership weight::zero
      cca16dbb
      History
      Eliminate all Instances of `Weight::zero()` (!197)
      Benjamin Gallois authored and Hugo Trentesaux's avatar Hugo Trentesaux committed
      * refactor functions naming for pallet-membership
      
      * refactor functions naming for pallet-certification
      
      * fix tests with runtime-benchmark feature
      
      * fix compilation with runtime-benchmarks feature
      
      * update documentation
      
      * remove handlers weights for pallet_membership
      
      * remove handlers weights for pallet_identity
      
      * remove handlers weights for pallet_certification
      
      * add best practices for benchmarking
      
      * add hooks benchmark pallet_universal_dividend
      
      * add missing benchmarks pallet_universal_dividend
      
      * update weights
      
      * fix pallet_provide_randomness on_initialize weight
      
      * fix pallet_identity weight::zero
      
      * fix pallet_membership weight::zero
      
      * fix pallet_certification weight::zero
      
      * fix pallet_authority_members weight::zero
      
      * fix pallet_identity weight::zero
      
      * fix pallet_membership weight::zero
    account.py 23.05 KiB
    """
    Created on 1 févr. 2014
    
    @author: inso
    """
    
    from ucoinpy.documents.certification import SelfCertification, Certification, Revocation
    from ucoinpy.documents.membership import Membership
    from ucoinpy.key import SigningKey
    
    import logging
    import time
    import asyncio
    
    from PyQt5.QtCore import QObject, pyqtSignal, QCoreApplication, QT_TRANSLATE_NOOP
    from PyQt5.QtNetwork import QNetworkReply
    
    from .wallet import Wallet
    from .community import Community
    from .registry import LocalState
    from ..tools.exceptions import ContactAlreadyExists
    from ..core.net.api import bma as qtbma
    from ..core.net.api.bma import PROTOCOL_VERSION
    
    
    def quantitative(units, community):
        """
        Return quantitative value of units
    
        :param int units:   Value
        :param cutecoin.core.community.Community community: Community instance
        :return: int
        """
        return int(units)
    
    
    def relative(units, community):
        """
        Return relaive value of units
    
        :param int units:   Value
        :param cutecoin.core.community.Community community: Community instance
        :return: float
        """
        if community.dividend > 0:
            return units / float(community.dividend)
        else:
            return 0
    
    
    def quantitative_zerosum(units, community):
        """
        Return quantitative value of units minus the average value
    
        :param int units:   Value
        :param cutecoin.core.community.Community community: Community instance
        :return: int
        """
        ud_block = community.get_ud_block()
        if ud_block and ud_block['membersCount'] > 0:
            average = community.monetary_mass / ud_block['membersCount']
        else:
            average = 0
        return units - average
    
    
    def relative_zerosum(units, community):
        """
        Return relative value of units minus the average value
    
        :param int units:   Value
        :param cutecoin.core.community.Community community: Community instance
        :return: float
        """
        ud_block = community.get_ud_block()
        if ud_block and ud_block['membersCount'] > 0:
            median = community.monetary_mass / ud_block['membersCount']
            relative_value = units / float(community.dividend)
            relative_median = median / community.dividend
        else:
            relative_median = 0
        return relative_value - relative_median
    
    
    class Account(QObject):
        """
        An account is specific to a key.
        Each account has only one key, and a key can
        be locally referenced by only one account.
        """
        # referentials are defined here
        # it is a list of tupple, each tupple contains :
        # (
        #   function used to calculate value,
        #   format string to display value,
        #   function used to calculate on differential value,
        #   format string to display differential value,
        #   translated name of referential,
        #   type relative "r" or quantitative "q" to help choose precision on display
        # )
        referentials = (
            (quantitative, '{0}', quantitative, '{0}', QT_TRANSLATE_NOOP('Account', 'Units'), 'q'),
            (relative, QT_TRANSLATE_NOOP('Account', 'UD {0}'), relative, QT_TRANSLATE_NOOP('Account', 'UD {0}'),
             QT_TRANSLATE_NOOP('Account', 'UD'), 'r'),
            (quantitative_zerosum, QT_TRANSLATE_NOOP('Account', 'Q0 {0}'), quantitative, '{0}',
             QT_TRANSLATE_NOOP('Account', 'Quant Z-sum'), 'q'),
            (relative_zerosum, QT_TRANSLATE_NOOP('Account', 'R0 {0}'), relative, QT_TRANSLATE_NOOP('Account', 'UD {0}'),
             QT_TRANSLATE_NOOP('Account', 'Relat Z-sum'), 'r')
        )
    
        loading_progressed = pyqtSignal(int, int)
        loading_finished = pyqtSignal(list)
        inner_data_changed = pyqtSignal(str)
        wallets_changed = pyqtSignal()
        membership_broadcasted = pyqtSignal()
        certification_broadcasted = pyqtSignal()
        selfcert_broadcasted = pyqtSignal()
        revoke_broadcasted = pyqtSignal()
        broadcast_error = pyqtSignal(int, str)
    
        def __init__(self, salt, pubkey, name, communities, wallets, contacts, identities_registry):
            """
            Create an account
    
            :param str salt: The root key salt
            :param str pubkey: Known account pubkey. Used to check that password \
             is OK by comparing (salt, given_passwd) = (pubkey, privkey) \
             with known pubkey
            :param str name: The account name, same as network identity uid
            :param list of cutecoin.core.Community communities: Community objects referenced by this account
            :param list of cutecoin.core.Wallet wallets: Wallet objects owned by this account
            :param list of dict contacts: Contacts of this account
            :param cutecoin.core.registry.IdentitiesRegistry: The identities registry intance
    
            .. warnings:: The class methods create and load should be used to create an account
            """
            super().__init__()
            self.salt = salt
            self.pubkey = pubkey
            self.name = name
            self.communities = communities
            self.wallets = wallets
            self.contacts = contacts
            self._refreshing = False
            self._identities_registry = identities_registry
            self.referential = 0
    
        @classmethod
        def create(cls, name, identities_registry):
            """
            Factory method to create an empty account object
            This new account doesn't have any key and it should be given
            one later
            It doesn't have any community nor does it have wallets.
            Communities could be added later, wallets will be managed
            by its wallet pool size.
    
            :param str name: The account name, same as network identity uid
            :return: A new empty account object
            """
            account = cls(None, None, name, [], [], [], identities_registry)
            return account
    
        @classmethod
        def load(cls, json_data, network_manager, identities_registry):
            """
            Factory method to create an Account object from its json view.
            :rtype : cutecoin.core.account.Account
            :param dict json_data: The account view as a json dict
            :param PyQt5.QtNetwork import QNetworkManager: network_manager
            :param cutecoin.core.registry.self._identities_registry: identities_registry
            :return: A new account object created from the json datas
            """
            salt = json_data['salt']
            pubkey = json_data['pubkey']
    
            name = json_data['name']
            contacts = []
    
            for contact_data in json_data['contacts']:
                contacts.append(contact_data)
    
            wallets = []
            for data in json_data['wallets']:
                wallets.append(Wallet.load(data, identities_registry))
    
            communities = []
            for data in json_data['communities']:
                community = Community.load(network_manager, data)
                communities.append(community)
    
            account = cls(salt, pubkey, name, communities, wallets,
                          contacts, identities_registry)
            return account
    
        def __eq__(self, other):
            """
            :return: True if account.pubkey == other.pubkey
            """
            if other is not None:
                return other.pubkey == self.pubkey
            else:
                return False
    
        def check_password(self, password):
            """
            Method to verify the key password validity
    
            :param str password: The key password
            :return: True if the generated pubkey is the same as the account
            .. warnings:: Generates a new temporary SigningKey
            """
            key = SigningKey(self.salt, password)
            return (key.pubkey == self.pubkey)
    
        def add_contact(self, new_contact):
            same_contact = [contact for contact in self.contacts
                            if new_contact['pubkey'] == contact['pubkey']]
    
            if len(same_contact) > 0:
                raise ContactAlreadyExists(new_contact['name'], same_contact[0]['name'])
            self.contacts.append(new_contact)
    
        def add_community(self, community):
            """
            Add a community to the account
    
            :param community: A community object to add
            """
            self.communities.append(community)
            return community
    
        def refresh_transactions(self, app, community):
            """
            Refresh the local account cache
            This needs n_wallets * n_communities cache refreshing to end
    
            .. note:: emit the Account pyqtSignal loading_progressed during refresh
            """
            logging.debug("Start refresh transactions")
            if not self._refreshing:
                self._refreshing = True
                loaded_wallets = 0
                received_list = []
                values = {}
                maximums = {}
    
                def progressing(value, maximum, hash):
                    logging.debug("Loading = {0} : {1} : {2}".format(value, maximum, loaded_wallets))
                    values[hash] = value
                    maximums[hash] = maximum
                    account_value = sum(values.values())
                    account_max = sum(maximums.values())
                    self.loading_progressed.emit(account_value, account_max)
    
                def wallet_finished(received):
                    logging.debug("Finished loading wallet")
                    nonlocal loaded_wallets
                    loaded_wallets += 1
                    if loaded_wallets == len(self.wallets):
                        logging.debug("All wallets loaded")
                        self._refreshing = False
                        self.loading_finished.emit(received_list)
                        for w in self.wallets:
                            w.refresh_progressed.disconnect(progressing)
                            w.refresh_finished.disconnect(wallet_finished)
    
                for w in self.wallets:
                    w.refresh_progressed.connect(progressing)
                    w.refresh_finished.connect(wallet_finished)
                    w.init_cache(app, community)
                    w.refresh_transactions(community, received_list)
    
        def set_display_referential(self, index):
            self.referential = index
    
        def identity(self, community):
            """
            Get the account identity in the specified community
            :param cutecoin.core.community.Community community: The community where to look after the identity
            :return: The account identity in the community
            :rtype: cutecoin.core.registry.Identity
            """
            identity = self._identities_registry.find(self.pubkey, community)
            if identity.local_state == LocalState.NOT_FOUND:
                identity.uid = self.name
            return identity
    
        @property
        def units_to_ref(self):
            return Account.referentials[self.referential][0]
    
        @property
        def units_to_diff_ref(self):
            return Account.referentials[self.referential][2]
    
        def ref_name(self, currency):
            text = QCoreApplication.translate('Account',
                                              Account.referentials[self.referential][1])
            return text.format(currency)
    
        def diff_ref_name(self, currency):
            text = QCoreApplication.translate('Account', Account.referentials[self.referential][3])
            return text.format(currency)
    
        def ref_type(self):
            """
            Return type of referential ('q' or 'r', for quantitative or relative)
            :return: str
            """
            return Account.referentials[self.referential][5]
    
        def set_walletpool_size(self, size, password):
            """
            Change the size of the wallet pool
    
            :param int size: The new size of the wallet pool
            :param str password: The password of the account, same for all wallets
            """
            logging.debug("Defining wallet pool size")
            if len(self.wallets) < size:
                for i in range(len(self.wallets), size):
                    wallet = Wallet.create(i, self.salt, password,
                                           "Wallet {0}".format(i), self._identities_registry)
                    self.wallets.append(wallet)
            else:
                self.wallets = self.wallets[:size]
            self.wallets_changed.emit()
    
        def transfers(self, community):
            """
            Get all transfers done in a community by all the wallets
            owned by this account
    
            :param community: The target community of this request
            :return: All account wallets transfers
            """
            sent = []
            for w in self.wallets:
                sent.extend(w.transfers(community))
            return sent
    
        def dividends(self, community):
            """
            Get all dividends received in this community
            by the first wallet of this account
    
            :param community: The target community
            :return: All account dividends
            """
            return self.wallets[0].dividends(community)
    
        @asyncio.coroutine
        def future_amount(self, community):
            """
            Get amount of money owned in a community by all the wallets
            owned by this account
    
            :param community: The target community of this request
            :return: The value of all wallets values accumulated
            """
            value = 0
            for w in self.wallets:
                val = yield from w.future_value(community)
                value += val
            return value
    
        def amount(self, community):
            """
            Get amount of money owned in a community by all the wallets
            owned by this account
    
            :param community: The target community of this request
            :return: The value of all wallets values accumulated
            """
            value = 0
            for w in self.wallets:
                val = w.value(community)
                value += val
            return value
    
        @asyncio.coroutine
        def send_selfcert(self, password, community):
            """
            Send our self certification to a target community
    
            :param str password: The account SigningKey password
            :param community: The community target of the self certification
            """
            selfcert = SelfCertification(PROTOCOL_VERSION,
                                         community.currency,
                                         self.pubkey,
                                         int(time.time()),
                                         self.name,
                                         None)
            key = SigningKey(self.salt, password)
            selfcert.sign([key])
            logging.debug("Key publish : {0}".format(selfcert.signed_raw()))
            replies = community.broadcast(qtbma.wot.Add, {}, {'pubkey': self.pubkey,
                                                  'self_': selfcert.signed_raw(),
                                                  'other': []})
            for r in replies:
                r.finished.connect(lambda reply=r: self.__handle_selfcert_replies(replies, reply))
    
        def __handle_selfcert_replies(self, replies, reply):
            """
            Handle the reply, if the request was accepted, disconnect
            all other replies
    
            :param QNetworkReply reply: The reply of this handler
            :param list of QNetworkReply replies: All request replies
            :return:
            """
            strdata = bytes(reply.readAll()).decode('utf-8')
            logging.debug("Received reply : {0} : {1}".format(reply.error(), strdata))
            if reply.error() == QNetworkReply.NoError:
                for r in replies:
                    try:
                        r.disconnect()
                    except TypeError as e:
                        if "disconnect()" in str(e):
                            logging.debug("Could not disconnect a reply")
                        else:
                            raise
                self.selfcert_broadcasted.emit()
            else:
                for r in replies:
                    if not r.isFinished() or r.error() == QNetworkReply.NoError:
                        return
                self.broadcast_error.emit(r.error(), strdata)
    
        @asyncio.coroutine
        def send_membership(self, password, community, mstype):
            """
            Send a membership document to a target community.
            Signal "document_broadcasted" is emitted at the end.
    
            :param str password: The account SigningKey password
            :param community: The community target of the membership document
            :param str mstype: The type of membership demand. "IN" to join, "OUT" to leave
            """
            logging.debug("Send membership")
    
            blockid = yield from community.blockid()
            self_identity = yield from self._identities_registry.future_find(self.pubkey, community)
            selfcert = yield from self_identity.selfcert(community)
    
            membership = Membership(PROTOCOL_VERSION, community.currency,
                                    selfcert.pubkey, blockid['number'],
                                    blockid['hash'], mstype, selfcert.uid,
                                    selfcert.timestamp, None)
            key = SigningKey(self.salt, password)
            membership.sign([key])
            logging.debug("Membership : {0}".format(membership.signed_raw()))
            replies = community.bma_access.broadcast(qtbma.blockchain.Membership, {},
                                {'membership': membership.signed_raw()})
            for r in replies:
                r.finished.connect(lambda reply=r: self.__handle_membership_replies(replies, reply))
    
        def __handle_membership_replies(self, replies, reply):
            """
            Handle the reply, if the request was accepted, disconnect
            all other replies
    
            :param QNetworkReply reply: The reply of this handler
            :param list of QNetworkReply replies: All request replies
            :return:
            """
            strdata = bytes(reply.readAll()).decode('utf-8')
            logging.debug("Received reply : {0} : {1}".format(reply.error(), strdata))
            if reply.error() == QNetworkReply.NoError:
                for r in replies:
                    try:
                        r.disconnect()
                    except TypeError as e:
                        if "disconnect()" in str(e):
                            logging.debug("Could not disconnect a reply")
                        else:
                            raise
                self.membership_broadcasted.emit()
            else:
                for r in replies:
                    if not r.isFinished() or r.error() == QNetworkReply.NoError:
                        return
                self.broadcast_error.emit(r.error(), strdata)
    
        @asyncio.coroutine
        def certify(self, password, community, pubkey):
            """
            Certify an other identity
    
            :param str password: The account SigningKey password
            :param cutecoin.core.community.Community community: The community target of the certification
            :param str pubkey: The certified identity pubkey
            """
            logging.debug("Certdata")
            blockid = yield from community.blockid()
            identity = yield from self._identities_registry.future_find(pubkey, community)
            selfcert = yield from identity.selfcert(community)
            certification = Certification(PROTOCOL_VERSION, community.currency,
                                          self.pubkey, pubkey,
                                          blockid['number'], blockid['hash'], None)
    
            key = SigningKey(self.salt, password)
            certification.sign(selfcert, [key])
            signed_cert = certification.signed_raw(selfcert)
            logging.debug("Certification : {0}".format(signed_cert))
    
            data = {'pubkey': pubkey,
                    'self_': selfcert.signed_raw(),
                    'other': "{0}\n".format(certification.inline())}
            logging.debug("Posted data : {0}".format(data))
            replies = community.bma_access.broadcast(qtbma.wot.Add, {}, data)
            for r in replies:
                r.finished.connect(lambda reply=r: self.__handle_certification_reply(replies, reply))
    
        def __handle_certification_reply(self, replies, reply):
            """
            Handle the reply, if the request was accepted, disconnect
            all other replies
    
            :param QNetworkReply reply: The reply of this handler
            :param list of QNetworkReply replies: All request replies
            :return:
            """
            strdata = bytes(reply.readAll()).decode('utf-8')
            logging.debug("Received reply : {0} : {1}".format(reply.error(), strdata))
            if reply.error() == QNetworkReply.NoError:
                for r in replies:
                    try:
                        r.disconnect()
                    except TypeError as e:
                        if "disconnect()" in str(e):
                            logging.debug("Could not disconnect a reply")
                        else:
                            raise
                self.certification_broadcasted.emit()
            else:
                for r in replies:
                    if not r.isFinished() or r.error() == QNetworkReply.NoError:
                        return
                self.broadcast_error.emit(r.error(), strdata)
    
        @asyncio.coroutine
        def revoke(self, password, community):
            """
            Revoke self-identity on server, not in blockchain
    
            :param str password: The account SigningKey password
            :param cutecoin.core.community.Community community: The community target of the revocation
            """
            revoked = yield from self._identities_registry.future_find(self.pubkey, community)
    
            revocation = Revocation(PROTOCOL_VERSION, community.currency, None)
            selfcert = revoked.selfcert(community)
    
            key = SigningKey(self.salt, password)
            revocation.sign(selfcert, [key])
    
            logging.debug("Self-Revocation Document : \n{0}".format(revocation.raw(selfcert)))
            logging.debug("Signature : \n{0}".format(revocation.signatures[0]))
    
            data = {
                'pubkey': revoked.pubkey,
                'self_': selfcert.signed_raw(),
                'sig': revocation.signatures[0]
            }
            logging.debug("Posted data : {0}".format(data))
            replies = community.broadcast(qtbma.wot.Revoke, {}, data)
            for r in replies:
                r.finished.connect(lambda reply=r: self.__handle_certification_reply(replies, reply))
    
        def __handle_revoke_reply(self, replies, reply):
            """
            Handle the reply, if the request was accepted, disconnect
            all other replies
    
            :param QNetworkReply reply: The reply of this handler
            :param list of QNetworkReply replies: All request replies
            :return:
            """
            strdata = bytes(reply.readAll()).decode('utf-8')
            logging.debug("Received reply : {0} : {1}".format(reply.error(), strdata))
            if reply.error() == QNetworkReply.NoError:
                for r in replies:
                    try:
                        r.disconnect()
                    except TypeError as e:
                        if "disconnect()" in str(e):
                            logging.debug("Could not disconnect a reply")
                        else:
                            raise
                self.revoke_broadcasted.emit()
            else:
                for r in replies:
                    if not r.isFinished() or r.error() == QNetworkReply.NoError:
                        return
                self.broadcast_error.emit(r.error(), strdata)
    
        def start_coroutines(self):
            for c in self.communities:
                c.start_coroutines()
    
        def stop_coroutines(self):
            for c in self.communities:
                c.stop_coroutines()
    
            for w in self.wallets:
                w.stop_coroutines()
    
        def jsonify(self):
            """
            Get the account in a json format.
    
            :return: A dict view of the account to be saved as json
            """
            data_communities = []
            for c in self.communities:
                data_communities.append(c.jsonify())
    
            data_wallets = []
            for w in self.wallets:
                data_wallets.append(w.jsonify())
    
            data = {'name': self.name,
                    'salt': self.salt,
                    'pubkey': self.pubkey,
                    'communities': data_communities,
                    'wallets': data_wallets,
                    'contacts': self.contacts}
            return data