コード例 #1
0
    def _purge_expired(self):
        current_time = time.get_time()
        expired_transactions = list(
            PoolTransaction.select().where(PoolTransaction.expires_at <= current_time)
        )

        ids = []
        for trans in expired_transactions:
            transaction = from_object(trans)
            sender_wallet = self.find_by_public_key(transaction.sender_public_key)
            transaction.revert_for_sender_wallet(sender_wallet)
            ids.append(transaction.id)

        delete_query = PoolTransaction.delete().where(PoolTransaction.id.in_(ids))
        delete_query.execute()
コード例 #2
0
    def has_sender_exceeded_max_transactions(self, sender_public_key):
        """Checks whether sender or transaction has exceeded max transactions in queue

        :param str sender_public_key: Sender's public key
        """
        self._purge_expired()
        if sender_public_key in config.pool["allowed_senders"]:
            logger.info(
                (
                    "Sender with public key %s is an allowed sender, thus skipping "
                    "throttling"
                ),
                sender_public_key,
            )
            return False

        count = (
            PoolTransaction.select()
            .where(PoolTransaction.sender_public_key == sender_public_key)
            .count()
        )
        return count > config.pool["max_transactions_per_sender"]
コード例 #3
0
    def build_wallets(self):
        self.wallets.clear_wallets()
        self._purge_expired()
        last_block = self.database.get_last_block()
        for trans in PoolTransaction.select():
            transaction = from_object(trans)
            sender_wallet = self.wallets.find_by_public_key(
                transaction.sender_public_key
            )
            if transaction.can_be_applied_to_wallet(
                sender_wallet, self.walelts, self, last_block.height
            ):
                transaction.apply_to_sender_wallet(sender_wallet)
                self.wallets.save_wallet(sender_wallet)
            else:
                logger.warning(
                    "Transaction %s can't be applied to wallet %s",
                    transaction.id,
                    sender_wallet.address,
                )
                self._purge_sender(transaction.sender_public_key)

        logger.info("Transaction pool wallets have been successfully built")
コード例 #4
0
ファイル: delegate_registration.py プロジェクト: tsifrer/ark
    def validate_for_transaction_pool(self, pool, transactions):
        if pool.sender_has_transactions_of_type(self):
            return (
                "Sender {} already has a transaction of type {} in the "
                "pool".format(self.sender_public_key, self.type)
            )

        # Reject all delegate registration transactions with the same delegate
        # name if there are more than one with the same name
        num_same_delegate_registration_in_payload = [
            trans
            for trans in transactions
            if trans.asset["delegate"]["username"] == self.asset["delegate"]["username"]
        ]
        if len(num_same_delegate_registration_in_payload) > 1:
            return (
                "Multiple delegate registrations for {} in transaction "
                "payload".format(self.asset["delegate"]["username"])
            )

        # Reject a transaction if delegate registration transaction for the same
        # username is already in the pool
        exists_in_pool = (
            PoolTransaction.select()
            .where(
                PoolTransaction.type == TRANSACTION_TYPE_DELEGATE_REGISTRATION,
                PoolTransaction.asset["delegate"]["username"]
                == self.asset["delegate"]["username"],
            )
            .exists()
        )
        if exists_in_pool:
            return "Delegate registration for {} already in the pool".format(
                self.asset["delegate"]["username"]
            )

        return None
コード例 #5
0
 def transaction_exists(self, transaction_id):
     query = PoolTransaction.select().where(PoolTransaction.id == transaction_id)
     return query.exists()
コード例 #6
0
    def process_transactions(self, transactions_data):
        self._purge_expired()

        errors = {}
        excess = []
        accepted = []
        broadcasted = []
        transactions = []

        last_block = self.database.get_last_block()

        # TODO: Transaction sequence is currently growing until transaction pool is
        # completely empty
        last_transaction_sequence = (
            PoolTransaction.select(PoolTransaction.sequence)
            .order_by(PoolTransaction.sequence.desc())
            .first()
        )
        sequence = 0
        if last_transaction_sequence:
            sequence = last_transaction_sequence.sequence

        for transaction_data in transactions_data:

            # NOTE: This check should really check if transaction is not bigger than
            # maximum BYTES and not characters, but the implementation in core is wrong
            # and we need to be consistent with their implementation. Also this should
            # probably be checked after the payload is converted to CryptoTransaction
            json_transaction = len(json.dumps(transaction_data))
            if json_transaction > config.pool["max_transaction_characters"]:
                errors[
                    transaction_data["id"]
                ] = "Transaction {} is larger than {} characters".format(
                    transaction_data["id"], config.pool["max_transaction_characters"]
                )
                continue
            transaction = from_dict(transaction_data)
            sequence += 1
            transaction.sequence = sequence
            transactions.append(transaction)

        for transaction in transactions:
            if self.has_sender_exceeded_max_transactions(transaction.sender_public_key):
                excess.append(transaction.id)
                continue

            validation_error = self._validate_transaction(
                transaction, last_block.height, transactions
            )
            if validation_error:
                errors[transaction.id] = validation_error
                continue

            if not self.wallets.can_apply_to_sender(transaction, last_block.height):
                errors[
                    transaction.id
                ] = "Transaction {} can't be applied to senders wallet".format(
                    transaction.id
                )
                continue

            valid_for_pool = valid_fee_for_pool(transaction, last_block.height)
            valid_for_broadcast = valid_fee_for_broadcast(
                transaction, last_block.height
            )

            if not valid_for_broadcast and not valid_for_pool:
                errors[
                    transaction.id
                ] = "Fee is too low to broadcast and accept the transaction {}".format(
                    transaction.id
                )
                continue

            if valid_for_pool:
                count = PoolTransaction.select().count()
                if count > config.pool["max_transactions_in_pool"]:
                    lowest_pool = (
                        PoolTransaction.select(PoolTransaction.fee)
                        .order_by(PoolTransaction.fee.asc())
                        .first()
                    )
                    lowest = from_object(lowest_pool)
                    if lowest.fee < transaction.fee:
                        lowest_sender = self.wallets.find_by_public_key(
                            lowest.sender_public_key
                        )
                        lowest.revert_for_sender_wallet(lowest_sender)
                        self.wallets.save_wallet(lowest_sender)
                        lowest_pool.delete_instance()
                    else:
                        errors[transaction.id] = (
                            "Pool is full (has {} transactions) and this transaction's "
                            "fee {} is not higher than the lowest fee already in the "
                            "pool {}".format(count, transaction.fee, lowest_pool.fee)
                        )
                        continue

                # Add transaction to the pool
                sender_wallet = self.wallets.find_by_public_key(
                    transaction.sender_public_key
                )
                transaction.apply_to_sender_wallet(sender_wallet)
                self.wallets.save_wallet(sender_wallet)
                pool_transaction = PoolTransaction.from_crypto(transaction)
                pool_transaction.save()
                accepted.append(transaction.id)

            if valid_for_broadcast:
                broadcasted.append(transaction.id)
                # TODO:
                # if (result.broadcast.length > 0) {
                #     app.resolvePlugin<P2P.IMonitor>("p2p").broadcastTransactions
                # (guard.getBroadcastTransactions());
                # }

        return {
            "accepted": accepted,
            "broadcasted": broadcasted,
            "excess": excess,
            "errors": errors,
            "invalid": list(errors.keys()),
        }
コード例 #7
0
 def sender_has_any_transactions(self, sender_public_key):
     query = PoolTransaction.select().where(
         PoolTransaction.sender_public_key == sender_public_key
     )
     return query.exists()
コード例 #8
0
 def sender_has_transactions_of_type(self, transaction):
     query = PoolTransaction.select().where(
         PoolTransaction.sender_public_key == transaction.sender_public_key,
         PoolTransaction.type == transaction.type,
     )
     return query.exists()