예제 #1
0
    def get_spent(self, txid, output, current_transactions=[]):
        transactions = backend.query.get_spent(self.connection, txid, output)
        transactions = list(transactions) if transactions else []

        for ctxn in current_transactions:
            for ctxn_input in ctxn.inputs:
                if ctxn_input.fulfills.txid == txid and\
                   ctxn_input.fulfills.output == output:
                    transactions.append(ctxn.to_dict())

        transaction = None
        if len(transactions) > 1:
            raise core_exceptions.CriticalDoubleSpend(
                '`{}` was spent more than once. There is a problem'
                ' with the chain'.format(txid))
        elif transactions:
            transaction = transactions[0]

        if transaction and transaction['operation'] == 'CREATE':
            asset = backend.query.get_asset(self.connection, transaction['id'])

            if asset:
                transaction['asset'] = asset
            else:
                transaction['asset'] = {'data': None}

            return Transaction.from_dict(transaction)
        elif transaction and transaction['operation'] == 'TRANSFER':
            return Transaction.from_dict(transaction)
        else:
            return None
예제 #2
0
def test_create_single_input(b, create_tx, alice):
    from bigchaindb.common.transaction import Transaction
    tx = create_tx.to_dict()
    tx['inputs'] += tx['inputs']
    tx = Transaction.from_dict(tx).sign([alice.private_key]).to_dict()
    validate_raises(tx)
    tx['id'] = None
    tx['inputs'] = []
    tx = Transaction.from_dict(tx).sign([alice.private_key]).to_dict()
    validate_raises(tx)
예제 #3
0
def test_transfer_asset_schema(user_sk, signed_transfer_tx):
    from bigchaindb.common.transaction import Transaction
    tx = signed_transfer_tx.to_dict()
    validate(tx)
    tx['id'] = None
    tx['asset']['data'] = {}
    tx = Transaction.from_dict(tx).sign([user_sk]).to_dict()
    validate_raises(tx)
    tx['id'] = None
    del tx['asset']['data']
    tx['asset']['id'] = 'b' * 63
    tx = Transaction.from_dict(tx).sign([user_sk]).to_dict()
    validate_raises(tx)
예제 #4
0
    def post(self):
        """API endpoint to push transactions to the Federation.

        Return:
            A ``dict`` containing the data about the transaction.
        """
        pool = current_app.config['bigchain_pool']
        monitor = current_app.config['monitor']

        # `force` will try to format the body of the POST request even if the `content-type` header is not
        # set to `application/json`
        tx = request.get_json(force=True)

        try:
            tx_obj = Transaction.from_dict(tx)
        except (ValidationError, InvalidSignature):
            return make_error(400, 'Invalid transaction')

        with pool() as bigchain:
            if bigchain.is_valid_transaction(tx_obj):
                rate = bigchaindb.config['statsd']['rate']
                with monitor.timer('write_transaction', rate=rate):
                    bigchain.write_transaction(tx_obj)
            else:
                return make_error(400, 'Invalid transaction')

        return tx
예제 #5
0
    def validate_tx(self, tx):
        """Validate a transaction.

        Also checks if the transaction already exists in the blockchain. If it
        does, or it's invalid, it's deleted from the backlog immediately.

        Args:
            tx (dict): the transaction to validate.

        Returns:
            :class:`~bigchaindb.models.Transaction`: The transaction if valid,
            ``None`` otherwise.
        """
        tx = Transaction.from_dict(tx)
        if self.bigchain.transaction_exists(tx.id):
            # if the transaction already exists, we must check whether
            # it's in a valid or undecided block
            tx, status = self.bigchain.get_transaction(tx.id,
                                                       include_status=True)
            if status == self.bigchain.TX_VALID \
               or status == self.bigchain.TX_UNDECIDED:
                # if the tx is already in a valid or undecided block,
                # then it no longer should be in the backlog, or added
                # to a new block. We can delete and drop it.
                self.bigchain.delete_transaction(tx.id)
                return None

        tx_validated = self.bigchain.is_valid_transaction(tx)
        if tx_validated:
            return tx
        else:
            # if the transaction is not valid, remove it from the
            # backlog
            self.bigchain.delete_transaction(tx.id)
            return None
예제 #6
0
파일: core.py 프로젝트: ssadler/bigchaindb
    def get_tx_by_payload_uuid(self, payload_uuid):
        """Retrieves transactions related to a digital asset.

        When creating a transaction one of the optional arguments is the `payload`. The payload is a generic
        dict that contains information about the digital asset.

        To make it easy to query BigchainDB for that digital asset we create a UUID for the payload and
        store it with the transaction. This makes it easy for developers to keep track of their digital
        assets in bigchain.

        Args:
            payload_uuid (str): the UUID for this particular payload.

        Returns:
            A list of transactions containing that payload. If no transaction exists with that payload it
            returns an empty list `[]`
        """
        cursor = self.connection.run(
            r.table('bigchain', read_mode=self.read_mode).get_all(
                payload_uuid,
                index='payload_uuid').concat_map(lambda block: block['block'][
                    'transactions']).filter(lambda transaction: transaction[
                        'transaction']['data']['uuid'] == payload_uuid))

        transactions = list(cursor)
        return [Transaction.from_dict(tx) for tx in transactions]
예제 #7
0
    def validate_tx(self, tx_dict, block_id, num_tx):
        """Validate a transaction. Transaction must also not be in any VALID
           block.

        Args:
            tx_dict (dict): the transaction to validate
            block_id (str): the id of block containing the transaction
            num_tx (int): the total number of transactions to process

        Returns:
            Three values are returned, the validity of the transaction,
            ``block_id``, ``num_tx``.
        """

        try:
            tx = Transaction.from_dict(tx_dict)
            new = self.bigchain.is_new_transaction(tx.id,
                                                   exclude_block_id=block_id)
            if not new:
                raise exceptions.ValidationError('Tx already exists, %s',
                                                 tx.id)
            tx.validate(self.bigchain)
            valid = True
        except exceptions.ValidationError as e:
            valid = False
            logger.warning('Invalid tx: %s', e)

        return valid, block_id, num_tx
    def post(self):
        """API endpoint to push transactions to the Federation.

        Return:
            A ``dict`` containing the data about the transaction.
        """
        pool = current_app.config['bigchain_pool']
        monitor = current_app.config['monitor']

        # `force` will try to format the body of the POST request even if the `content-type` header is not
        # set to `application/json`
        tx = request.get_json(force=True)

        try:
            tx_obj = Transaction.from_dict(tx)
        except (InvalidHash, InvalidSignature):
            return make_error(400, 'Invalid transaction')

        with pool() as bigchain:
            if bigchain.is_valid_transaction(tx_obj):
                rate = bigchaindb.config['statsd']['rate']
                with monitor.timer('write_transaction', rate=rate):
                    bigchain.write_transaction(tx_obj)
            else:
                return make_error(400, 'Invalid transaction')

        return tx
예제 #9
0
파일: block.py 프로젝트: sbellem/bigchaindb
    def validate_tx(self, tx):
        """Validate a transaction.

        Also checks if the transaction already exists in the blockchain. If it
        does, or it's invalid, it's deleted from the backlog immediately.

        Args:
            tx (dict): the transaction to validate.

        Returns:
            :class:`~bigchaindb.models.Transaction`: The transaction if valid,
            ``None`` otherwise.
        """
        tx = Transaction.from_dict(tx)
        if self.bigchain.transaction_exists(tx.id):
            # if the transaction already exists, we must check whether
            # it's in a valid or undecided block
            tx, status = self.bigchain.get_transaction(tx.id,
                                                       include_status=True)
            if status == self.bigchain.TX_VALID \
               or status == self.bigchain.TX_UNDECIDED:
                # if the tx is already in a valid or undecided block,
                # then it no longer should be in the backlog, or added
                # to a new block. We can delete and drop it.
                self.bigchain.delete_transaction(tx.id)
                return None

        tx_validated = self.bigchain.is_valid_transaction(tx)
        if tx_validated:
            return tx
        else:
            # if the transaction is not valid, remove it from the
            # backlog
            self.bigchain.delete_transaction(tx.id)
            return None
예제 #10
0
파일: vote.py 프로젝트: cgwyx/bigchaindb
    def validate_tx(self, tx_dict, block_id, num_tx):
        """Validate a transaction. Transaction must also not be in any VALID
           block.

        Args:
            tx_dict (dict): the transaction to validate
            block_id (str): the id of block containing the transaction
            num_tx (int): the total number of transactions to process

        Returns:
            Three values are returned, the validity of the transaction,
            ``block_id``, ``num_tx``.
        """

        try:
            tx = Transaction.from_dict(tx_dict)
            new = self.bigchain.is_new_transaction(tx.id, exclude_block_id=block_id)
            if not new:
                raise exceptions.ValidationError('Tx already exists, %s', tx.id)
            tx.validate(self.bigchain)
            valid = True
        except exceptions.ValidationError as e:
            valid = False
            logger.warning('Invalid tx: %s', e)

        return valid, block_id, num_tx
예제 #11
0
    def validate_tx(self, tx):
        """Validate a transaction.

        Also checks if the transaction already exists in the blockchain. If it
        does, or it's invalid, it's deleted from the backlog immediately.

        Args:
            tx (dict): the transaction to validate.

        Returns:
            :class:`~bigchaindb.models.Transaction`: The transaction if valid,
            ``None`` otherwise.
        """
        try:
            tx = Transaction.from_dict(tx)
        except ValidationError:
            return None

        # If transaction is in any VALID or UNDECIDED block we
        # should not include it again
        if not self.bigchain.is_new_transaction(tx.id):
            self.bigchain.delete_transaction(tx.id)
            return None

        # If transaction is not valid it should not be included
        try:
            tx.validate(self.bigchain)
            return tx
        except ValidationError as e:
            logger.warning('Invalid tx: %s', e)
            self.bigchain.delete_transaction(tx.id)
            return None
예제 #12
0
    def post(self):
        """API endpoint to push transactions to the Federation.

        Return:
            A ``dict`` containing the data about the transaction.
        """
        pool = current_app.config['bigchain_pool']

        # `force` will try to format the body of the POST request even if the
        # `content-type` header is not set to `application/json`
        tx = request.get_json(force=True)

        try:
            tx_obj = Transaction.from_dict(tx)
        except SchemaValidationError as e:
            return make_error(400,
                              message='Invalid transaction schema: {}'.format(
                                  e.__cause__.message))
        except ValidationError as e:
            return make_error(
                400,
                'Invalid transaction ({}): {}'.format(type(e).__name__, e))

        with pool() as bigchain:
            try:
                bigchain.validate_transaction(tx_obj)
            except ValidationError as e:
                return make_error(
                    400,
                    'Invalid transaction ({}): {}'.format(type(e).__name__, e))
            else:
                bigchain.write_transaction(tx_obj)

        return tx, 202
예제 #13
0
    def get_tx_by_metadata_id(self, metadata_id):
        """Retrieves transactions related to a metadata.

        When creating a transaction one of the optional arguments is the `metadata`. The metadata is a generic
        dict that contains extra information that can be appended to the transaction.

        To make it easy to query the bigchain for that particular metadata we create a UUID for the metadata and
        store it with the transaction.

        Args:
            metadata_id (str): the id for this particular metadata.

        Returns:
            A list of transactions containing that metadata. If no transaction exists with that metadata it
            returns an empty list `[]`
        """
        cursor = self.connection.run(
            r.table('bigchain', read_mode=self.read_mode).get_all(
                metadata_id,
                index='metadata_id').concat_map(lambda block: block['block'][
                    'transactions']).filter(lambda transaction: transaction[
                        'transaction']['metadata']['id'] == metadata_id))

        transactions = list(cursor)
        return [Transaction.from_dict(tx) for tx in transactions]
def test_create_tx_no_fulfills(b, create_tx):
    from bigchaindb.common.transaction import Transaction
    tx = create_tx.to_dict()
    tx['inputs'][0]['fulfills'] = {'transaction_id': 'a' * 64,
                                   'output_index': 0}
    tx = Transaction.from_dict(tx).sign([b.me_private]).to_dict()
    validate_raises(tx)
예제 #15
0
    def validate_tx(self, tx):
        """Validate a transaction.

        Also checks if the transaction already exists in the blockchain. If it
        does, or it's invalid, it's deleted from the backlog immediately.

        Args:
            tx (dict): the transaction to validate.

        Returns:
            :class:`~bigchaindb.models.Transaction`: The transaction if valid,
            ``None`` otherwise.
        """
        try:
            tx = Transaction.from_dict(tx)
        except (SchemaValidationError, InvalidHash, InvalidSignature,
                AmountError):
            return None

        # If transaction is in any VALID or UNDECIDED block we
        # should not include it again
        if not self.bigchain.is_new_transaction(tx.id):
            self.bigchain.delete_transaction(tx.id)
            return None

        # If transaction is not valid it should not be included
        if not self.bigchain.is_valid_transaction(tx):
            self.bigchain.delete_transaction(tx.id)
            return None

        return tx
예제 #16
0
파일: core.py 프로젝트: nyimbi/bigchaindb
    def get_transaction(self, txid, include_status=False):
        """Retrieve a transaction with `txid` from bigchain.

        Queries the bigchain for a transaction, if it's in a valid or invalid
        block.

        Args:
            txid (str): transaction id of the transaction to query
            include_status (bool): also return the status of the transaction
                                   the return value is then a tuple: (tx, status)

        Returns:
            A :class:`~.models.Transaction` instance if the transaction
            was found, otherwise ``None``.
            If :attr:`include_status` is ``True``, also returns the
            transaction's status if the transaction was found.
        """

        response, tx_status = None, None

        validity = self.get_blocks_status_containing_tx(txid)

        if validity:
            # Disregard invalid blocks, and return if there are no valid or undecided blocks
            validity = {
                _id: status
                for _id, status in validity.items()
                if status != Bigchain.BLOCK_INVALID
            }
            if validity:

                tx_status = self.TX_UNDECIDED
                # If the transaction is in a valid or any undecided block, return it. Does not check
                # if transactions in undecided blocks are consistent, but selects the valid block before
                # undecided ones
                for target_block_id in validity:
                    if validity[target_block_id] == Bigchain.BLOCK_VALID:
                        tx_status = self.TX_VALID
                        break

                # Query the transaction in the target block and return
                response = self.backend.get_transaction_from_block(
                    txid, target_block_id)

        else:
            # Otherwise, check the backlog
            response = self.backend.get_transaction_from_backlog(txid)

            if response:
                tx_status = self.TX_IN_BACKLOG

        if response:
            response = Transaction.from_dict(response)

        if include_status:
            return response, tx_status
        else:
            return response
예제 #17
0
def test_memoize_from_dict(b):
    alice = generate_key_pair()
    asset = {
        'data': {
            'id': 'test_id'
        },
    }

    assert from_dict.cache_info().hits == 0
    assert from_dict.cache_info().misses == 0

    tx = Transaction.create([alice.public_key],
                            [([alice.public_key], 1)],
                            asset=asset,)\
                    .sign([alice.private_key])
    tx_dict = deepcopy(tx.to_dict())

    Transaction.from_dict(tx_dict)

    assert from_dict.cache_info().hits == 0
    assert from_dict.cache_info().misses == 1

    Transaction.from_dict(tx_dict)
    Transaction.from_dict(tx_dict)

    assert from_dict.cache_info().hits == 2
    assert from_dict.cache_info().misses == 1
예제 #18
0
def test_memoize_from_dict(b):
    alice = generate_key_pair()
    asset = {
        'data': {'id': 'test_id'},
    }

    assert from_dict.cache_info().hits == 0
    assert from_dict.cache_info().misses == 0

    tx = Transaction.create([alice.public_key],
                            [([alice.public_key], 1)],
                            asset=asset,)\
                    .sign([alice.private_key])
    tx_dict = deepcopy(tx.to_dict())

    Transaction.from_dict(tx_dict)

    assert from_dict.cache_info().hits == 0
    assert from_dict.cache_info().misses == 1

    Transaction.from_dict(tx_dict)
    Transaction.from_dict(tx_dict)

    assert from_dict.cache_info().hits == 2
    assert from_dict.cache_info().misses == 1
예제 #19
0
    def get_spent(self, txid, output):
        """Check if a `txid` was already used as an input.

        A transaction can be used as an input for another transaction. Bigchain
        needs to make sure that a given `(txid, output)` is only used once.

        This method will check if the `(txid, output)` has already been
        spent in a transaction that is in either the `VALID`, `UNDECIDED` or
        `BACKLOG` state.

        Args:
            txid (str): The id of the transaction
            output (num): the index of the output in the respective transaction

        Returns:
            The transaction (Transaction) that used the `(txid, output)` as an
            input else `None`

        Raises:
            CriticalDoubleSpend: If the given `(txid, output)` was spent in
            more than one valid transaction.
        """
        # checks if an input was already spent
        # checks if the bigchain has any transaction with input {'txid': ...,
        # 'output': ...}
        transactions = list(backend.query.get_spent(self.connection, txid,
                                                    output))

        # a transaction_id should have been spent at most one time
        # determine if these valid transactions appear in more than one valid
        # block
        num_valid_transactions = 0
        non_invalid_transactions = []
        for transaction in transactions:
            # ignore transactions in invalid blocks
            # FIXME: Isn't there a faster solution than doing I/O again?
            txn, status = self.get_transaction(transaction['id'],
                                               include_status=True)
            if status == self.TX_VALID:
                num_valid_transactions += 1
            # `txid` can only have been spent in at most on valid block.
            if num_valid_transactions > 1:
                raise core_exceptions.CriticalDoubleSpend(
                    '`{}` was spent more than once. There is a problem'
                    ' with the chain'.format(txid))
            # if its not and invalid transaction
            if status is not None:
                transaction.update({'metadata': txn.metadata})
                non_invalid_transactions.append(transaction)

        if non_invalid_transactions:
            return Transaction.from_dict(non_invalid_transactions[0])
예제 #20
0
파일: core.py 프로젝트: yy1117/bigchaindb
    def get_spent(self, txid, output):
        """Check if a `txid` was already used as an input.

        A transaction can be used as an input for another transaction. Bigchain
        needs to make sure that a given `(txid, output)` is only used once.

        This method will check if the `(txid, output)` has already been
        spent in a transaction that is in either the `VALID`, `UNDECIDED` or
        `BACKLOG` state.

        Args:
            txid (str): The id of the transaction
            output (num): the index of the output in the respective transaction

        Returns:
            The transaction (Transaction) that used the `(txid, output)` as an
            input else `None`

        Raises:
            CriticalDoubleSpend: If the given `(txid, output)` was spent in
            more than one valid transaction.
        """
        # checks if an input was already spent
        # checks if the bigchain has any transaction with input {'txid': ...,
        # 'output': ...}
        transactions = list(
            backend.query.get_spent(self.connection, txid, output))

        # a transaction_id should have been spent at most one time
        # determine if these valid transactions appear in more than one valid
        # block
        num_valid_transactions = 0
        non_invalid_transactions = []
        for transaction in transactions:
            # ignore transactions in invalid blocks
            # FIXME: Isn't there a faster solution than doing I/O again?
            txn, status = self.get_transaction(transaction['id'],
                                               include_status=True)
            if status == self.TX_VALID:
                num_valid_transactions += 1
            # `txid` can only have been spent in at most on valid block.
            if num_valid_transactions > 1:
                raise core_exceptions.CriticalDoubleSpend(
                    '`{}` was spent more than once. There is a problem'
                    ' with the chain'.format(txid))
            # if its not and invalid transaction
            if status is not None:
                transaction.update({'metadata': txn.metadata})
                non_invalid_transactions.append(transaction)

        if non_invalid_transactions:
            return Transaction.from_dict(non_invalid_transactions[0])
예제 #21
0
    def post(self):
        """API endpoint to push transactions to the Federation.

        Return:
            A ``dict`` containing the data about the transaction.
        """
        pool = current_app.config['bigchain_pool']

        # `force` will try to format the body of the POST request even if the
        # `content-type` header is not set to `application/json`
        tx = request.get_json(force=True)

        try:
            tx_obj = Transaction.from_dict(tx)
        except SchemaValidationError as e:
            return make_error(
                400,
                message='Invalid transaction schema: {}'.format(
                    e.__cause__.message)
            )
        except ValidationError as e:
            return make_error(
                400,
                'Invalid transaction ({}): {}'.format(type(e).__name__, e)
            )

        with pool() as bigchain:
            bigchain.statsd.incr('web.tx.post')
            try:
                bigchain.validate_transaction(tx_obj)
            except ValidationError as e:
                return make_error(
                    400,
                    'Invalid transaction ({}): {}'.format(type(e).__name__, e)
                )
            else:
                bigchain.write_transaction(tx_obj)

        response = jsonify(tx)
        response.status_code = 202

        # NOTE: According to W3C, sending a relative URI is not allowed in the
        # Location Header:
        #   - https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
        #
        # Flask is autocorrecting relative URIs. With the following command,
        # we're able to prevent this.
        response.autocorrect_location_header = False
        status_monitor = '../statuses?transaction_id={}'.format(tx_obj.id)
        response.headers['Location'] = status_monitor
        return response
예제 #22
0
    def post(self):
        """API endpoint to push transactions to the Federation.

        Return:
            A ``dict`` containing the data about the transaction.
        """
        pool = current_app.config['bigchain_pool']

        # `force` will try to format the body of the POST request even if the
        # `content-type` header is not set to `application/json`
        tx = request.get_json(force=True)

        try:
            tx_obj = Transaction.from_dict(tx)
        except SchemaValidationError as e:
            return make_error(
                400,
                message='Invalid transaction schema: {}'.format(
                    e.__cause__.message)
            )
        except ValidationError as e:
            return make_error(
                400,
                'Invalid transaction ({}): {}'.format(type(e).__name__, e)
            )

        with pool() as bigchain:
            try:
                bigchain.validate_transaction(tx_obj)
            except ValidationError as e:
                return make_error(
                    400,
                    'Invalid transaction ({}): {}'.format(type(e).__name__, e)
                )
            else:
                bigchain.write_transaction(tx_obj)

        response = jsonify(tx)
        response.status_code = 202

        # NOTE: According to W3C, sending a relative URI is not allowed in the
        # Location Header:
        #   - https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
        #
        # Flask is autocorrecting relative URIs. With the following command,
        # we're able to prevent this.
        response.autocorrect_location_header = False
        status_monitor = '../statuses?transaction_id={}'.format(tx_obj.id)
        response.headers['Location'] = status_monitor
        return response
예제 #23
0
    def post(self):
        """API endpoint to push transactions to the Federation.

        Return:
            A ``dict`` containing the data about the transaction.
        """
        parser = reqparse.RequestParser()
        parser.add_argument('mode', type=parameters.valid_mode,
                            default='broadcast_tx_async')
        args = parser.parse_args()
        mode = str(args['mode'])

        pool = current_app.config['bigchain_pool']

        # `force` will try to format the body of the POST request even if the
        # `content-type` header is not set to `application/json`
        tx = request.get_json(force=True)

        try:
            tx_obj = Transaction.from_dict(tx)
        except SchemaValidationError as e:
            return make_error(
                400,
                message='Invalid transaction schema: {}'.format(
                    e.__cause__.message)
            )
        except ValidationError as e:
            return make_error(
                400,
                'Invalid transaction ({}): {}'.format(type(e).__name__, e)
            )

        with pool() as bigchain:
            try:
                bigchain.validate_transaction(tx_obj)
            except ValidationError as e:
                return make_error(
                    400,
                    'Invalid transaction ({}): {}'.format(type(e).__name__, e)
                )
            else:
                status_code, message = bigchain.write_transaction(tx_obj, mode)

        if status_code == 202:
            response = jsonify(tx)
            response.status_code = 202
            return response
        else:
            return make_error(status_code, message)
예제 #24
0
    def post(self):
        """API endpoint to push transactions to the Federation.

        Return:
            A ``dict`` containing the data about the transaction.
        """
        pool = current_app.config['bigchain_pool']
        monitor = current_app.config['monitor']

        # `force` will try to format the body of the POST request even if the
        # `content-type` header is not set to `application/json`
        tx = request.get_json(force=True)

        try:
            tx_obj = Transaction.from_dict(tx)
        except SchemaValidationError as e:
            return make_error(
                400,
                message='Invalid transaction schema: {}'.format(
                    e.__cause__.message)
            )
        except (ValidationError, InvalidSignature) as e:
            return make_error(
                400,
                'Invalid transaction ({}): {}'.format(type(e).__name__, e)
            )

        with pool() as bigchain:
            try:
                bigchain.validate_transaction(tx_obj)
            except (ValueError,
                    OperationError,
                    TransactionDoesNotExist,
                    TransactionOwnerError,
                    DoubleSpend,
                    InvalidHash,
                    InvalidSignature,
                    TransactionNotInValidBlock,
                    AmountError) as e:
                return make_error(
                    400,
                    'Invalid transaction ({}): {}'.format(type(e).__name__, e)
                )
            else:
                rate = bigchaindb.config['statsd']['rate']
                with monitor.timer('write_transaction', rate=rate):
                    bigchain.write_transaction(tx_obj)

        return tx
예제 #25
0
    def post(self):
        """API endpoint to push transactions to the Federation.

        Return:
            A ``dict`` containing the data about the transaction.
        """
        parser = reqparse.RequestParser()
        parser.add_argument('mode', type=parameters.valid_mode,
                            default='broadcast_tx_async')
        args = parser.parse_args()
        mode = str(args['mode'])

        pool = current_app.config['bigchain_pool']

        # `force` will try to format the body of the POST request even if the
        # `content-type` header is not set to `application/json`
        tx = request.get_json(force=True)

        try:
            tx_obj = Transaction.from_dict(tx)
        except SchemaValidationError as e:
            return make_error(
                400,
                message='Invalid transaction schema: {}'.format(
                    e.__cause__.message)
            )
        except ValidationError as e:
            return make_error(
                400,
                'Invalid transaction ({}): {}'.format(type(e).__name__, e)
            )

        with pool() as bigchain:
            try:
                bigchain.validate_transaction(tx_obj)
            except ValidationError as e:
                return make_error(
                    400,
                    'Invalid transaction ({}): {}'.format(type(e).__name__, e)
                )
            else:
                status_code, message = bigchain.write_transaction(tx_obj, mode)

        if status_code == 202:
            response = jsonify(tx)
            response.status_code = 202
            return response
        else:
            return make_error(status_code, message)
예제 #26
0
    def get_spent(self, txid, cid):
        """Check if a `txid` was already used as an input.

        A transaction can be used as an input for another transaction. Bigchain needs to make sure that a
        given `txid` is only used once.

        Args:
            txid (str): The id of the transaction
            cid (num): the index of the condition in the respective transaction

        Returns:
            The transaction (Transaction) that used the `txid` as an input else
            `None`
        """
        # checks if an input was already spent
        # checks if the bigchain has any transaction with input {'txid': ..., 'cid': ...}
        response = self.connection.run(
            r.table('bigchain', read_mode=self.read_mode).
            concat_map(lambda doc: doc['block']['transactions']).filter(
                lambda transaction: transaction['transaction']['fulfillments'].
                contains(lambda fulfillment: fulfillment['input'] == {
                    'txid': txid,
                    'cid': cid
                })))

        transactions = list(response)

        # a transaction_id should have been spent at most one time
        if transactions:
            # determine if these valid transactions appear in more than one valid block
            num_valid_transactions = 0
            for transaction in transactions:
                # ignore invalid blocks
                # FIXME: Isn't there a faster solution than doing I/O again?
                if self.get_transaction(transaction['id']):
                    num_valid_transactions += 1
                if num_valid_transactions > 1:
                    raise exceptions.DoubleSpend(
                        '`{}` was spent more then once. There is a problem with the chain'
                        .format(txid))

            if num_valid_transactions:
                return Transaction.from_dict(transactions[0])
            else:
                # all queried transactions were invalid
                return None
        else:
            return None
예제 #27
0
    def get_txs_by_asset_id(self, asset_id):
        """Retrieves transactions related to a particular asset.

        A digital asset in bigchaindb is identified by an uuid. This allows us to query all the transactions
        related to a particular digital asset, knowing the id.

        Args:
            asset_id (str): the id for this particular metadata.

        Returns:
            A list of transactions containing related to the asset. If no transaction exists for that asset it
            returns an empty list `[]`
        """

        cursor = self.backend.get_transactions_by_asset_id(asset_id)
        return [Transaction.from_dict(tx) for tx in cursor]
예제 #28
0
파일: lib.py 프로젝트: roderik/bigchaindb
    def validate_transaction(self, tx, current_transactions=[]):
        """Validate a transaction against the current status of the database."""

        transaction = tx

        # CLEANUP: The conditional below checks for transaction in dict format.
        # It would be better to only have a single format for the transaction
        # throught the code base.
        if isinstance(transaction, dict):
            try:
                transaction = Transaction.from_dict(tx)
            except SchemaValidationError as e:
                logger.warning('Invalid transaction schema: %s', e.__cause__.message)
                return False
            except ValidationError as e:
                logger.warning('Invalid transaction (%s): %s', type(e).__name__, e)
                return False
        return transaction.validate(self, current_transactions)
예제 #29
0
    def get_tx_by_metadata_id(self, metadata_id):
        """Retrieves transactions related to a metadata.

        When creating a transaction one of the optional arguments is the `metadata`. The metadata is a generic
        dict that contains extra information that can be appended to the transaction.

        To make it easy to query the bigchain for that particular metadata we create a UUID for the metadata and
        store it with the transaction.

        Args:
            metadata_id (str): the id for this particular metadata.

        Returns:
            A list of transactions containing that metadata. If no transaction exists with that metadata it
            returns an empty list `[]`
        """
        cursor = self.backend.get_transactions_by_metadata_id(metadata_id)
        return [Transaction.from_dict(tx) for tx in cursor]
예제 #30
0
    def post(self):
        pool = current_app.config['bigchain_pool']
        tx = request.get_json(force=True)
        tx_obj = Transaction.from_dict(tx)
        with pool() as bigchain:
            try:
                bigchain.validate_transaction(tx_obj)
            except (ValueError, OperationError, TransactionDoesNotExist,
                    TransactionOwnerError, FulfillmentNotInValidBlock,
                    DoubleSpend, InvalidHash, InvalidSignature,
                    AmountError) as e:
                return make_error(
                    400,
                    'Invalid transaction ({}): {}'.format(type(e).__name__, e))
            else:
                bigchain.write_transaction(tx_obj)

        return tx, 202
예제 #31
0
    def validate_transaction(self, tx, current_transactions=[]):
        """Validate a transaction against the current status of the database."""

        transaction = tx

        # CLEANUP: The conditional below checks for transaction in dict format.
        # It would be better to only have a single format for the transaction
        # throught the code base.
        if isinstance(transaction, dict):
            try:
                transaction = Transaction.from_dict(tx)
            except SchemaValidationError as e:
                logger.warning('Invalid transaction schema: %s', e.__cause__.message)
                return False
            except ValidationError as e:
                logger.warning('Invalid transaction (%s): %s', type(e).__name__, e)
                return False
        return transaction.validate(self, current_transactions)
예제 #32
0
파일: lib.py 프로젝트: roderik/bigchaindb
    def get_transaction(self, transaction_id):
        transaction = backend.query.get_transaction(self.connection, transaction_id)

        if transaction:
            asset = backend.query.get_asset(self.connection, transaction_id)
            metadata = backend.query.get_metadata(self.connection, [transaction_id])
            if asset:
                transaction['asset'] = asset

            if 'metadata' not in transaction:
                metadata = metadata[0] if metadata else None
                if metadata:
                    metadata = metadata.get('metadata')

                transaction.update({'metadata': metadata})

            transaction = Transaction.from_dict(transaction)

        return transaction
예제 #33
0
    def get_transaction(self, transaction_id):
        transaction = backend.query.get_transaction(self.connection, transaction_id)

        if transaction:
            asset = backend.query.get_asset(self.connection, transaction_id)
            metadata = backend.query.get_metadata(self.connection, [transaction_id])
            if asset:
                transaction['asset'] = asset

            if 'metadata' not in transaction:
                metadata = metadata[0] if metadata else None
                if metadata:
                    metadata = metadata.get('metadata')

                transaction.update({'metadata': metadata})

            transaction = Transaction.from_dict(transaction)

        return transaction
예제 #34
0
파일: core.py 프로젝트: soonwen/bigchaindb
    def get_spent(self, txid, output):
        """Check if a `txid` was already used as an input.

        A transaction can be used as an input for another transaction. Bigchain needs to make sure that a
        given `txid` is only used once.

        Args:
            txid (str): The id of the transaction
            output (num): the index of the output in the respective transaction

        Returns:
            The transaction (Transaction) that used the `txid` as an input else
            `None`
        """
        # checks if an input was already spent
        # checks if the bigchain has any transaction with input {'txid': ...,
        # 'output': ...}
        transactions = list(
            backend.query.get_spent(self.connection, txid, output))

        # a transaction_id should have been spent at most one time
        if transactions:
            # determine if these valid transactions appear in more than one valid block
            num_valid_transactions = 0
            for transaction in transactions:
                # ignore invalid blocks
                # FIXME: Isn't there a faster solution than doing I/O again?
                if self.get_transaction(transaction['id']):
                    num_valid_transactions += 1
                if num_valid_transactions > 1:
                    raise exceptions.DoubleSpend(
                        ('`{}` was spent more than'
                         ' once. There is a problem'
                         ' with the chain').format(txid))

            if num_valid_transactions:
                return Transaction.from_dict(transactions[0])
            else:
                # all queried transactions were invalid
                return None
        else:
            return None
예제 #35
0
    def get_txs_by_asset_id(self, asset_id):
        """Retrieves transactions related to a particular asset.

        A digital asset in bigchaindb is identified by an uuid. This allows us to query all the transactions
        related to a particular digital asset, knowing the id.

        Args:
            asset_id (str): the id for this particular metadata.

        Returns:
            A list of transactions containing related to the asset. If no transaction exists for that asset it
            returns an empty list `[]`
        """
        cursor = self.connection.run(
            r.table('bigchain', read_mode=self.read_mode)
             .get_all(asset_id, index='asset_id')
             .concat_map(lambda block: block['block']['transactions'])
             .filter(lambda transaction: transaction['transaction']['asset']['id'] == asset_id))

        return [Transaction.from_dict(tx) for tx in cursor]
예제 #36
0
    def validate_transaction(self, tx, current_transactions=[]):
        """Validate a transaction against the current status of the database."""

        transaction = tx

        if not isinstance(transaction, Transaction):
            try:
                transaction = Transaction.from_dict(tx)
            except SchemaValidationError as e:
                logger.warning('Invalid transaction schema: %s', e.__cause__.message)
                return False
            except ValidationError as e:
                logger.warning('Invalid transaction (%s): %s', type(e).__name__, e)
                return False
        try:
            return transaction.validate(self, current_transactions)
        except ValidationError as e:
            logger.warning('Invalid transaction (%s): %s', type(e).__name__, e)
            return False
        return transaction
예제 #37
0
    def validate_tx(self, tx):
        """Validate a transaction.

        Also checks if the transaction already exists in the blockchain. If it
        does, or it's invalid, it's deleted from the backlog immediately.

        Args:
            tx (dict): the transaction to validate.

        Returns:
            :class:`~bigchaindb.models.Transaction`: The transaction if valid,
            ``None`` otherwise.
        """
        try:
            tx = Transaction.from_dict(tx)
        except ValidationError:
            return None

        # If transaction is in any VALID or UNDECIDED block we
        # should not include it again
        if not self.bigchain.is_new_transaction(tx.id):
            self.bigchain.delete_transaction(tx.id)
            return None

        # If transaction is not valid it should not be included
        try:
            # Do not allow an externally submitted GENESIS transaction.
            # A simple check is enough as a pipeline is started only after the
            # creation of GENESIS block, or after the verification of a GENESIS
            # block. Voting will fail at a later stage if the GENESIS block is
            # absent.
            if tx.operation == Transaction.GENESIS:
                raise GenesisBlockAlreadyExistsError(
                    'Duplicate GENESIS transaction')

            tx.validate(self.bigchain)
            return tx
        except ValidationError as e:
            logger.warning('Invalid tx: %s', e)
            self.bigchain.delete_transaction(tx.id)
            return None
예제 #38
0
파일: block.py 프로젝트: cgwyx/bigchaindb
    def validate_tx(self, tx):
        """Validate a transaction.

        Also checks if the transaction already exists in the blockchain. If it
        does, or it's invalid, it's deleted from the backlog immediately.

        Args:
            tx (dict): the transaction to validate.

        Returns:
            :class:`~bigchaindb.models.Transaction`: The transaction if valid,
            ``None`` otherwise.
        """
        try:
            tx = Transaction.from_dict(tx)
        except ValidationError:
            return None

        # If transaction is in any VALID or UNDECIDED block we
        # should not include it again
        if not self.bigchain.is_new_transaction(tx.id):
            self.bigchain.delete_transaction(tx.id)
            return None

        # If transaction is not valid it should not be included
        try:
            # Do not allow an externally submitted GENESIS transaction.
            # A simple check is enough as a pipeline is started only after the
            # creation of GENESIS block, or after the verification of a GENESIS
            # block. Voting will fail at a later stage if the GENESIS block is
            # absent.
            if tx.operation == Transaction.GENESIS:
                raise GenesisBlockAlreadyExistsError('Duplicate GENESIS transaction')

            tx.validate(self.bigchain)
            return tx
        except ValidationError as e:
            logger.warning('Invalid tx: %s', e)
            self.bigchain.delete_transaction(tx.id)
            return None
예제 #39
0
    def post(self):
        """API endpoint to push transactions to the Federation.

        Return:
            A ``dict`` containing the data about the transaction.
        """
        pool = current_app.config['bigchain_pool']
        monitor = current_app.config['monitor']

        # `force` will try to format the body of the POST request even if the
        # `content-type` header is not set to `application/json`
        tx = request.get_json(force=True)

        try:
            tx_obj = Transaction.from_dict(tx)
        except SchemaValidationError as e:
            return make_error(400,
                              message='Invalid transaction schema: {}'.format(
                                  e.__cause__.message))
        except (ValidationError, InvalidSignature) as e:
            return make_error(
                400,
                'Invalid transaction ({}): {}'.format(type(e).__name__, e))

        with pool() as bigchain:
            try:
                bigchain.validate_transaction(tx_obj)
            except (ValueError, OperationError, TransactionDoesNotExist,
                    TransactionOwnerError, DoubleSpend, InvalidHash,
                    InvalidSignature, TransactionNotInValidBlock,
                    AmountError) as e:
                return make_error(
                    400,
                    'Invalid transaction ({}): {}'.format(type(e).__name__, e))
            else:
                rate = bigchaindb.config['statsd']['rate']
                with monitor.timer('write_transaction', rate=rate):
                    bigchain.write_transaction(tx_obj)

        return tx
예제 #40
0
파일: core.py 프로젝트: sbellem/bigchaindb
    def get_spent(self, txid, cid):
        """Check if a `txid` was already used as an input.

        A transaction can be used as an input for another transaction. Bigchain needs to make sure that a
        given `txid` is only used once.

        Args:
            txid (str): The id of the transaction
            cid (num): the index of the condition in the respective transaction

        Returns:
            The transaction (Transaction) that used the `txid` as an input else
            `None`
        """
        # checks if an input was already spent
        # checks if the bigchain has any transaction with input {'txid': ..., 'cid': ...}
        transactions = list(backend.query.get_spent(self.connection, txid, cid))

        # a transaction_id should have been spent at most one time
        if transactions:
            # determine if these valid transactions appear in more than one valid block
            num_valid_transactions = 0
            for transaction in transactions:
                # ignore invalid blocks
                # FIXME: Isn't there a faster solution than doing I/O again?
                if self.get_transaction(transaction['id']):
                    num_valid_transactions += 1
                if num_valid_transactions > 1:
                    raise exceptions.DoubleSpend(('`{}` was spent more than'
                                                  ' once. There is a problem'
                                                  ' with the chain')
                                                 .format(txid))

            if num_valid_transactions:
                return Transaction.from_dict(transactions[0])
            else:
                # all queried transactions were invalid
                return None
        else:
            return None
예제 #41
0
    def get_transaction(self, transaction_id, include_status=False):
        transaction = backend.query.get_transaction(self.connection, transaction_id)

        if transaction:
            asset = backend.query.get_asset(self.connection, transaction_id)
            metadata = backend.query.get_metadata(self.connection, [transaction_id])
            if asset:
                transaction['asset'] = asset

            if 'metadata' not in transaction:
                metadata = metadata[0] if metadata else None
                if metadata:
                    metadata = metadata.get('metadata')

                transaction.update({'metadata': metadata})

            transaction = Transaction.from_dict(transaction)

        if include_status:
            return transaction, self.TX_VALID if transaction else None
        else:
            return transaction
예제 #42
0
    def post(self):
        """API endpoint to push transactions to the Federation.

        Return:
            A ``dict`` containing the data about the transaction.
        """
        pool = current_app.config['bigchain_pool']

        # `force` will try to format the body of the POST request even if the
        # `content-type` header is not set to `application/json`
        tx = request.get_json(force=True)

        try:
            tx_obj = Transaction.from_dict(tx)
        except SchemaValidationError as e:
            return make_error(
                400,
                message='Invalid transaction schema: {}'.format(
                    e.__cause__.message)
            )
        except ValidationError as e:
            return make_error(
                400,
                'Invalid transaction ({}): {}'.format(type(e).__name__, e)
            )

        with pool() as bigchain:
            try:
                bigchain.validate_transaction(tx_obj)
            except ValidationError as e:
                return make_error(
                    400,
                    'Invalid transaction ({}): {}'.format(type(e).__name__, e)
                )
            else:
                bigchain.write_transaction(tx_obj)

        return tx, 202
예제 #43
0
    def post(self):
        print("createContractTx")

        pool = current_app.config['bigchain_pool']
        contractTx = request.get_json(force=True)
        # print(111)
        print(contractTx)

        contractTx_obj = Transaction.from_dict(contractTx)
        # TODO validate data structure /version=2;opercation=create/transfer;    has relation and contact?
        print("222====",contractTx_obj)
        with pool() as bigchain:
            try:
                # print("333")
                bigchain.validate_transaction(contractTx_obj)
                # print("444")
            except (ValueError,
                    OperationError,
                    TransactionDoesNotExist,
                    TransactionOwnerError,
                    FulfillmentNotInValidBlock,
                    DoubleSpend,
                    InvalidHash,
                    InvalidSignature,
                    AmountError) as e:
                return make_response(constant.RESPONSE_STATUS_PARAM_ERROE,
                                     constant.RESPONSE_CODE_PARAM_ERROE,
                                     "invalidate contract transaction.")
            # print(contractTx_obj.metadata.to_dict())
            tx_result = bigchain.write_transaction(contractTx_obj)
            result_messages = "add contract transaction success"

        return make_response(constant.RESPONSE_STATUS_SUCCESS,
                             constant.RESPONSE_CODE_SUCCESS,
                             result_messages,
                             tx_result)
예제 #44
0
파일: core.py 프로젝트: sbellem/bigchaindb
    def get_transaction(self, txid, include_status=False):
        """Get the transaction with the specified `txid` (and optionally its status)

        This query begins by looking in the bigchain table for all blocks containing
        a transaction with the specified `txid`. If one of those blocks is valid, it
        returns the matching transaction from that block. Else if some of those
        blocks are undecided, it returns a matching transaction from one of them. If
        the transaction was found in invalid blocks only, or in no blocks, then this
        query looks for a matching transaction in the backlog table, and if it finds
        one there, it returns that.

        Args:
            txid (str): transaction id of the transaction to get
            include_status (bool): also return the status of the transaction
                                   the return value is then a tuple: (tx, status)

        Returns:
            A :class:`~.models.Transaction` instance if the transaction
            was found in a valid block, an undecided block, or the backlog table,
            otherwise ``None``.
            If :attr:`include_status` is ``True``, also returns the
            transaction's status if the transaction was found.
        """

        response, tx_status = None, None

        validity = self.get_blocks_status_containing_tx(txid)
        check_backlog = True

        if validity:
            # Disregard invalid blocks, and return if there are no valid or undecided blocks
            validity = {_id: status for _id, status in validity.items()
                        if status != Bigchain.BLOCK_INVALID}
            if validity:

                # The transaction _was_ found in an undecided or valid block,
                # so there's no need to look in the backlog table
                check_backlog = False

                tx_status = self.TX_UNDECIDED
                # If the transaction is in a valid or any undecided block, return it. Does not check
                # if transactions in undecided blocks are consistent, but selects the valid block
                # before undecided ones
                for target_block_id in validity:
                    if validity[target_block_id] == Bigchain.BLOCK_VALID:
                        tx_status = self.TX_VALID
                        break

                # Query the transaction in the target block and return
                response = backend.query.get_transaction_from_block(self.connection, txid, target_block_id)

        if check_backlog:
            response = backend.query.get_transaction_from_backlog(self.connection, txid)

            if response:
                tx_status = self.TX_IN_BACKLOG

        if response:
            response = Transaction.from_dict(response)

        if include_status:
            return response, tx_status
        else:
            return response