Example #1
0
class Wallet(BaseModelMixin, TimestampMixin, db.Model):
    __tablename__ = "wallet"
    # id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    # is_delete = db.Column(db.Boolean, nullable=False, default=False)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), primary_key=True)
    zone_id = db.Column(db.Integer,
                        db.ForeignKey('proposal_zone.id'),
                        primary_key=True)  # 对应 proposal zone id
    currency_id = db.Column(db.Integer,
                            db.ForeignKey('currency.id'),
                            primary_key=True)
    address = db.Column(db.String(255), nullable=True)  # wallet address
    # Nonce code for security checks
    nonce = db.Column(db.String(100), nullable=True)

    @staticmethod
    def recover_address(data: str, signature: str) -> str:
        message = encode_defunct(text=data)
        return w3.eth.account.recover_message(message, signature=signature)

    @staticmethod
    def checkNonce(wallet, signature):
        """
        Check the nonce validity
        :param wallet:
        :return: boolean
        """

        if wallet.nonce == None:
            return False

        result = Wallet.recover_address(wallet.nonce, signature)
        print(wallet.nonce, wallet.address, result)
        return result == wallet.address
Example #2
0
class Currency(db.Model):
    """
    Currency
    """
    __tablename__ = 'currency'

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(100), unique=True)
    unit = db.Column(db.String(100), unique=True)
    is_delete = db.Column(db.Boolean, nullable=False, default=False)
    proposals_used = db.relationship('Proposal',
                                     foreign_keys='Proposal.currency_id',
                                     backref='currency_unit',
                                     lazy='dynamic')
Example #3
0
class ProposalClaim(BaseModelMixin, TimestampMixin, db.Model):
    __tablename__ = "proposal_claim"

    claim_id = db.Column(db.String(200), nullable=False, default=uuid4)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), primary_key=True)
    proposal_id = db.Column(db.Integer,
                            db.ForeignKey('proposal.id'),
                            primary_key=True)

    # 申领状态
    # 100 申领中
    # 200 申领通过
    # 300 申领不通过
    # 400 撤销申领
    # 500 提交结果中
    # 600 结果通过
    # 700 结果不通过
    status = db.Column(db.Integer,
                       default=ProposalClaimStatus['claiming'].value)

    # 申领理由
    reason = db.Column(db.Text)
    plan = db.Column(db.Text)

    budget_amount = db.Column(db.DECIMAL, nullable=True)
    budget_currency_id = db.Column(db.Integer,
                                   db.ForeignKey('currency.id'),
                                   nullable=True)

    # 收款地址
    payment_address = db.Column(db.String(255), nullable=True)

    # 完成结果
    result = db.Column(db.Text, nullable=True)

    # team = db.relationship('ProposalClaimTeam',
    #                        foreign_keys='ProposalClaimTeam.team_id',
    #                        backref='team',
    #                        lazy='dynamic')

    @property
    def status_key(self):
        if self.status:
            return ProposalClaimStatus(self.status).name

    @property
    def team(self):
        return ProposalClaimTeam.query.filter_by(team_id=self.id, is_delete=0)
Example #4
0
class BlacklistToken(db.Model):
    """
    Token Model for storing JWT tokens
    """
    __tablename__ = 'blacklist_tokens'

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    token = db.Column(db.String(500), unique=True, nullable=False)
    blacklisted_on = db.Column(db.DateTime, nullable=False)

    def __init__(self, token):
        self.token = token
        self.blacklisted_on = datetime.datetime.now()

    def __repr__(self):
        return '<id: token: {}'.format(self.token)

    @staticmethod
    def check_blacklist(auth_token):
        # check whether auth token has been blacklisted
        res = BlacklistToken.query.filter_by(token=str(auth_token)).first()
        if res:
            return True
        else:
            return False
Example #5
0
class UserWallet(TimestampMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    is_delete = db.Column(db.Boolean, nullable=False, default=False)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
    token_zone_id = db.Column(db.Integer,
                              db.ForeignKey('proposal_zone.id'),
                              nullable=False)  # 对应 proposal zone id
    wallet_addr = db.Column(db.String(255), nullable=False)  # wallet address
Example #6
0
class Category(BaseModelMixin, TimestampMixin, db.Model):
    """
    Proposal Category
    """

    name = db.Column(db.String(200), unique=True)  # chinese name
    name_en = db.Column(db.String(200), unique=True,
                        nullable=True)  # english name

    order = db.Column(db.Integer, default=0)  # use for order category list
    proposals = db.relationship('Proposal', backref='category')

    @property
    def proposals_count(self):
        # return len(self.proposals)
        return Proposal.query.filter_by(category_id=self.id,
                                        is_delete=0).count()
Example #7
0
class ProposalZone(BaseModelMixin, TimestampMixin, db.Model):
    """
    Proposal Zone
    """
    __tablename__ = 'proposal_zone'

    name = db.Column(db.String(100), unique=True)
    title = db.Column(db.String(100))
    token = db.Column(db.String(100))  # token name
    summary = db.Column(db.String(200))
    detail = db.Column(db.Text)
    vote_rule = db.Column(db.Text)
    cover = db.Column(db.String(100))  # proposal cover image filename

    # 专区主题
    # proposal theme style css: {'background':'#ccc', 'color':'#fff'}
    theme_style = db.Column(db.Text)

    # 专区主题色
    theme_color = db.Column(db.Text)

    # 投票最小持续时间
    vote_duration_hours_min = db.Column(db.Integer,
                                        default=1)  # vote min duration: 1h

    # 投票最大持续时间
    vote_duration_hours_max = db.Column(
        db.Integer, default=120)  # vote min duration: 120h=5day

    vote_addr_weight_json = db.Column(db.Text)

    # 合约地址
    multiSigAddress = db.Column(db.String(100))
    voteAddress = db.Column(db.String(100))

    # 该专区下的提案
    proposals = db.relationship('Proposal',
                                foreign_keys='Proposal.zone_id',
                                backref='zone',
                                lazy='dynamic')

    # 该专区支持的代币
    currencies = db.relationship('Currency',
                                 secondary='zone_currency',
                                 backref="zones")

    # 绑定了该专区的用户钱包
    wallets = db.relationship('Wallet',
                              foreign_keys='Wallet.zone_id',
                              backref="zone",
                              lazy='dynamic')

    @property
    def total_proposals(self):
        return Proposal.query.filter_by(zone_id=self.id).count()

    def __repr__(self):
        return "<Proposal Zone '{}'>".format(self.name)
Example #8
0
class Proposal(BaseModelMixin, TimestampMixin, db.Model):
    """
    Proposal
    """

    __tablename__ = 'proposal'

    zone_id = db.Column(db.Integer, db.ForeignKey('proposal_zone.id'))
    zone_proposal_id = db.Column(
        db.Integer)  # 该 proposal 在某专区内的 ID, 以在该专区内的 ID 为自增
    title = db.Column(db.String(200))
    tag = db.Column(db.String(200))
    amount = db.Column(db.DECIMAL)
    currency_id = db.Column(db.Integer,
                            db.ForeignKey('currency.id'),
                            nullable=True)
    summary = db.Column(db.String(200))
    detail = db.Column(db.Text)
    status = db.Column(db.Integer)

    # 注意,backref 不能跟 talename 重名
    comments = db.relationship('Comment',
                               foreign_keys='Comment.proposal_id',
                               backref='link_proposal',
                               lazy='dynamic')

    def __repr__(self):
        return "<Proposal '{}'>".format(self.title)

    def zone(self):
        return ProposalZone.query.filter(zone=self).first()

    def creator(self):
        return User.query.filter(creator=self).first()

    def currency_unit(self):
        return Currency.query.filter(currency_unit=self).first()
Example #9
0
class ProposalZone(BaseModelMixin, TimestampMixin, db.Model):
    """
    Proposal Zone
    """
    __tablename__ = 'proposal_zone'

    name = db.Column(db.String(100), unique=True)
    title = db.Column(db.String(100))
    token = db.Column(db.String(100), unique=True)  # token name
    summary = db.Column(db.String(200))
    detail = db.Column(db.Text)
    vote_rule = db.Column(db.Text)
    cover = db.Column(db.String(100))  # proposal cover image filename
    theme_style = db.Column(
        db.Text
    )  # proposal theme style css: {'background':'#ccc', 'color':'#fff'}
    vote_addr_weight_json = db.Column(db.Text)
    proposals = db.relationship('Proposal',
                                foreign_keys='Proposal.zone_id',
                                backref='zone',
                                lazy='dynamic')

    def __repr__(self):
        return "<Proposal Zone '{}'>".format(self.name)
Example #10
0
class User(db.Model):
    """ User Model for storing user related details """
    __tablename__ = "user"

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    email = db.Column(db.String(255), unique=True, nullable=False)
    avatar = db.Column(db.String(255))
    registered_on = db.Column(db.DateTime, nullable=False)
    admin = db.Column(db.Boolean, nullable=False, default=False)
    public_id = db.Column(db.String(100), unique=True)
    username = db.Column(db.String(50), unique=True)
    nickname = db.Column(db.String(50), unique=True)
    password_hash = db.Column(db.String(100))
    sign = db.Column(db.String(255))

    confirmed = db.Column(db.Boolean, default=False)

    # 注意,backref 不能跟 talename 重名
    proposals_created = db.relationship('Proposal',
                                        foreign_keys='Proposal.creator_id',
                                        backref='creator',
                                        lazy='dynamic')
    # 该用户创造的 comment
    comment_created = db.relationship('Comment',
                                      foreign_keys='Comment.creator_id',
                                      backref='creator',
                                      lazy='dynamic')

    # 该用户的 wallet
    wallets = db.relationship('UserWallet',
                              foreign_keys='UserWallet.user_id',
                              backref='user',
                              lazy='dynamic')

    @property
    def password(self):
        raise AttributeError('password: write-only field')

    @password.setter
    def password(self, password):
        self.password_hash = flask_bcrypt.generate_password_hash(
            password).decode('utf-8')

    def set_password(self, password):
        self.password_hash = flask_bcrypt.generate_password_hash(
            password).decode('utf-8')

    def check_password(self, password):
        return flask_bcrypt.check_password_hash(self.password_hash, password)

    @staticmethod
    def encode_auth_token(user_id):
        """
        Generates the Auth Token
        :return: string
        """
        try:
            payload = {
                'exp':
                datetime.datetime.utcnow() +
                datetime.timedelta(days=1, seconds=5),
                'iat':
                datetime.datetime.utcnow(),
                'sub':
                user_id
            }
            return jwt.encode(payload, key, algorithm='HS256')
        except Exception as e:
            return e

    @staticmethod
    def decode_auth_token(auth_token):
        """
        Decodes the auth token
        :param auth_token:
        :return: integer|string
        """
        try:
            payload = jwt.decode(auth_token, key)
            is_blacklisted_token = BlacklistToken.check_blacklist(auth_token)
            if is_blacklisted_token:
                return 'Token blacklisted. Please log in again.'
            else:
                return payload['sub']
        except jwt.ExpiredSignatureError:
            return 'Signature expired. Please log in again.'
        except jwt.InvalidTokenError:
            return 'Invalid token. Please log in again.'

    def __repr__(self):
        return "<User '{}'>".format(self.username)
Example #11
0
class Proposal(BaseModelMixin, TimestampMixin, db.Model):
    """
    Proposal
    """

    __tablename__ = 'proposal'

    zone_id = db.Column(db.Integer, db.ForeignKey('proposal_zone.id'))
    zone_proposal_id = db.Column(
        db.Integer)  # 该 proposal 在某专区内的 ID, 以在该专区内的 ID 为自增
    title = db.Column(db.String(200))
    category_id = db.Column(db.Integer,
                            db.ForeignKey('category.id'),
                            nullable=True)
    tag = db.Column(db.String(200))
    amount = db.Column(db.DECIMAL)

    # 前端上,只能选择该专区支持的代币
    currency_id = db.Column(db.Integer,
                            db.ForeignKey('currency.id'),
                            nullable=True)
    summary = db.Column(db.String(200))
    detail = db.Column(db.Text)

    # 提案状态

    # 100 待投票: 创建后第一个状态,此时还未上链
    # 200 立项投票中: 上链成功, 在规定投票时间内进行链上投票
    # 300 申领中: 如果投票通过, 提案状态自动改变(投票结束时达成条件)
    # 400 投票未通过: 在规定投票时内, 没有达成通过条件, 状态自动改变
    # 以下是 status=300 后才会有的状态
    # 500 进行中: 由(专区)管理员修改到此状态
    # 600 验收中: 由(专区)管理员修改到此状态,此时需要进行多签投票,决定提案是否验收
    # 700 已完成: 如果投票通过, 提案状态自动改变(投票结束时达成条件)
    # 800 失败: 验收投票不通过, 提案状态自动改变(投票结束时达成条件)
    status = db.Column(db.Integer)

    # 是否已经上链
    # onchain = db.Column(db.Boolean, default=False)

    # 上链后的 hash
    onchain_hash = db.Column(db.String(500))

    # 预计工时
    estimated_hours = db.Column(db.Integer, nullable=True)

    # 投票开始时间, 默认是创建后马上开始
    vote_start_time = db.Column(db.DateTime, default=datetime.utcnow)

    # 投票最大持续时间
    vote_duration_hours = db.Column(
        db.Integer, default=7200)  # vote default duration: 7200min=5day

    # 注意,backref 不能跟 talename 重名
    comments = db.relationship('Comment',
                               foreign_keys='Comment.proposal_id',
                               backref='link_proposal',
                               lazy='dynamic')

    logs = db.relationship('ProposalLog',
                           foreign_keys='ProposalLog.proposal_id',
                           backref='proposal',
                           lazy='dynamic')

    claims = db.relationship('ProposalClaim',
                             foreign_keys='ProposalClaim.proposal_id',
                             backref='proposal',
                             lazy='dynamic')

    def __repr__(self):
        return "<Proposal '{}'>".format(self.title)

    # 加上 @property 注解,表示这是一个属性字段
    # @property
    # def comments_count(self):
    #     return Comment.query.with_parent(self).filter_by(is_delete=0).count()

    def set_onchain_hash(self):
        self.onchain_hash = md5(
            str([
                self.id, self.title, self.summary, self.status,
                self.creator_id, self.created
            ]))

    @property
    def status_key(self):
        if self.status:
            return ProposalStatus(self.status).name

    @hybrid_property
    def comments_count(self):
        return self.comments.filter_by(is_delete=0).count()

    @comments_count.expression
    def comments_count(cls):
        return (select([func.count(Comment.id)]).where(
            and_(Comment.proposal_id == cls.id, Comment.is_delete == 0)))