Пример #1
0
    def get_spent(self, tx_input):
        """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:
            tx_input (dict): Input of a transaction in the form `{'txid': 'transaction id', 'cid': 'condition id'}`

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

        # a transaction_id should have been spent at most one time
        transactions = list(response)
        if transactions:
            if len(transactions) != 1:
                raise exceptions.DoubleSpend('`{}` was spent more then once. There is a problem with the chain'.format(
                    tx_input['txid']))
            else:
                return transactions[0]
        else:
            return None
Пример #2
0
    def validate_transaction(self, transaction):
        """Validate a transaction.

        Args:
            transaction (dict): transaction to validate.

        Returns:
            The transaction if the transaction is valid else it raises and exception
            describing the reason why the transaction is invalid.

        Raises:
            OperationError: if the transaction operation is not supported
            TransactionDoesNotExist: if the input of the transaction is not found
            TransactionOwnerError: if the new transaction is using an input it doesn't own
            DoubleSpend: if the transaction is a double spend
            InvalidHash: if the hash of the transaction is wrong
            InvalidSignature: if the signature of the transaction is wrong
        """

        # If the operation is CREATE the transaction should have no inputs and should be signed by a
        # federation node
        if transaction['transaction']['operation'] == 'CREATE':
            if transaction['transaction']['input']:
                raise ValueError('A CREATE operation has no inputs')
            if not (set(transaction['transaction']['current_owners']) <=
                    set(self.federation_nodes + [self.me])):
                raise exceptions.OperationError(
                    'Only federation nodes can use the operation `CREATE`')

        else:
            # check if the input exists, is owned by the current_owner
            if not transaction['transaction']['input']:
                raise ValueError(
                    'Only `CREATE` transactions can have null inputs')

            tx_input = self.get_transaction(
                transaction['transaction']['input'])
            if not tx_input:
                raise exceptions.TransactionDoesNotExist(
                    'input `{}` does not exist in the bigchain'.format(
                        transaction['transaction']['input']))

            if tx_input['transaction']['new_owners'] != transaction[
                    'transaction']['current_owners']:
                raise exceptions.TransactionOwnerError(
                    'current_owner `{}` does not own the input `{}`'.format(
                        transaction['transaction']['current_owners'],
                        transaction['transaction']['input']))

            # check if the input was already spent by a transaction other then this one.
            spent = self.get_spent(tx_input['id'])
            if spent:
                if spent['id'] != transaction['id']:
                    raise exceptions.DoubleSpend(
                        'input `{}` was already spent'.format(
                            transaction['transaction']['input']))

        util.check_hash_and_signature(transaction)
        return transaction
Пример #3
0
    def get_spent(self, tx_input):
        """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:
            tx_input (dict): Input of a transaction in the form `{'txid': 'transaction id', 'cid': 'condition id'}`

        Returns:
            The transaction that used the `txid` as an input if it exists else it returns `None`
        """
        # checks if an input was already spent
        # checks if the bigchain has any transaction with input {'txid': ..., 'cid': ...}
        response = 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'] == tx_input))\
            .run(self.conn)

        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
                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(tx_input['txid']))

            if num_valid_transactions:
                return transactions[0]
            else:
                # all queried transactions were invalid
                return None
        else:
            return None
Пример #4
0
    def validate_transaction(bigchain, transaction):
        """Validate a transaction.

        Args:
            bigchain (Bigchain): an instantiated bigchaindb.Bigchain object.
            transaction (dict): transaction to validate.

        Returns:
            The transaction if the transaction is valid else it raises an
            exception describing the reason why the transaction is invalid.

        Raises:
            OperationError: if the transaction operation is not supported
            TransactionDoesNotExist: if the input of the transaction is not found
            TransactionOwnerError: if the new transaction is using an input it doesn't own
            DoubleSpend: if the transaction is a double spend
            InvalidHash: if the hash of the transaction is wrong
            InvalidSignature: if the signature of the transaction is wrong
        """

        # If the operation is CREATE the transaction should have no inputs and
        # should be signed by a federation node
        if transaction['transaction']['operation'] == 'CREATE':
            if transaction['transaction']['input']:
                raise ValueError('A CREATE operation has no inputs')
            if transaction['transaction']['current_owner'] not in (
                    bigchain.federation_nodes + [bigchain.me]):
                raise exceptions.OperationError(
                    'Only federation nodes can use the operation `CREATE`')

        else:
            # check if the input exists, is owned by the current_owner
            if not transaction['transaction']['input']:
                raise ValueError(
                    'Only `CREATE` transactions can have null inputs')

            tx_input = bigchain.get_transaction(
                transaction['transaction']['input'])

            if not tx_input:
                raise exceptions.TransactionDoesNotExist(
                    'input `{}` does not exist in the bigchain'.format(
                        transaction['transaction']['input']))

            if (tx_input['transaction']['new_owner'] !=
                    transaction['transaction']['current_owner']):
                raise exceptions.TransactionOwnerError(
                    'current_owner `{}` does not own the input `{}`'.format(
                        transaction['transaction']['current_owner'],
                        transaction['transaction']['input']))

            # check if the input was already spent by a transaction other than
            # this one.
            spent = bigchain.get_spent(tx_input['id'])
            if spent and spent['id'] != transaction['id']:
                raise exceptions.DoubleSpend(
                    'input `{}` was already spent'.format(
                        transaction['transaction']['input']))

        # Check hash of the transaction
        calculated_hash = crypto.hash_data(
            util.serialize(transaction['transaction']))
        if calculated_hash != transaction['id']:
            raise exceptions.InvalidHash()

        # Check signature
        if not util.verify_signature(transaction):
            raise exceptions.InvalidSignature()

        return transaction
Пример #5
0
    def validate_transaction(bigchain, transaction):
        """Validate a transaction.

        Args:
            bigchain (Bigchain): an instantiated bigchaindb.Bigchain object.
            transaction (dict): transaction to validate.

        Returns:
            The transaction if the transaction is valid else it raises an
            exception describing the reason why the transaction is invalid.

        Raises:
            OperationError: if the transaction operation is not supported
            TransactionDoesNotExist: if the input of the transaction is not found
            TransactionOwnerError: if the new transaction is using an input it doesn't own
            DoubleSpend: if the transaction is a double spend
            InvalidHash: if the hash of the transaction is wrong
            InvalidSignature: if the signature of the transaction is wrong
        """

        # If the operation is CREATE the transaction should have no inputs and
        # should be signed by a federation node
        if transaction['transaction']['operation'] == 'CREATE':
            # TODO: for now lets assume a CREATE transaction only has one fulfillment
            if transaction['transaction']['fulfillments'][0]['input']:
                raise ValueError('A CREATE operation has no inputs')
            # TODO: for now lets assume a CREATE transaction only has one current_owner
            if transaction['transaction']['fulfillments'][0]['current_owners'][
                    0] not in (bigchain.nodes_except_me + [bigchain.me]):
                raise exceptions.OperationError(
                    'Only federation nodes can use the operation `CREATE`')

        else:
            # check if the input exists, is owned by the current_owner
            if not transaction['transaction']['fulfillments']:
                raise ValueError('Transaction contains no fulfillments')

            # check inputs
            for fulfillment in transaction['transaction']['fulfillments']:
                if not fulfillment['input']:
                    raise ValueError(
                        'Only `CREATE` transactions can have null inputs')
                tx_input = bigchain.get_transaction(
                    fulfillment['input']['txid'])

                if not tx_input:
                    raise exceptions.TransactionDoesNotExist(
                        'input `{}` does not exist in the bigchain'.format(
                            fulfillment['input']['txid']))
                # TODO: check if current owners own tx_input (maybe checked by InvalidSignature)
                # check if the input was already spent by a transaction other than
                # this one.
                spent = bigchain.get_spent(fulfillment['input'])
                if spent and spent['id'] != transaction['id']:
                    raise exceptions.DoubleSpend(
                        'input `{}` was already spent'.format(
                            fulfillment['input']))

        # Check hash of the transaction
        calculated_hash = util.get_hash_data(transaction)
        if calculated_hash != transaction['id']:
            raise exceptions.InvalidHash()

        # Check fulfillments
        if not util.validate_fulfillments(transaction):
            raise exceptions.InvalidSignature()

        return transaction