Exemplo n.º 1
0
def create_tx(current_owner, new_owner, tx_input, operation, payload=None):
    """Create a new transaction

    A transaction in the bigchain is a transfer of a digital asset between two entities represented
    by public keys.

    Currently the bigchain supports two types of operations:

        `CREATE` - Only federation nodes are allowed to use this operation. In a create operation
        a federation node creates a digital asset in the bigchain and assigns that asset to a public
        key. The owner of the private key can then decided to transfer this digital asset by using the
        `transaction id` of the transaction as an input in a `TRANSFER` transaction.

        `TRANSFER` - A transfer operation allows for a transfer of the digital assets between entities.

    Args:
        current_owner (str): base58 encoded public key of the current owner of the asset.
        new_owner (str): base58 encoded public key of the new owner of the digital asset.
        tx_input (str): id of the transaction to use as input.
        operation (str): Either `CREATE` or `TRANSFER` operation.
        payload (Optional[dict]): dictionary with information about asset.

    Returns:
        dict: unsigned transaction.


    Raises:
        TypeError: if the optional ``payload`` argument is not a ``dict``.
    """

    data = None
    if payload is not None:
        if isinstance(payload, dict):
            hash_payload = crypto.hash_data(serialize(payload))
            data = {'hash': hash_payload, 'payload': payload}
        else:
            raise TypeError('`payload` must be an dict instance')

    hash_payload = crypto.hash_data(serialize(payload))
    data = {'hash': hash_payload, 'payload': payload}

    tx = {
        'current_owner': current_owner,
        'new_owner': new_owner,
        'input': tx_input,
        'operation': operation,
        'timestamp': timestamp(),
        'data': data
    }

    # serialize and convert to bytes
    tx_serialized = serialize(tx)
    tx_hash = crypto.hash_data(tx_serialized)

    # create the transaction
    transaction = {'id': tx_hash, 'transaction': tx}

    return transaction
Exemplo n.º 2
0
    def test_invalid_transactions_in_block(self, b, user_public_key, ):
        # invalid transaction
        valid_input = b.get_owned_ids(user_public_key).pop()
        tx_invalid = b.create_transaction('a', 'b', valid_input, 'c')

        block = b.create_block([tx_invalid])

        # create a block with invalid transactions
        block = {
            'timestamp': util.timestamp(),
            'transactions': [tx_invalid],
            'node_pubkey': b.me,
            'voters': b.federation_nodes
        }

        block_data = util.serialize(block)
        block_hash = crypto.hash_data(block_data)
        block_signature = crypto.SigningKey(b.me_private).sign(block_data)

        block = {
            'id': block_hash,
            'block': block,
            'signature': block_signature,
            'votes': []
        }

        with pytest.raises(exceptions.TransactionOwnerError) as excinfo:
            b.validate_block(block)

        assert excinfo.value.args[0] == 'current_owner `a` does not own the input `{}`'.format(valid_input)
Exemplo n.º 3
0
    def create_block(self, validated_transactions):
        """Creates a block given a list of `validated_transactions`.

        Note that this method does not validate the transactions. Transactions should be validated before
        calling create_block.

        Args:
            validated_transactions (list): list of validated transactions.

        Returns:
            dict: created block.
        """

        # Prevent the creation of empty blocks
        if len(validated_transactions) == 0:
            raise exceptions.OperationError("Empty block creation is not allowed")

        # Create the new block
        block = {
            "timestamp": util.timestamp(),
            "transactions": validated_transactions,
            "node_pubkey": self.me,
            "voters": self.nodes_except_me + [self.me],
        }

        # Calculate the hash of the new block
        block_data = util.serialize(block)
        block_hash = crypto.hash_data(block_data)
        block_signature = crypto.SigningKey(self.me_private).sign(block_data)

        block = {"id": block_hash, "block": block, "signature": block_signature}

        return block
    def test_invalid_transactions_in_block(self, b):
        # invalid transaction
        create_inputs()
        valid_input = b.get_owned_ids(USER_PUBLIC_KEY).pop()
        tx_invalid = b.create_transaction('a', 'b', valid_input, 'c')

        block = b.create_block([tx_invalid])
        assert invalid_transactions == [tx_invalid]

        # create a block with invalid transactions
        block = {
            'timestamp': b.timestamp(),
            'transactions': [tx_invalid],
            'node_pubkey': b.me,
            'voters': b.federation_nodes
        }

        block_data = b.serialize(block)
        block_hash = hash_data(block_data)
        block_signature = PrivateKey(b.me_private).sign(block_data)

        block = {
            'id': block_hash,
            'block': block,
            'signature': block_signature,
            'votes': []
        }

        with pytest.raises(exceptions.TransactionOwnerError) as excinfo:
            b.validate_block(block)

        assert excinfo.value.args[0] == 'current_owner `a` does not own the input `{}`'.format(valid_input)
Exemplo n.º 5
0
    def validate_block(bigchain, block):
        """Validate a block.

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

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

        Raises:
            InvalidHash: if the hash of the block is wrong.
        """

        # Check if current hash is correct
        calculated_hash = crypto.hash_data(util.serialize(block['block']))
        if calculated_hash != block['id']:
            raise exceptions.InvalidHash()

        # Check if the block was created by a federation node
        if block['block']['node_pubkey'] not in (bigchain.nodes_except_me +
                                                 [bigchain.me]):
            raise exceptions.OperationError(
                'Only federation nodes can create blocks')

        # Check if block signature is valid
        verifying_key = crypto.VerifyingKey(block['block']['node_pubkey'])
        if not verifying_key.verify(util.serialize(block['block']),
                                    block['signature']):
            raise exceptions.InvalidSignature('Invalid block signature')

        return block
    def validate_block(bigchain, block):
        """Validate a block.

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

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

        Raises:
            InvalidHash: if the hash of the block is wrong.
        """

        # Check if current hash is correct
        calculated_hash = crypto.hash_data(util.serialize(block['block']))
        if calculated_hash != block['id']:
            raise exceptions.InvalidHash()

        # Check if the block was created by a federation node
        if block['block']['node_pubkey'] not in (bigchain.federation_nodes + [bigchain.me]):
            raise exceptions.OperationError('Only federation nodes can create blocks')

        # Check if block signature is valid
        verifying_key = crypto.VerifyingKey(block['block']['node_pubkey'])
        if not verifying_key.verify(util.serialize(block['block']), block['signature']):
            raise exceptions.InvalidSignature('Invalid block signature')

        return block
Exemplo n.º 7
0
    def test_invalid_transactions_in_block(self, b, user_public_key, ):
        # invalid transaction
        valid_input = b.get_owned_ids(user_public_key).pop()
        tx_invalid = b.create_transaction('a', 'b', valid_input, 'c')

        block = b.create_block([tx_invalid])
        assert invalid_transactions == [tx_invalid]

        # create a block with invalid transactions
        block = {
            'timestamp': b.timestamp(),
            'transactions': [tx_invalid],
            'node_pubkey': b.me,
            'voters': b.federation_nodes
        }

        block_data = b.serialize(block)
        block_hash = hash_data(block_data)
        block_signature = PrivateKey(b.me_private).sign(block_data)

        block = {
            'id': block_hash,
            'block': block,
            'signature': block_signature,
            'votes': []
        }

        with pytest.raises(exceptions.TransactionOwnerError) as excinfo:
            b.validate_block(block)

        assert excinfo.value.args[0] == 'current_owner `a` does not own the input `{}`'.format(valid_input)
Exemplo n.º 8
0
def exists(digital_asset_payload):
    payload_hash = crypto.hash_data(util.serialize(digital_asset_payload))

    transactions = b.get_tx_by_payload_hash(payload_hash)
    result = transactions is not None and len(transactions) > 0

    return result
Exemplo n.º 9
0
    def create_block(self, validated_transactions):
        """Creates a block given a list of `validated_transactions`.

        Note that this method does not validate the transactions. Transactions should be validated before
        calling create_block.

        Args:
            validated_transactions (list): list of validated transactions.

        Returns:
            dict: created block.
        """

        # Create the new block
        block = {
            'timestamp': util.timestamp(),
            'transactions': validated_transactions,
            'node_pubkey': self.me,
            'voters': self.federation_nodes + [self.me]
        }

        # Calculate the hash of the new block
        block_data = util.serialize(block)
        block_hash = crypto.hash_data(block_data)
        block_signature = crypto.SigningKey(self.me_private).sign(block_data)

        block = {
            'id': block_hash,
            'block': block,
            'signature': block_signature,
            'votes': []
        }

        return block
Exemplo n.º 10
0
def exists(digital_asset_payload):
    payload_hash = crypto.hash_data(util.serialize(digital_asset_payload))

    transactions = b.get_tx_by_payload_hash(payload_hash)
    result = transactions is not None and len(transactions) > 0

    return result
Exemplo n.º 11
0
    def create_block(self, validated_transactions):
        """Creates a block given a list of `validated_transactions`.

        Note that this method does not validate the transactions. Transactions should be validated before
        calling create_block.

        Args:
            validated_transactions (list): list of validated transactions.

        Returns:
            dict: created block.
        """

        # Create the new block
        block = {
            'timestamp': util.timestamp(),
            'transactions': validated_transactions,
            'node_pubkey': self.me,
            'voters': self.federation_nodes + [self.me]
        }

        # Calculate the hash of the new block
        block_data = util.serialize(block)
        block_hash = crypto.hash_data(block_data)
        block_signature = crypto.SigningKey(self.me_private).sign(block_data).decode()

        block = {
            'id': block_hash,
            'block': block,
            'signature': block_signature,
            'votes': []
        }

        return block
Exemplo n.º 12
0
def check_hash_and_signature(transaction):
    # Check hash of the transaction
    calculated_hash = crypto.hash_data(serialize(transaction['transaction']))
    if calculated_hash != transaction['id']:
        raise exceptions.InvalidHash()

    # Check signature
    if not verify_signature(transaction):
        raise exceptions.InvalidSignature()
Exemplo n.º 13
0
    def test_create_new_block(self, b):
        new_block = b.create_block([])
        block_hash = hash_data(b.serialize(new_block['block']))

        assert new_block['block']['voters'] == [b.me]
        assert new_block['block']['node_pubkey'] == b.me
        assert PublicKey(b.me).verify(b.serialize(new_block['block']), new_block['signature']) is True
        assert new_block['id'] == block_hash
        assert new_block['votes'] == []
Exemplo n.º 14
0
def check_hash_and_signature(transaction):
    # Check hash of the transaction
    calculated_hash = hash_data(serialize(transaction['transaction']))
    if calculated_hash != transaction['id']:
        raise exceptions.InvalidHash()

    # Check signature
    if not verify_signature(transaction):
        raise exceptions.InvalidSignature()
Exemplo n.º 15
0
    def test_create_new_block(self, b):
        new_block = b.create_block([])
        block_hash = hash_data(b.serialize(new_block['block']))

        assert new_block['block']['voters'] == [b.me]
        assert new_block['block']['node_pubkey'] == b.me
        assert PublicKey(b.me).verify(b.serialize(new_block['block']), new_block['signature']) == True
        assert new_block['id'] == block_hash
        assert new_block['votes'] == []
Exemplo n.º 16
0
    def test_create_new_block(self, b):
        new_block = b.create_block([])
        block_hash = crypto.hash_data(util.serialize(new_block['block']))

        assert new_block['block']['voters'] == [b.me]
        assert new_block['block']['node_pubkey'] == b.me
        assert crypto.VerifyingKey(b.me).verify(util.serialize(new_block['block']), new_block['signature']) is True
        assert new_block['id'] == block_hash
        assert new_block['votes'] == []
Exemplo n.º 17
0
 def test_transaction_hash(self, b):
     payload = {'cats': 'are awesome'}
     tx = b.create_transaction('a', 'b', 'c', 'd', payload)
     tx_calculated = {
         'current_owner': 'a',
         'new_owner': 'b',
         'input': 'c',
         'operation': 'd',
         'timestamp': tx['transaction']['timestamp'],
         'data': {
             'hash': hash_data(b.serialize(payload)),
             'payload': payload
         }
     }
     assert tx['transaction']['data'] == tx_calculated['data']
Exemplo n.º 18
0
 def test_transaction_hash(self, b):
     payload = {'cats': 'are awesome'}
     tx = b.create_transaction('a', 'b', 'c', 'd', payload)
     tx_calculated = {
         'current_owner': 'a',
         'new_owner': 'b',
         'input': 'c',
         'operation': 'd',
         'timestamp': tx['transaction']['timestamp'],
         'data': {
             'hash': hash_data(b.serialize(payload)),
             'payload': payload
         }
     }
     assert tx['transaction']['data'] == tx_calculated['data']
Exemplo n.º 19
0
def get_hash_data(transaction):
    """ Get the hashed data that (should) correspond to the `transaction['id']`

    Args:
        transaction (dict): the transaction to be hashed

    Returns:
        str: the hash of the transaction
    """
    tx = copy.deepcopy(transaction)
    if 'transaction' in tx:
        tx = tx['transaction']

    # remove the fulfillment messages (signatures)
    for fulfillment in tx['fulfillments']:
        fulfillment['fulfillment'] = None

    return crypto.hash_data(serialize(tx))
Exemplo n.º 20
0
def get_hash_data(transaction):
    """ Get the hashed data that (should) correspond to the `transaction['id']`

    Args:
        transaction (dict): the transaction to be hashed

    Returns:
        str: the hash of the transaction
    """
    tx = copy.deepcopy(transaction)
    if 'transaction' in tx:
        tx = tx['transaction']

    # remove the fulfillment messages (signatures)
    for fulfillment in tx['fulfillments']:
        fulfillment['fulfillment'] = None

    return crypto.hash_data(serialize(tx))
Exemplo n.º 21
0
    def validate_block(bigchain, block):
        """Validate a block.

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

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

        Raises:
            InvalidHash: if the hash of the block is wrong.
        """

        # Check if current hash is correct
        calculated_hash = crypto.hash_data(util.serialize(block['block']))
        if calculated_hash != block['id']:
            raise exceptions.InvalidHash()

        return block
Exemplo n.º 22
0
    def validate_block(bigchain, block):
        """Validate a block.

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

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

        Raises:
            InvalidHash: if the hash of the block is wrong.
        """

        # Check if current hash is correct
        calculated_hash = crypto.hash_data(util.serialize(block['block']))
        if calculated_hash != block['id']:
            raise exceptions.InvalidHash()

        return block
Exemplo n.º 23
0
    def create_block(self, validated_transactions):
        """Creates a block given a list of `validated_transactions`.

        Note that this method does not validate the transactions. Transactions should be validated before
        calling create_block.

        Args:
            validated_transactions (list): list of validated transactions.

        Returns:
            dict: created block.
        """

        # Prevent the creation of empty blocks
        if len(validated_transactions) == 0:
            raise exceptions.OperationError(
                'Empty block creation is not allowed')

        # Create the new block
        block = {
            'timestamp': util.timestamp(),
            'transactions': validated_transactions,
            'node_pubkey': self.me,
            'voters': self.nodes_except_me + [self.me]
        }

        # Calculate the hash of the new block
        block_data = util.serialize(block)
        block_hash = crypto.hash_data(block_data)
        block_signature = crypto.SigningKey(self.me_private).sign(block_data)

        block = {
            'id': block_hash,
            'block': block,
            'signature': block_signature,
            'votes': []
        }

        return block
Exemplo n.º 24
0
    def create_block(self, validated_transactions):
        """Creates a block given a list of `validated_transactions`.

        Note that this method does not validate the transactions. Transactions should be validated before
        calling create_block.

        Args:
            validated_transactions (list): list of validated transactions.

        Returns:
            dict: created block.
        """

        # Prevent the creation of empty blocks
        if len(validated_transactions) == 0:
            raise exceptions.OperationError('Empty block creation is not allowed')

        # Create the new block
        block = {
            'timestamp': util.timestamp(),
            'transactions': validated_transactions,
            'node_pubkey': self.me,
            'voters': self.nodes_except_me + [self.me]
        }

        # Calculate the hash of the new block
        block_data = util.serialize(block)
        block_hash = crypto.hash_data(block_data)
        block_signature = crypto.SigningKey(self.me_private).sign(block_data)

        block = {
            'id': block_hash,
            'block': block,
            'signature': block_signature,
            'votes': []
        }

        return block
Exemplo n.º 25
0
    def validate_block(self, block):
        """Validate a block.

        Args:
            block (dict): block to validate.

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

        # 1. Check if current hash is correct
        calculated_hash = crypto.hash_data(util.serialize(block['block']))
        if calculated_hash != block['id']:
            raise exceptions.InvalidHash()

        # 2. Validate all transactions in the block
        for transaction in block['block']['transactions']:
            if not self.is_valid_transaction(transaction):
                # this will raise the exception
                self.validate_transaction(transaction)

        return block
Exemplo n.º 26
0
def create_tx(current_owners, new_owners, inputs, operation, payload=None):
    """Create a new transaction

    A transaction in the bigchain is a transfer of a digital asset between two entities represented
    by public keys.

    Currently the bigchain supports two types of operations:

        `CREATE` - Only federation nodes are allowed to use this operation. In a create operation
        a federation node creates a digital asset in the bigchain and assigns that asset to a public
        key. The owner of the private key can then decided to transfer this digital asset by using the
        `transaction id` of the transaction as an input in a `TRANSFER` transaction.

        `TRANSFER` - A transfer operation allows for a transfer of the digital assets between entities.

    Args:
        current_owners (list): base58 encoded public key of the current owners of the asset.
        new_owners (list): base58 encoded public key of the new owners of the digital asset.
        inputs (list): id of the transaction to use as input.
        operation (str): Either `CREATE` or `TRANSFER` operation.
        payload (Optional[dict]): dictionary with information about asset.

    Returns:
        dict: unsigned transaction.


    Raises:
        TypeError: if the optional ``payload`` argument is not a ``dict``.

    Reference:
        {
            "id": "<sha3 hash>",
            "version": "transaction version number",
            "transaction": {
                "fulfillments": [
                        {
                            "current_owners": ["list of <pub-keys>"],
                            "input": {
                                "txid": "<sha3 hash>",
                                "cid": "condition index"
                            },
                            "fulfillment": "fulfillement of condition cid",
                            "fid": "fulfillment index"
                        }
                    ],
                "conditions": [
                        {
                            "new_owners": ["list of <pub-keys>"],
                            "condition": "condition to be met",
                            "cid": "condition index (1-to-1 mapping with fid)"
                        }
                    ],
                "operation": "<string>",
                "timestamp": "<timestamp from client>",
                "data": {
                    "hash": "<SHA3-256 hash hexdigest of payload>",
                    "payload": {
                        "title": "The Winds of Plast",
                        "creator": "Johnathan Plunkett",
                        "IPFS_key": "QmfQ5QAjvg4GtA3wg3adpnDJug8ktA1BxurVqBD8rtgVjP"
                    }
                }
            },
        }
    """
    # validate arguments (owners and inputs should be lists or None)

    # The None case appears on fulfilling a hashlock
    if current_owners is None:
        current_owners = []
    if not isinstance(current_owners, list):
        current_owners = [current_owners]

    # The None case appears on assigning a hashlock
    if new_owners is None:
        new_owners = []
    if not isinstance(new_owners, list):
        new_owners = [new_owners]

    if not isinstance(inputs, list):
        inputs = [inputs]

    # handle payload
    data = None
    if payload is not None:
        if isinstance(payload, dict):
            hash_payload = crypto.hash_data(serialize(payload))
            data = {'hash': hash_payload, 'payload': payload}
        else:
            raise TypeError('`payload` must be an dict instance')

    # handle inputs
    fulfillments = []

    # transfer
    if inputs:
        for fid, tx_input in enumerate(inputs):
            fulfillments.append({
                'current_owners': current_owners,
                'input': tx_input,
                'fulfillment': None,
                'fid': fid
            })
    # create
    else:
        fulfillments.append({
            'current_owners': current_owners,
            'input': None,
            'fulfillment': None,
            'fid': 0
        })

    # handle outputs
    conditions = []
    for fulfillment in fulfillments:

        # threshold condition
        if len(new_owners) > 1:
            condition = cc.ThresholdSha256Fulfillment(
                threshold=len(new_owners))
            for new_owner in new_owners:
                condition.add_subfulfillment(
                    cc.Ed25519Fulfillment(public_key=new_owner))

        # simple signature condition
        elif len(new_owners) == 1:
            condition = cc.Ed25519Fulfillment(public_key=new_owners[0])

        # to be added later (hashlock conditions)
        else:
            condition = None

        if condition:
            conditions.append({
                'new_owners': new_owners,
                'condition': {
                    'details': rapidjson.loads(condition.serialize_json()),
                    'uri': condition.condition_uri
                },
                'cid': fulfillment['fid']
            })

    tx = {
        'fulfillments': fulfillments,
        'conditions': conditions,
        'operation': operation,
        'timestamp': timestamp(),
        'data': data
    }

    # serialize and convert to bytes
    tx_hash = get_hash_data(tx)

    # create the transaction
    transaction = {'id': tx_hash, 'version': 1, 'transaction': tx}

    return transaction
Exemplo n.º 27
0
def create_tx(current_owners, new_owners, inputs, operation, payload=None):
    """Create a new transaction

    A transaction in the bigchain is a transfer of a digital asset between two entities represented
    by public keys.

    Currently the bigchain supports two types of operations:

        `CREATE` - Only federation nodes are allowed to use this operation. In a create operation
        a federation node creates a digital asset in the bigchain and assigns that asset to a public
        key. The owner of the private key can then decided to transfer this digital asset by using the
        `transaction id` of the transaction as an input in a `TRANSFER` transaction.

        `TRANSFER` - A transfer operation allows for a transfer of the digital assets between entities.

    Args:
        current_owners (list): base58 encoded public key of the current owners of the asset.
        new_owners (list): base58 encoded public key of the new owners of the digital asset.
        inputs (list): id of the transaction to use as input.
        operation (str): Either `CREATE` or `TRANSFER` operation.
        payload (Optional[dict]): dictionary with information about asset.

    Returns:
        dict: unsigned transaction.


    Raises:
        TypeError: if the optional ``payload`` argument is not a ``dict``.

    Reference:
        {
            "id": "<sha3 hash>",
            "version": "transaction version number",
            "transaction": {
                "fulfillments": [
                        {
                            "current_owners": ["list of <pub-keys>"],
                            "input": {
                                "txid": "<sha3 hash>",
                                "cid": "condition index"
                            },
                            "fulfillment": "fulfillement of condition cid",
                            "fid": "fulfillment index"
                        }
                    ],
                "conditions": [
                        {
                            "new_owners": ["list of <pub-keys>"],
                            "condition": "condition to be met",
                            "cid": "condition index (1-to-1 mapping with fid)"
                        }
                    ],
                "operation": "<string>",
                "timestamp": "<timestamp from client>",
                "data": {
                    "hash": "<SHA3-256 hash hexdigest of payload>",
                    "payload": {
                        "title": "The Winds of Plast",
                        "creator": "Johnathan Plunkett",
                        "IPFS_key": "QmfQ5QAjvg4GtA3wg3adpnDJug8ktA1BxurVqBD8rtgVjP"
                    }
                }
            },
        }
    """
    # validate arguments (owners and inputs should be lists or None)

    # The None case appears on fulfilling a hashlock
    if current_owners is None:
        current_owners = []
    if not isinstance(current_owners, list):
        current_owners = [current_owners]

    # The None case appears on assigning a hashlock
    if new_owners is None:
        new_owners = []
    if not isinstance(new_owners, list):
        new_owners = [new_owners]

    if not isinstance(inputs, list):
        inputs = [inputs]

    # handle payload
    data = None
    if payload is not None:
        if isinstance(payload, dict):
            hash_payload = crypto.hash_data(serialize(payload))
            data = {
                'hash': hash_payload,
                'payload': payload
            }
        else:
            raise TypeError('`payload` must be an dict instance')

    # handle inputs
    fulfillments = []

    # transfer
    if inputs:
        for fid, tx_input in enumerate(inputs):
            fulfillments.append({
                'current_owners': current_owners,
                'input': tx_input,
                'fulfillment': None,
                'fid': fid
            })
    # create
    else:
        fulfillments.append({
            'current_owners': current_owners,
            'input': None,
            'fulfillment': None,
            'fid': 0
        })

    # handle outputs
    conditions = []
    for fulfillment in fulfillments:

        # threshold condition
        if len(new_owners) > 1:
            condition = cc.ThresholdSha256Fulfillment(threshold=len(new_owners))
            for new_owner in new_owners:
                condition.add_subfulfillment(cc.Ed25519Fulfillment(public_key=new_owner))

        # simple signature condition
        elif len(new_owners) == 1:
            condition = cc.Ed25519Fulfillment(public_key=new_owners[0])

        # to be added later (hashlock conditions)
        else:
            condition = None

        if condition:
            conditions.append({
                'new_owners': new_owners,
                'condition': {
                    'details': json.loads(condition.serialize_json()),
                    'uri': condition.condition.serialize_uri()
                },
                'cid': fulfillment['fid']
            })

    tx = {
        'fulfillments': fulfillments,
        'conditions': conditions,
        'operation': operation,
        'timestamp': timestamp(),
        'data': data
    }

    # serialize and convert to bytes
    tx_hash = get_hash_data(tx)

    # create the transaction
    transaction = {
        'id': tx_hash,
        'version': 1,
        'transaction': tx
    }

    return transaction
Exemplo n.º 28
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
Exemplo n.º 29
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
Exemplo n.º 30
0
def create_tx(current_owner, new_owner, tx_input, operation, payload=None):
    """Create a new transaction

    A transaction in the bigchain is a transfer of a digital asset between two entities represented
    by public keys.

    Currently the bigchain supports two types of operations:

        `CREATE` - Only federation nodes are allowed to use this operation. In a create operation
        a federation node creates a digital asset in the bigchain and assigns that asset to a public
        key. The owner of the private key can then decided to transfer this digital asset by using the
        `transaction id` of the transaction as an input in a `TRANSFER` transaction.

        `TRANSFER` - A transfer operation allows for a transfer of the digital assets between entities.

    Args:
        current_owner (str): base58 encoded public key of the current owner of the asset.
        new_owner (str): base58 encoded public key of the new owner of the digital asset.
        tx_input (str): id of the transaction to use as input.
        operation (str): Either `CREATE` or `TRANSFER` operation.
        payload (Optional[dict]): dictionary with information about asset.

    Returns:
        dict: unsigned transaction.


    Raises:
        TypeError: if the optional ``payload`` argument is not a ``dict``.
    """

    data = None
    if payload is not None:
        if isinstance(payload, dict):
            hash_payload = crypto.hash_data(serialize(payload))
            data = {
                'hash': hash_payload,
                'payload': payload
            }
        else:
            raise TypeError('`payload` must be an dict instance')

    hash_payload = crypto.hash_data(serialize(payload))
    data = {
        'hash': hash_payload,
        'payload': payload
    }

    tx = {
        'current_owner': current_owner,
        'new_owner': new_owner,
        'input': tx_input,
        'operation': operation,
        'timestamp': timestamp(),
        'data': data
    }

    # serialize and convert to bytes
    tx_serialized = serialize(tx)
    tx_hash = crypto.hash_data(tx_serialized)

    # create the transaction
    transaction = {
        'id': tx_hash,
        'transaction': tx
    }

    return transaction