mirror of
https://github.com/bigchaindb/bigchaindb.git
synced 2024-10-13 13:34:05 +00:00
Pulled validate_transaction and validate_block out into a base ConsensusRules plugin class
This commit is contained in:
parent
8a9030e5c0
commit
ee4720d1a5
107
bigchaindb/consensus/base.py
Normal file
107
bigchaindb/consensus/base.py
Normal file
@ -0,0 +1,107 @@
|
||||
import bigchaindb.exceptions as exceptions
|
||||
from bigchaindb.crypto import hash_data
|
||||
|
||||
class ConsensusRules(object):
|
||||
"""Base consensus rules for Bigchain.
|
||||
|
||||
This class can be copied to write your own consensus rules!
|
||||
|
||||
Note: Consensus plugins will be executed in the order that they're listed in
|
||||
the bigchain config file.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def validate_transaction(cls, 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 = hash_data(bigchain.serialize(
|
||||
transaction['transaction']))
|
||||
if calculated_hash != transaction['id']:
|
||||
raise exceptions.InvalidHash()
|
||||
|
||||
# Check signature
|
||||
if not bigchain.verify_signature(transaction):
|
||||
raise exceptions.InvalidSignature()
|
||||
|
||||
return transaction
|
||||
|
||||
# TODO: check that the votings structure is correctly constructed
|
||||
@classmethod
|
||||
def validate_block(cls, 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 and 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 = hash_data(bigchain.serialize(block['block']))
|
||||
if calculated_hash != block['id']:
|
||||
raise exceptions.InvalidHash()
|
||||
|
||||
return block
|
@ -327,57 +327,16 @@ class Bigchain(object):
|
||||
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
|
||||
The transaction if the transaction is valid else it raises an
|
||||
exception describing the reason why the transaction is invalid.
|
||||
"""
|
||||
# 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 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_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 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']))
|
||||
|
||||
# Check hash of the transaction
|
||||
calculated_hash = hash_data(self.serialize(transaction['transaction']))
|
||||
if calculated_hash != transaction['id']:
|
||||
raise exceptions.InvalidHash()
|
||||
|
||||
# Check signature
|
||||
if not self.verify_signature(transaction):
|
||||
raise exceptions.InvalidSignature()
|
||||
for plugin in self.consensus_plugins:
|
||||
transaction = plugin.validate_transaction(self, transaction)
|
||||
|
||||
return transaction
|
||||
|
||||
|
||||
def is_valid_transaction(self, transaction):
|
||||
"""Check whether a transacion is valid or invalid.
|
||||
|
||||
@ -447,12 +406,13 @@ class Bigchain(object):
|
||||
|
||||
"""
|
||||
|
||||
# 1. Check if current hash is correct
|
||||
calculated_hash = hash_data(self.serialize(block['block']))
|
||||
if calculated_hash != block['id']:
|
||||
raise exceptions.InvalidHash()
|
||||
# First run all of the plugin block validation logic
|
||||
for plugin in self.consensus_plugins:
|
||||
transaction = plugin.validate_block(self, block)
|
||||
|
||||
# 2. Validate all transactions in the block
|
||||
|
||||
# Finally: Tentative assumption that every blockchain will want to
|
||||
# validate all transactions in each block
|
||||
for transaction in block['block']['transactions']:
|
||||
if not self.is_valid_transaction(transaction):
|
||||
# this will raise the exception
|
||||
|
Loading…
x
Reference in New Issue
Block a user