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)
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
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
class Donation(Model): donor = ForeignKeyField(Donor, related_name='donated_by', null=False) value = DecimalField(max_digits=10, decimal_places=2) class Meta: database = db
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