diff --git a/bigchaindb/client.py b/bigchaindb/client.py index f7b37ad4..5d512ecf 100644 --- a/bigchaindb/client.py +++ b/bigchaindb/client.py @@ -90,6 +90,24 @@ class Client: tx, private_key=self.private_key) return self._push(signed_tx) + def validate(self, tx): + """Validate a transaction object. + + If tx is a `CREATE` transaction, this method will return (True, '') even + without the federation signature as long as the transaction is otherwise + valid. + + Args: + tx (dict): the transaction object to be validated + + Return: + (bool, str): (True, '') if the tx is valid, else (False, errormsg) + """ + + res = requests.post(self.api_endpoint + '/transactions/validate/', + json=tx) + return (res.json()['valid'], res.json()['error']) + def _push(self, tx): """Submit a transaction to the Federation. diff --git a/bigchaindb/web/views.py b/bigchaindb/web/views.py index d09bbb79..589adfb7 100644 --- a/bigchaindb/web/views.py +++ b/bigchaindb/web/views.py @@ -7,7 +7,7 @@ For more information please refer to the documentation in Apiary: import flask from flask import current_app, request, Blueprint -from bigchaindb import util +from bigchaindb import util, exceptions basic_views = Blueprint('basic_views', __name__) @@ -68,3 +68,30 @@ def create_transaction(): return flask.jsonify(**tx) +@basic_views.route('/transactions/validate/', methods=['POST']) +def validate_transaction(): + """API endpoint to validate transactions without pushing them to the + Federation. No federation node signature is required for `CREATE` + transactions. + + Return: + A JSON object with the `valid` field populated with a boolean value + and the `error` field populated with an error message or an empty str + """ + bigchain = current_app.config['bigchain'] + + tx = request.get_json(force=True) + + if tx['transaction']['operation'] == 'CREATE': + tx = util.transform_create(tx) + + try: + bigchain.validate_transaction(tx) + except exceptions.InvalidSignature as e: + # We skipped signing CREATEs with the node's private key, so expect this + if tx['transaction']['operation'] != 'CREATE': + return flask.jsonify({'valid': False, 'error': repr(e)}) + except Exception as e: + return flask.jsonify({'valid': False, 'error': repr(e)}) + + return flask.jsonify({'valid': True, 'error': ''})