Esempio n. 1
0
class Proposal(GovernanceClass, BaseModel):
    governance_object = ForeignKeyField(GovernanceObject,
                                        related_name='proposals',
                                        on_delete='CASCADE',
                                        on_update='CASCADE')
    name = CharField(default='', max_length=40)
    url = CharField(default='')
    start_epoch = IntegerField()
    end_epoch = IntegerField()
    payment_address = CharField(max_length=36)
    payment_amount = DecimalField(max_digits=16, decimal_places=8)
    object_hash = CharField(max_length=64)

    govobj_type = DASHD_GOVOBJ_TYPES['proposal']

    class Meta:
        db_table = 'proposals'

    def is_valid(self):
        import absolutelib

        printdbg("In Proposal#is_valid, for Proposal: %s" % self.__dict__)

        try:
            # proposal name exists and is not null/whitespace
            if (len(self.name.strip()) == 0):
                printdbg("\tInvalid Proposal name [%s], returning False" %
                         self.name)
                return False

            # proposal name is normalized (something like "[a-zA-Z0-9-_]+")
            if not re.match(r'^[-_a-zA-Z0-9]+$', self.name):
                printdbg(
                    "\tInvalid Proposal name [%s] (does not match regex), returning False"
                    % self.name)
                return False

            # end date < start date
            if (self.end_epoch <= self.start_epoch):
                printdbg(
                    "\tProposal end_epoch [%s] <= start_epoch [%s] , returning False"
                    % (self.end_epoch, self.start_epoch))
                return False

            # amount must be numeric
            if misc.is_numeric(self.payment_amount) is False:
                printdbg(
                    "\tProposal amount [%s] is not valid, returning False" %
                    self.payment_amount)
                return False

            # amount can't be negative or 0
            if (float(self.payment_amount) <= 0):
                printdbg(
                    "\tProposal amount [%s] is negative or zero, returning False"
                    % self.payment_amount)
                return False

            # payment address is valid base58 absolute addr, non-multisig
            if not absolutelib.is_valid_absolute_address(
                    self.payment_address, config.network):
                printdbg(
                    "\tPayment address [%s] not a valid Absolute address for network [%s], returning False"
                    % (self.payment_address, config.network))
                return False

            # URL
            if (len(self.url.strip()) < 4):
                printdbg("\tProposal URL [%s] too short, returning False" %
                         self.url)
                return False

            try:
                parsed = urlparse.urlparse(self.url)
            except Exception as e:
                printdbg(
                    "\tUnable to parse Proposal URL, marking invalid: %s" % e)
                return False

        except Exception as e:
            printdbg(
                "Unable to validate in Proposal#is_valid, marking invalid: %s"
                % e.message)
            return False

        printdbg("Leaving Proposal#is_valid, Valid = True")
        return True

    def is_expired(self, superblockcycle=None):
        from constants import SUPERBLOCK_FUDGE_WINDOW
        import absolutelib

        if not superblockcycle:
            raise Exception("Required field superblockcycle missing.")

        printdbg("In Proposal#is_expired, for Proposal: %s" % self.__dict__)
        now = misc.now()
        printdbg("\tnow = %s" % now)

        # half the SB cycle, converted to seconds
        # add the fudge_window in seconds, defined elsewhere in Sentinel
        expiration_window_seconds = int(
            (absolutelib.blocks_to_seconds(superblockcycle) / 2) +
            SUPERBLOCK_FUDGE_WINDOW)
        printdbg("\texpiration_window_seconds = %s" %
                 expiration_window_seconds)

        # "fully expires" adds the expiration window to end time to ensure a
        # valid proposal isn't excluded from SB by cutting it too close
        fully_expires_at = self.end_epoch + expiration_window_seconds
        printdbg("\tfully_expires_at = %s" % fully_expires_at)

        if (fully_expires_at < now):
            printdbg("\tProposal end_epoch [%s] < now [%s] , returning True" %
                     (self.end_epoch, now))
            return True

        printdbg("Leaving Proposal#is_expired, Expired = False")
        return False

    def is_deletable(self):
        # end_date < (current_date - 30 days)
        thirty_days = (86400 * 30)
        if (self.end_epoch < (misc.now() - thirty_days)):
            return True

        # TBD (item moved to external storage/AbsoluteDrive, etc.)
        return False

    @classmethod
    def approved_and_ranked(self, proposal_quorum, next_superblock_max_budget):
        # return all approved proposals, in order of descending vote count
        #
        # we need a secondary 'order by' in case of a tie on vote count, since
        # superblocks must be deterministic
        query = (
            self.select(
                self,
                GovernanceObject)  # Note that we are selecting both models.
            .join(GovernanceObject).where(
                GovernanceObject.absolute_yes_count > proposal_quorum).
            order_by(GovernanceObject.absolute_yes_count.desc(),
                     GovernanceObject.object_hash.desc()))

        ranked = []
        for proposal in query:
            proposal.max_budget = next_superblock_max_budget
            if proposal.is_valid():
                ranked.append(proposal)

        return ranked

    @classmethod
    def expired(self, superblockcycle=None):
        if not superblockcycle:
            raise Exception("Required field superblockcycle missing.")

        expired = []

        for proposal in self.select():
            if proposal.is_expired(superblockcycle):
                expired.append(proposal)

        return expired

    @property
    def rank(self):
        rank = 0
        if self.governance_object:
            rank = self.governance_object.absolute_yes_count
            return rank

    def get_prepare_command(self):
        import absolutelib
        obj_data = absolutelib.SHIM_serialise_for_absoluted(self.serialise())

        # new superblocks won't have parent_hash, revision, etc...
        cmd = ['gobject', 'prepare', '0', '1', str(int(time.time())), obj_data]

        return cmd

    def prepare(self, absoluted):
        try:
            object_hash = absoluted.rpc_command(*self.get_prepare_command())
            printdbg("Submitted: [%s]" % object_hash)
            self.go.object_fee_tx = object_hash
            self.go.save()

            manual_submit = ' '.join(self.get_submit_command())
            print(manual_submit)

        except JSONRPCException as e:
            print("Unable to prepare: %s" % e.message)
Esempio n. 2
0
    class Journal(Model):
        timestamp = DateTimeField(default=datetime.now)
        level = SmallIntegerField(index=True)
        text = CharField(max_length=255, index=True)

        col_float1 = FloatField(default=2.2)
        col_smallint1 = SmallIntegerField(default=2)
        col_int1 = IntegerField(default=2000000)
        col_bigint1 = BigIntegerField(default=99999999)
        col_char1 = CharField(max_length=255, default="value1")
        col_text1 = TextField(
            default="Moo,Foo,Baa,Waa,Moo,Foo,Baa,Waa,Moo,Foo,Baa,Waa")
        col_decimal1 = DecimalField(12, 8, default=Decimal("2.2"))
        col_json1 = JSONField(default={
            "a": 1,
            "b": "b",
            "c": [2],
            "d": {
                "e": 3
            },
            "f": True
        })

        col_float2 = FloatField(null=True)
        col_smallint2 = SmallIntegerField(null=True)
        col_int2 = IntegerField(null=True)
        col_bigint2 = BigIntegerField(null=True)
        col_char2 = CharField(max_length=255, null=True)
        col_text2 = TextField(null=True)
        col_decimal2 = DecimalField(12, 8, null=True)
        col_json2 = JSONField(null=True)

        col_float3 = FloatField(default=2.2)
        col_smallint3 = SmallIntegerField(default=2)
        col_int3 = IntegerField(default=2000000)
        col_bigint3 = BigIntegerField(default=99999999)
        col_char3 = CharField(max_length=255, default="value1")
        col_text3 = TextField(
            default="Moo,Foo,Baa,Waa,Moo,Foo,Baa,Waa,Moo,Foo,Baa,Waa")
        col_decimal3 = DecimalField(12, 8, default=Decimal("2.2"))
        col_json3 = JSONField(default={
            "a": 1,
            "b": "b",
            "c": [2],
            "d": {
                "e": 3
            },
            "f": True
        })

        col_float4 = FloatField(null=True)
        col_smallint4 = SmallIntegerField(null=True)
        col_int4 = IntegerField(null=True)
        col_bigint4 = BigIntegerField(null=True)
        col_char4 = CharField(max_length=255, null=True)
        col_text4 = TextField(null=True)
        col_decimal4 = DecimalField(12, 8, null=True)
        col_json4 = JSONField(null=True)

        class Meta:
            database = db
Esempio n. 3
0
class Proposal(GovernanceClass, BaseModel):
    governance_object = ForeignKeyField(GovernanceObject, related_name='proposals', on_delete='CASCADE', on_update='CASCADE')
    name = CharField(default='', max_length=40)
    url = CharField(default='')
    start_epoch = IntegerField()
    end_epoch = IntegerField()
    payment_address = CharField(max_length=36)
    payment_amount = DecimalField(max_digits=16, decimal_places=8)
    object_hash = CharField(max_length=64)

    # src/governance-validators.cpp
    MAX_DATA_SIZE = 512

    govobj_type = AZARTD_GOVOBJ_TYPES['proposal']

    class Meta:
        db_table = 'proposals'

    def is_valid(self):
        import azartlib

        printdbg("In Proposal#is_valid, for Proposal: %s" % self.__dict__)

        try:
            # proposal name exists and is not null/whitespace
            if (len(self.name.strip()) == 0):
                printdbg("\tInvalid Proposal name [%s], returning False" % self.name)
                return False

            # proposal name is normalized (something like "[a-zA-Z0-9-_]+")
            if not re.match(r'^[-_a-zA-Z0-9]+$', self.name):
                printdbg("\tInvalid Proposal name [%s] (does not match regex), returning False" % self.name)
                return False

            # end date < start date
            if (self.end_epoch <= self.start_epoch):
                printdbg("\tProposal end_epoch [%s] <= start_epoch [%s] , returning False" % (self.end_epoch, self.start_epoch))
                return False

            # amount must be numeric
            if misc.is_numeric(self.payment_amount) is False:
                printdbg("\tProposal amount [%s] is not valid, returning False" % self.payment_amount)
                return False

            # amount can't be negative or 0
            if (float(self.payment_amount) <= 0):
                printdbg("\tProposal amount [%s] is negative or zero, returning False" % self.payment_amount)
                return False

            # payment address is valid base58 azart addr, non-multisig
            if not azartlib.is_valid_azart_address(self.payment_address, config.network):
                printdbg("\tPayment address [%s] not a valid Azart address for network [%s], returning False" % (self.payment_address, config.network))
                return False

            # URL
            if (len(self.url.strip()) < 4):
                printdbg("\tProposal URL [%s] too short, returning False" % self.url)
                return False

            # proposal URL has any whitespace
            if (re.search(r'\s', self.url)):
                printdbg("\tProposal URL [%s] has whitespace, returning False" % self.name)
                return False

            # Azart Core restricts proposals to 512 bytes max
            if len(self.serialise()) > (self.MAX_DATA_SIZE * 2):
                printdbg("\tProposal [%s] is too big, returning False" % self.name)
                return False

            try:
                parsed = urlparse.urlparse(self.url)
            except Exception as e:
                printdbg("\tUnable to parse Proposal URL, marking invalid: %s" % e)
                return False

        except Exception as e:
            printdbg("Unable to validate in Proposal#is_valid, marking invalid: %s" % e.message)
            return False

        printdbg("Leaving Proposal#is_valid, Valid = True")
        return True

    def is_expired(self, superblockcycle=None):
        from constants import SUPERBLOCK_FUDGE_WINDOW
        import azartlib

        if not superblockcycle:
            raise Exception("Required field superblockcycle missing.")

        printdbg("In Proposal#is_expired, for Proposal: %s" % self.__dict__)
        now = misc.now()
        printdbg("\tnow = %s" % now)

        # half the SB cycle, converted to seconds
        # add the fudge_window in seconds, defined elsewhere in Sentinel
        expiration_window_seconds = int(
            (azartlib.blocks_to_seconds(superblockcycle) / 2) +
            SUPERBLOCK_FUDGE_WINDOW
        )
        printdbg("\texpiration_window_seconds = %s" % expiration_window_seconds)

        # "fully expires" adds the expiration window to end time to ensure a
        # valid proposal isn't excluded from SB by cutting it too close
        fully_expires_at = self.end_epoch + expiration_window_seconds
        printdbg("\tfully_expires_at = %s" % fully_expires_at)

        if (fully_expires_at < now):
            printdbg("\tProposal end_epoch [%s] < now [%s] , returning True" % (self.end_epoch, now))
            return True

        printdbg("Leaving Proposal#is_expired, Expired = False")
        return False

    @classmethod
    def approved_and_ranked(self, proposal_quorum, next_superblock_max_budget):
        # return all approved proposals, in order of descending vote count
        #
        # we need a secondary 'order by' in case of a tie on vote count, since
        # superblocks must be deterministic
        query = (self
                 .select(self, GovernanceObject)  # Note that we are selecting both models.
                 .join(GovernanceObject)
                 .where(GovernanceObject.absolute_yes_count > proposal_quorum)
                 .order_by(GovernanceObject.absolute_yes_count.desc(), GovernanceObject.object_hash.desc())
                 )

        ranked = []
        for proposal in query:
            proposal.max_budget = next_superblock_max_budget
            if proposal.is_valid():
                ranked.append(proposal)

        return ranked

    @classmethod
    def expired(self, superblockcycle=None):
        if not superblockcycle:
            raise Exception("Required field superblockcycle missing.")

        expired = []

        for proposal in self.select():
            if proposal.is_expired(superblockcycle):
                expired.append(proposal)

        return expired

    @property
    def rank(self):
        rank = 0
        if self.governance_object:
            rank = self.governance_object.absolute_yes_count
            return rank
Esempio n. 4
0
class Donation(Model):
    donor = ForeignKeyField(Donor, related_name='donated_by', null=False)
    value = DecimalField(max_digits=10, decimal_places=2)

    class Meta:
        database = db
Esempio n. 5
0
class Proposal(GovernanceClass, BaseModel):
    governance_object = ForeignKeyField(GovernanceObject,
                                        related_name='proposals',
                                        on_delete='CASCADE',
                                        on_update='CASCADE')
    name = CharField(default='', max_length=20)
    url = CharField(default='')
    start_epoch = IntegerField()
    end_epoch = IntegerField()
    payment_address = CharField(max_length=36)
    payment_amount = DecimalField(max_digits=16, decimal_places=8)
    object_hash = CharField(max_length=64)

    govobj_type = DASHD_GOVOBJ_TYPES['proposal']

    class Meta:
        db_table = 'proposals'

    def is_valid(self, dashd):
        import dashlib
        now = misc.now()

        # proposal name exists and is not null/whitespace
        if (len(self.name.strip()) == 0):
            return False

        # proposal name is normalized (something like "[a-zA-Z0-9-_]+")
        if not re.match(r'^[-_a-zA-Z0-9]+$', self.name):
            return False

        # end date < start date
        if (self.end_epoch <= self.start_epoch):
            return False

        # end date < current date
        if (self.end_epoch <= now):
            return False

        # budget check
        max_budget = dashd.next_superblock_max_budget()
        if (max_budget and (self.payment_amount > max_budget)):
            return False

        # amount can't be negative or 0
        if (self.payment_amount <= 0):
            return False

        # payment address is valid base58 dash addr, non-multisig
        if not dashlib.is_valid_dash_address(self.payment_address,
                                             config.network):
            return False

        # URL
        if (len(self.url.strip()) < 4):
            return False

        return True

    def is_deletable(self):
        # end_date < (current_date - 30 days)
        thirty_days = (86400 * 30)
        if (self.end_epoch < (misc.now() - thirty_days)):
            return True

        # TBD (item moved to external storage/DashDrive, etc.)
        return False

    @classmethod
    def approved_and_ranked(self, dashd):
        proposal_quorum = dashd.governance_quorum()
        next_superblock_max_budget = dashd.next_superblock_max_budget()

        # return all approved proposals, in order of descending vote count
        #
        # we need a secondary 'order by' in case of a tie on vote count, since
        # superblocks must be deterministic
        query = (
            self.select(
                self,
                GovernanceObject)  # Note that we are selecting both models.
            .join(GovernanceObject).where(
                GovernanceObject.absolute_yes_count > proposal_quorum).
            order_by(GovernanceObject.absolute_yes_count.desc(),
                     GovernanceObject.object_hash))

        ranked = []
        for proposal in query:
            proposal.max_budget = next_superblock_max_budget
            if proposal.is_valid(dashd):
                ranked.append(proposal)

        return ranked

    @property
    def rank(self):
        rank = 0
        if self.governance_object:
            rank = self.governance_object.absolute_yes_count
            return rank