diff --git a/bigchaindb/consensus.py b/bigchaindb/consensus.py index ec5d6ee9..c44b98fd 100644 --- a/bigchaindb/consensus.py +++ b/bigchaindb/consensus.py @@ -1,3 +1,4 @@ +import copy from abc import ABCMeta, abstractmethod import bigchaindb.exceptions as exceptions @@ -119,9 +120,11 @@ class BaseConsensusRules(AbstractConsensusRules): # 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']['inputs']: + # 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') - if transaction['transaction']['current_owner'] not in ( + # TODO: fow now lets assume a CREATE transaction only has one current_owner + if transaction['transaction']['fulfillments'][0]['current_owners'][0] not in ( bigchain.federation_nodes + [bigchain.me]): raise exceptions.OperationError( 'Only federation nodes can use the operation `CREATE`') @@ -156,6 +159,11 @@ class BaseConsensusRules(AbstractConsensusRules): 'input `{}` was already spent'.format(inp)) # Check hash of the transaction + # remove the fulfillment messages (signatures) + transaction_data = copy.deepcopy(transaction) + for fulfillment in transaction_data['transaction']['fulfillments']: + fulfillment['fulfillment'] = None + calculated_hash = crypto.hash_data(util.serialize( transaction['transaction'])) if calculated_hash != transaction['id']: diff --git a/bigchaindb/core.py b/bigchaindb/core.py index 8a31c61a..2747c359 100644 --- a/bigchaindb/core.py +++ b/bigchaindb/core.py @@ -388,7 +388,7 @@ class Bigchain(object): raise GenesisBlockAlreadyExistsError('Cannot create the Genesis block') payload = {'message': 'Hello World from the BigchainDB'} - transaction = self.create_transaction(self.me, self.me, None, 'GENESIS', payload=payload) + transaction = self.create_transaction([self.me], [self.me], None, 'GENESIS', payload=payload) transaction_signed = self.sign_transaction(transaction, self.me_private) # create the block diff --git a/bigchaindb/util.py b/bigchaindb/util.py index 1f6fcefe..b377fe31 100644 --- a/bigchaindb/util.py +++ b/bigchaindb/util.py @@ -239,7 +239,7 @@ def sign_tx(transaction, private_key): for fulfillment in transaction['transaction']['fulfillments']: fulfillment_message = common_data.copy() - if transaction['transaction']['operation'] == 'CREATE': + if transaction['transaction']['operation'] in ['CREATE', 'GENESIS']: fulfillment_message.update({ 'input': None, 'condition': None @@ -279,6 +279,7 @@ def check_hash_and_signature(transaction): def verify_signature(signed_transaction): + # TODO: The name should change. This will be the validation of the fulfillments """Verify the signature of a transaction A valid transaction should have been signed `current_owner` corresponding private key. @@ -290,16 +291,38 @@ def verify_signature(signed_transaction): bool: True if the signature is correct, False otherwise. """ - data = signed_transaction.copy() + b = bigchaindb.Bigchain() - # if assignee field in the transaction, remove it - if 'assignee' in data: - data.pop('assignee') + common_data = { + 'operation': signed_transaction['transaction']['operation'], + 'timestamp': signed_transaction['transaction']['timestamp'], + 'data': signed_transaction['transaction']['data'], + 'version': signed_transaction['version'], + 'id': signed_transaction['id'] + } - signature = data.pop('signature') - public_key_base58 = signed_transaction['transaction']['current_owner'] - public_key = crypto.VerifyingKey(public_key_base58) - return public_key.verify(serialize(data), signature) + for fulfillment in signed_transaction['transaction']['fulfillments']: + fulfillment_message = common_data.copy() + fulfillment_message.update({ + 'input': fulfillment['input'], + 'condition': None, + }) + + # if not a `CREATE` transaction + if fulfillment['input']: + # get previous condition + previous_tx = b.get_transaction(fulfillment['input']['txid']) + conditions = sorted(previous_tx['transaction']['conditions'], key=lambda d: d['cid']) + fulfillment_message['condition'] = conditions[fulfillment['cid']] + + # verify the signature (for now lets assume there is only one owner) + vk = crypto.VerifyingKey(fulfillment['current_owners'][0]) + + is_valid = vk.verify(serialize(fulfillment_message), fulfillment['fulfillment']) + if not is_valid: + return False + + return True def transform_create(tx):