mirror of
https://github.com/bigchaindb/bigchaindb.git
synced 2024-10-13 13:34:05 +00:00
Merge a44a5703d11bc04db4fb8d4021b4819692014003 into 955fd86a7f4323f61ae7c30ada53dccdbed575aa
This commit is contained in:
commit
ce3374caa5
@ -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.
|
||||
|
||||
|
||||
@ -199,7 +199,9 @@ def verify_signature(signed_transaction):
|
||||
if 'assignee' in data:
|
||||
data.pop('assignee')
|
||||
|
||||
signature = data.pop('signature')
|
||||
signature = data.pop('signature', None)
|
||||
if not signature: return False
|
||||
|
||||
public_key_base58 = signed_transaction['transaction']['current_owner']
|
||||
public_key = PublicKey(public_key_base58)
|
||||
return public_key.verify(serialize(data), signature)
|
||||
|
||||
@ -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__)
|
||||
@ -37,6 +37,7 @@ def get_transaction(tx_id):
|
||||
bigchain = current_app.config['bigchain']
|
||||
|
||||
tx = bigchain.get_transaction(tx_id)
|
||||
if not tx: flask.abort(404)
|
||||
return flask.jsonify(**tx)
|
||||
|
||||
|
||||
@ -47,12 +48,13 @@ def create_transaction():
|
||||
Return:
|
||||
A JSON string containing the data about the transaction.
|
||||
"""
|
||||
|
||||
bigchain = current_app.config['bigchain']
|
||||
|
||||
val = {}
|
||||
|
||||
# `force` will try to format the body of the POST request even if the `content-type` header is not
|
||||
# set to `application/json`
|
||||
# `force` will try to format the body of the POST request even if the
|
||||
# `content-type` header is not set to `application/json`
|
||||
tx = request.get_json(force=True)
|
||||
|
||||
if tx['transaction']['operation'] == 'CREATE':
|
||||
@ -67,3 +69,38 @@ 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)
|
||||
|
||||
# Always validate TRANSFER signatures; but only validate CREATE signatures
|
||||
# if present.
|
||||
validate_sig = True
|
||||
|
||||
# If a CREATE doesn't have the signature populated, then we treat it as
|
||||
# an input to the `create` function and transform it.
|
||||
if tx['transaction']['operation'] == 'CREATE' and 'signature' not in tx:
|
||||
validate_sig = False
|
||||
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 validate_sig:
|
||||
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='')
|
||||
|
||||
@ -57,3 +57,11 @@ def test_client_can_transfer_assets(mock_requests_post, client):
|
||||
|
||||
assert util.verify_signature(tx)
|
||||
|
||||
|
||||
def test_client_can_validate_transaction(mock_requests_post, client):
|
||||
from bigchaindb import util
|
||||
|
||||
assert client.validate({'valid': True,
|
||||
'error': ''}) == (True, '')
|
||||
assert client.validate({'valid': False,
|
||||
'error': 'Some Error'}) == (False, 'Some Error')
|
||||
|
||||
@ -6,8 +6,38 @@ from bigchaindb import util
|
||||
|
||||
|
||||
TX_ENDPOINT = '/api/v1/transactions/'
|
||||
VALIDATE_ENDPOINT = '/api/v1/transactions/validate/'
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def valid_create_transaction(user_public_key):
|
||||
return util.create_tx(
|
||||
current_owner=None,
|
||||
new_owner=user_public_key,
|
||||
tx_input=None,
|
||||
operation='CREATE',
|
||||
payload={
|
||||
'IPFS_key': 'QmfQ5QAjvg4GtA3wg3adpnDJug8ktA1BxurVqBD8rtgVjP',
|
||||
'creator': 'Johnathan Plunkett',
|
||||
'title': 'The Winds of Plast'})
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def valid_transfer_transaction(user_public_key, user_private_key):
|
||||
# Requires an tx_input param to create a *valid* transfer tx
|
||||
def make_tx(tx_input):
|
||||
return util.create_and_sign_tx(
|
||||
private_key=user_private_key,
|
||||
current_owner=user_public_key,
|
||||
new_owner=user_public_key,
|
||||
tx_input=tx_input, #Fill_me_in
|
||||
operation='TRANSFER',
|
||||
payload={
|
||||
'IPFS_key': 'QmfQ5QAjvg4GtA3wg3adpnDJug8ktA1BxurVqBD8rtgVjP',
|
||||
'creator': 'Johnathan Plunkett',
|
||||
'title': 'The Winds of Plast 2: The Plastening'})
|
||||
return make_tx
|
||||
|
||||
@pytest.mark.usefixtures('inputs')
|
||||
def test_get_transaction_endpoint(b, client, user_public_key):
|
||||
input_tx = b.get_owned_ids(user_public_key).pop()
|
||||
@ -40,3 +70,34 @@ def test_post_transfer_transaction_endpoint(b, client):
|
||||
assert res.json['transaction']['current_owner'] == from_keypair[1]
|
||||
assert res.json['transaction']['new_owner'] == to_keypair[1]
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('inputs')
|
||||
def test_post_validate_transaction_endpoint(b, client, user_public_key,
|
||||
valid_create_transaction,
|
||||
valid_transfer_transaction):
|
||||
# Validate valid CREATE tx
|
||||
res = client.post(VALIDATE_ENDPOINT,
|
||||
data=json.dumps(valid_create_transaction))
|
||||
assert res.json['valid'] == True
|
||||
assert res.json['error'] == ''
|
||||
|
||||
# Validate invalid CREATE tx
|
||||
valid_create_transaction.update({'signature': 'junk'})
|
||||
res = client.post(VALIDATE_ENDPOINT,
|
||||
data=json.dumps(valid_create_transaction))
|
||||
assert res.json['valid'] == False
|
||||
assert res.json['error'] == \
|
||||
"OperationError('Only federation nodes can use the operation `CREATE`',)"
|
||||
|
||||
# Validate valid TRANSFER tx
|
||||
res = client.post(VALIDATE_ENDPOINT, data=json.dumps(
|
||||
valid_transfer_transaction(b.get_owned_ids(user_public_key).pop())))
|
||||
assert res.json['valid'] == True
|
||||
assert res.json['error'] == ''
|
||||
|
||||
# Validate invalid TRANSFER tx
|
||||
res = client.post(VALIDATE_ENDPOINT, data=json.dumps(
|
||||
valid_transfer_transaction(None)))
|
||||
assert res.json['valid'] == False
|
||||
assert res.json['error'] == \
|
||||
"ValueError('Only `CREATE` transactions can have null inputs',)"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user