Merge branch 'master' into update-docs-server-requirements-txt

This commit is contained in:
kansi 2017-11-09 16:31:43 +05:30
commit 0092270964
28 changed files with 366 additions and 33 deletions

View File

@ -50,6 +50,11 @@ def drop_database(conn, dbname):
def create_bigchain_secondary_index(conn, dbname):
logger.info('Create `bigchain` secondary index.')
# secondary index on block id which should be unique
conn.conn[dbname]['bigchain'].create_index('id',
name='block_id',
unique=True)
# to order blocks by timestamp
conn.conn[dbname]['bigchain'].create_index([('block.timestamp',
ASCENDING)],

View File

@ -16,10 +16,17 @@ import logging
import bigchaindb
from bigchaindb.backend.connection import connect
from bigchaindb.common.exceptions import ValidationError
from bigchaindb.common.utils import validate_all_values_for_key
logger = logging.getLogger(__name__)
TABLES = ('bigchain', 'backlog', 'votes', 'assets')
VALID_LANGUAGES = ('danish', 'dutch', 'english', 'finnish', 'french', 'german',
'hungarian', 'italian', 'norwegian', 'portuguese', 'romanian',
'russian', 'spanish', 'swedish', 'turkish', 'none',
'da', 'nl', 'en', 'fi', 'fr', 'de', 'hu', 'it', 'nb', 'pt',
'ro', 'ru', 'es', 'sv', 'tr')
@singledispatch
@ -99,3 +106,44 @@ def init_database(connection=None, dbname=None):
create_database(connection, dbname)
create_tables(connection, dbname)
create_indexes(connection, dbname)
def validate_language_key(obj, key):
"""Validate all nested "language" key in `obj`.
Args:
obj (dict): dictionary whose "language" key is to be validated.
Returns:
None: validation successful
Raises:
ValidationError: will raise exception in case language is not valid.
"""
backend = bigchaindb.config['database']['backend']
if backend == 'mongodb':
data = obj.get(key, {})
if isinstance(data, dict):
validate_all_values_for_key(data, 'language', validate_language)
def validate_language(value):
"""Check if `value` is a valid language.
https://docs.mongodb.com/manual/reference/text-search-languages/
Args:
value (str): language to validated
Returns:
None: validation successful
Raises:
ValidationError: will raise exception in case language is not valid.
"""
if value not in VALID_LANGUAGES:
error_str = ('MongoDB does not support text search for the '
'language "{}". If you do not understand this error '
'message then please rename key/field "language" to '
'something else like "lang".').format(value)
raise ValidationError(error_str)

View File

@ -196,7 +196,7 @@ def run_start(args):
logger.info('RethinkDB started with PID %s' % proc.pid)
try:
if args.initialize_database:
if not args.skip_initialize_database:
logger.info('Initializing database')
_run_init()
except DatabaseAlreadyExists:
@ -302,10 +302,11 @@ def create_parser():
action='store_true',
help='Run RethinkDB on start')
start_parser.add_argument('--init',
dest='initialize_database',
start_parser.add_argument('--no-init',
dest='skip_initialize_database',
default=False,
action='store_true',
help='Force initialize database')
help='Skip database initialization')
# parser for configuring the number of shards
sharding_parser = subparsers.add_parser('set-shards',

View File

@ -1,7 +1,10 @@
import time
import re
import rapidjson
import bigchaindb
from bigchaindb.common.exceptions import ValidationError
def gen_timestamp():
"""The Unix time, rounded to the nearest second.
@ -46,3 +49,90 @@ def deserialize(data):
string.
"""
return rapidjson.loads(data)
def validate_txn_obj(obj_name, obj, key, validation_fun):
"""Validate value of `key` in `obj` using `validation_fun`.
Args:
obj_name (str): name for `obj` being validated.
obj (dict): dictionary object.
key (str): key to be validated in `obj`.
validation_fun (function): function used to validate the value
of `key`.
Returns:
None: indicates validation successful
Raises:
ValidationError: `validation_fun` will raise exception on failure
"""
backend = bigchaindb.config['database']['backend']
if backend == 'mongodb':
data = obj.get(key, {})
if isinstance(data, dict):
validate_all_keys(obj_name, data, validation_fun)
def validate_all_keys(obj_name, obj, validation_fun):
"""Validate all (nested) keys in `obj` by using `validation_fun`.
Args:
obj_name (str): name for `obj` being validated.
obj (dict): dictionary object.
validation_fun (function): function used to validate the value
of `key`.
Returns:
None: indicates validation successful
Raises:
ValidationError: `validation_fun` will raise this error on failure
"""
for key, value in obj.items():
validation_fun(obj_name, key)
if isinstance(value, dict):
validate_all_keys(obj_name, value, validation_fun)
def validate_all_values_for_key(obj, key, validation_fun):
"""Validate value for all (nested) occurrence of `key` in `obj`
using `validation_fun`.
Args:
obj (dict): dictionary object.
key (str): key whose value is to be validated.
validation_fun (function): function used to validate the value
of `key`.
Raises:
ValidationError: `validation_fun` will raise this error on failure
"""
for vkey, value in obj.items():
if vkey == key:
validation_fun(value)
elif isinstance(value, dict):
validate_all_values_for_key(value, key, validation_fun)
def validate_key(obj_name, key):
"""Check if `key` contains ".", "$" or null characters.
https://docs.mongodb.com/manual/reference/limits/#Restrictions-on-Field-Names
Args:
obj_name (str): object name to use when raising exception
key (str): key to validated
Returns:
None: validation successful
Raises:
ValidationError: will raise exception in case of regex match.
"""
if re.search(r'^[$]|\.|\x00', key):
error_str = ('Invalid key name "{}" in {} object. The '
'key name cannot contain characters '
'".", "$" or null characters').format(key, obj_name)
raise ValidationError(error_str)

View File

@ -8,8 +8,10 @@ from bigchaindb.common.exceptions import (InvalidHash, InvalidSignature,
SybilError,
DuplicateTransaction)
from bigchaindb.common.transaction import Transaction
from bigchaindb.common.utils import gen_timestamp, serialize
from bigchaindb.common.utils import (gen_timestamp, serialize,
validate_txn_obj, validate_key)
from bigchaindb.common.schema import validate_transaction_schema
from bigchaindb.backend.schema import validate_language_key
class Transaction(Transaction):
@ -84,6 +86,9 @@ class Transaction(Transaction):
@classmethod
def from_dict(cls, tx_body):
validate_transaction_schema(tx_body)
validate_txn_obj('asset', tx_body['asset'], 'data', validate_key)
validate_txn_obj('metadata', tx_body, 'metadata', validate_key)
validate_language_key(tx_body['asset'], 'data')
return super().from_dict(tx_body)
@classmethod

View File

@ -25,7 +25,7 @@ services:
BIGCHAINDB_GRAPHITE_HOST: graphite
ports:
- "9984"
command: bigchaindb start --init
command: bigchaindb start
graphite:
image: hopsoft/graphite-statsd

View File

@ -45,4 +45,4 @@ services:
BIGCHAINDB_SERVER_BIND: 0.0.0.0:9984
ports:
- "9984"
command: bigchaindb start --init
command: bigchaindb start

View File

@ -30,4 +30,4 @@ services:
BIGCHAINDB_WSSERVER_HOST: 0.0.0.0
ports:
- "9984"
command: bigchaindb start --init
command: bigchaindb start

View File

@ -1,6 +1,8 @@
# BigchainDB and Byzantine Fault Tolerance
While BigchainDB is not currently [Byzantine fault tolerant (BFT)](https://en.wikipedia.org/wiki/Byzantine_fault_tolerance), we plan to offer it as an option.
We anticipate that turning it on will cause a severe dropoff in performance. See [Issue #293](https://github.com/bigchaindb/bigchaindb/issues/293).
While BigchainDB is not currently [Byzantine fault tolerant (BFT)](https://en.wikipedia.org/wiki/Byzantine_fault_tolerance), we plan to offer it as an option.
Update Nov 2017: we're actively working on this, the next release or two will likely have support. More details to come in blog form and github issues
Related issue: [Issue #293](https://github.com/bigchaindb/bigchaindb/issues/293). We anticipate that turning on BFT will cause a dropoff in performance (for a gain in security).
In the meantime, there are practical things that one can do to increase security (e.g. firewalls, key management, and access controls).

View File

@ -34,7 +34,9 @@ from recommonmark.parser import CommonMarkParser
# ones.
import sphinx_rtd_theme
extensions = []
extensions = [
'sphinx.ext.autosectionlabel',
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

View File

@ -88,5 +88,6 @@ More About BigchainDB
assets
smart-contracts
transaction-concepts
permissions
timestamps
Data Models <https://docs.bigchaindb.com/projects/server/en/latest/data-models/index.html>

View File

@ -0,0 +1,74 @@
Permissions in BigchainDB
-------------------------
BigchainDB lets users control what other users can do, to some extent. That ability resembles "permissions" in the \*nix world, "privileges" in the SQL world, and "access control" in the security world.
Permission to Spend/Transfer an Output
======================================
In BigchainDB, every output has an associated condition (crypto-condition).
To spend/transfer an unspent output, a user (or group of users) must fulfill the condition. Another way to say that is that only certain users have permission to spend the output. The simplest condition is of the form, "Only someone with the private key corresponding to this public key can spend this output." Much more elaborate conditions are possible, e.g. "To spend this output, …"
- "…anyone in the Accounting Group can sign."
- "…three of these four people must sign."
- "…either Bob must sign, or both Tom and Sylvia must sign."
For details, see `the documentation about conditions in BigchainDB <https://docs.bigchaindb.com/projects/server/en/latest/data-models/conditions.html>`_.
Once an output has been spent, it can't be spent again: *nobody* has permission to do that. That is, BigchainDB doesn't permit anyone to "double spend" an output.
Write Permissions
=================
When someone builds a TRANSFER transaction, they can put an arbitrary JSON object in the ``metadata`` field (within reason; real BigchainDB networks put a limit on the size of transactions). That is, they can write just about anything they want in a TRANSFER transaction.
Does that mean there are no "write permissions" in BigchainDB? Not at all!
A TRANSFER transaction will only be valid (allowed) if its inputs fulfill some previous outputs. The conditions on those outputs will control who can build valid TRANSFER transactions. In other words, one can interpret the condition on an output as giving "write permissions" to certain users to write something into the history of the associated asset.
As a concrete example, you could use BigchainDB to write a public journal where only you have write permissions. Here's how: First you'd build a CREATE transaction with the ``asset.data`` being something like ``{"title": "The Journal of John Doe"}``, with one output. That output would have an amount 1 and a condition that only you (who has your private key) can spend that output.
Each time you want to append something to your journal, you'd build a new TRANSFER transaction with your latest entry in the ``metadata`` field, e.g.
.. code-block:: json
{"timestamp": "1508319582",
"entry": "I visited Marmot Lake with Jane."}
The TRANSFER transaction would have one output. That output would have an amount 1 and a condition that only you (who has your private key) can spend that output. And so on. Only you would be able to append to the history of that asset (your journal).
The same technique could be used for scientific notebooks, supply-chain records, government meeting minutes, and so on.
You could do more elaborate things too. As one example, each time someone writes a TRANSFER transaction, they give *someone else* permission to spend it, setting up a sort of writers-relay or chain letter.
.. note::
Anyone can write any JSON (again, within reason) in the ``asset.data`` field of a CREATE transaction. They don't need permission.
Read Permissions
================
All the data stored in a BigchainDB network can be read by anyone with access to that network. One *can* store encrypted data, but if the decryption key ever leaks out, then the encrypted data can be read, decrypted, and leak out too. (Deleting the encrypted data is :doc:`not an option <immutable>`.)
The permission to read some specific information (e.g. a music file) can be thought of as an *asset*. (In many countries, that permission or "right" is a kind of intellectual property.)
BigchainDB can be used to register that asset and transfer it from owner to owner.
Today, BigchainDB does not have a way to restrict read access of data stored in a BigchainDB network, but many third-party services do offer that (e.g. Google Docs, Dropbox).
In principle, a third party service could ask a BigchainDB network to determine if a particular user has permission to read some particular data. Indeed they could use BigchainDB to keep track of *all* the rights a user has for some data (not just the right to read it).
That third party could also use BigchainDB to store audit logs, i.e. records of every read, write or other operation on stored data.
BigchainDB can be used in other ways to help parties exchange private data:
- It can be used to publicly disclose the *availability* of some private data (stored elsewhere). For example, there might be a description of the data and a price.
- It can be used to record the TLS handshakes which two parties sent to each other to establish an encrypted and authenticated TLS connection, which they could use to exchange private data with each other. (The stored handshake information wouldn't be enough, by itself, to decrypt the data.) It would be a "proof of TLS handshake."
- See the BigchainDB `Privacy Protocols repository <https://github.com/bigchaindb/privacy-protocols>`_ for more techniques.
Role-Based Access Control (RBAC)
================================
In September 2017, we published a `blog post about how one can define an RBAC sub-system on top of BigchainDB <https://blog.bigchaindb.com/role-based-access-control-for-bigchaindb-assets-b7cada491997>`_.
At the time of writing (October 2017), doing so required the use of a plugin, so it's not possible using standard BigchainDB (which is what's available on `IPDB <https://ipdb.io/>`_). That may change in the future.
If you're interested, `contact BigchainDB <https://www.bigchaindb.com/contact/>`_.

View File

@ -33,7 +33,7 @@ API Server bind? (default `localhost:9984`): 0.0.0.0:9984
Finally, run BigchainDB Server by doing:
```text
bigchaindb start --init
bigchaindb start
```
BigchainDB Server should now be running on the Azure virtual machine.

View File

@ -2,6 +2,8 @@
To avoid redundant data in transactions, the asset model is different for `CREATE` and `TRANSFER` transactions.
## In CREATE Transactions
In a `CREATE` transaction, the `"asset"` must contain exactly one key-value pair. The key must be `"data"` and the value can be any valid JSON document, or `null`. For example:
```json
{
@ -12,6 +14,15 @@ In a `CREATE` transaction, the `"asset"` must contain exactly one key-value pair
}
```
When using MongoDB for storage, certain restriction apply to all (including nested) keys of the `"data"` JSON document:
* Keys (i.e. key names, not values) must **not** begin with the `$` character.
* Keys must not contain `.` or the null character (Unicode code point 0000).
* The key `"language"` (at any level in the hierarchy) is a special key and used for specifying text search language. Its value must be one of the allowed values; see the valid [Text Search Languages](https://docs.mongodb.com/manual/reference/text-search-languages/) in the MongoDB Docs. In BigchainDB, only the languages supported by _MongoDB community edition_ are allowed.
## In TRANSFER Transactions
In a `TRANSFER` transaction, the `"asset"` must contain exactly one key-value pair. They key must be `"id"` and the value must contain a transaction ID (i.e. a SHA3-256 hash: the ID of the `CREATE` transaction which created the asset, which also serves as the asset ID). For example:
```json
{

View File

@ -46,6 +46,10 @@ Here's some explanation of the contents:
- **metadata**: User-provided transaction metadata.
It can be any valid JSON document, or ``null``.
**NOTE:** When using MongoDB for storage, certain restriction apply
to all (including nested) keys of the ``"data"`` JSON document:
1) keys (i.e. key names, not values) must **not** begin with the ``$`` character, and
2) keys must not contain ``.`` or the null character (Unicode code point 0000).
**How the transaction ID is computed.**
1) Build a Python dictionary containing ``version``, ``inputs``, ``outputs``, ``operation``, ``asset``, ``metadata`` and their values,

View File

@ -27,7 +27,7 @@ waiting for connections on port 27017
To run BigchainDB Server, do:
```text
$ bigchaindb start --init
$ bigchaindb start
```
You can [run all the unit tests](running-all-tests.html) to test your installation.
@ -55,7 +55,7 @@ You can verify that RethinkDB is running by opening the RethinkDB web interface
To run BigchainDB Server, do:
```text
$ bigchaindb start --init
$ bigchaindb start
```
You can [run all the unit tests](running-all-tests.html) to test your installation.

View File

@ -54,7 +54,7 @@ $ bigchaindb -y configure mongodb
I. Run BigchainDB Server:
```text
$ bigchaindb start --init
$ bigchaindb start
```
J. Verify BigchainDB Server setup by visiting the BigchainDB Root URL in your browser:

View File

@ -61,7 +61,7 @@ If you want to force-drop the database (i.e. skipping the yes/no prompt), then u
## bigchaindb start
Start BigchainDB assuming that the database has already been initialized using `bigchaindb init`. If that is not the case then passing the flag `--init` will initialize the database and start BigchainDB.
Start BigchainDB. It always begins by trying a `bigchaindb init` first. See the note in the documentation for `bigchaindb init`. The database initialization step is optional and can be skipped by passing the `--no-init` flag i.e. `bigchaindb start --no-init`.
You can also use the `--dev-start-rethinkdb` command line option to automatically start rethinkdb with bigchaindb if rethinkdb is not already running,
e.g. `bigchaindb --dev-start-rethinkdb start`. Note that this will also shutdown rethinkdb when the bigchaindb process stops.
The option `--dev-allow-temp-keypair` will generate a keypair on the fly if no keypair is found, this is useful when you want to run a temporary instance of BigchainDB in a Docker container, for example.

View File

@ -105,17 +105,17 @@ $ docker-compose build
First, start `RethinkDB` in the background:
```text
$ docker-compose up -d rdb
$ docker-compose -f docker-compose.rdb.yml up -d rdb
```
then run the tests using:
```text
$ docker-compose run --rm bdb-rdb py.test -v
$ docker-compose -f docker-compose.rdb.yml run --rm bdb-rdb py.test -v
```
to rebuild all the images (usually you only need to rebuild the `bdb` and
`bdb-rdb` images).
`bdb-rdb` images). If that fails, then do `make clean-pyc` and try again.
## Automated Testing of All Pull Requests

View File

@ -299,12 +299,13 @@ def test_count_blocks(signed_create_tx):
from bigchaindb.models import Block
conn = connect()
assert query.count_blocks(conn) == 0
# create and insert some blocks
block = Block(transactions=[signed_create_tx])
conn.db.bigchain.insert_one(block.to_dict())
conn.db.bigchain.insert_one(block.to_dict())
assert query.count_blocks(conn) == 2
assert query.count_blocks(conn) == 1
def test_count_backlog(signed_create_tx):

View File

@ -22,8 +22,8 @@ def test_init_creates_db_tables_and_indexes():
'votes']
indexes = conn.conn[dbname]['bigchain'].index_information().keys()
assert sorted(indexes) == ['_id_', 'asset_id', 'block_timestamp', 'inputs',
'outputs', 'transaction_id']
assert sorted(indexes) == ['_id_', 'asset_id', 'block_id', 'block_timestamp',
'inputs', 'outputs', 'transaction_id']
indexes = conn.conn[dbname]['backlog'].index_information().keys()
assert sorted(indexes) == ['_id_', 'assignee__transaction_timestamp',
@ -86,8 +86,8 @@ def test_create_secondary_indexes():
# Bigchain table
indexes = conn.conn[dbname]['bigchain'].index_information().keys()
assert sorted(indexes) == ['_id_', 'asset_id', 'block_timestamp', 'inputs',
'outputs', 'transaction_id']
assert sorted(indexes) == ['_id_', 'asset_id', 'block_id', 'block_timestamp',
'inputs', 'outputs', 'transaction_id']
# Backlog table
indexes = conn.conn[dbname]['backlog'].index_information().keys()

View File

@ -49,7 +49,7 @@ def run_start_args(request):
config=param.get('config'),
start_rethinkdb=param.get('start_rethinkdb', False),
allow_temp_keypair=param.get('allow_temp_keypair', False),
initialize_database=param.get('initialize_database', True),
skip_initialize_database=param.get('skip_initialize_database', False),
)

View File

@ -14,7 +14,7 @@ def test_bigchain_run_start_with_rethinkdb(mock_start_rethinkdb,
from bigchaindb import config
from bigchaindb.commands.bigchaindb import run_start
args = Namespace(start_rethinkdb=True, allow_temp_keypair=False, config=None, yes=True,
initialize_database=True)
skip_initialize_database=False)
run_start(args)
mock_start_rethinkdb.assert_called_with()

View File

@ -40,7 +40,7 @@ def test_bigchain_run_start(mock_run_configure,
from bigchaindb import config
from bigchaindb.commands.bigchaindb import run_start
args = Namespace(start_rethinkdb=False, allow_temp_keypair=False, config=None, yes=True,
initialize_database=True)
skip_initialize_database=False)
run_start(args)
mocked_setup_logging.assert_called_once_with(user_log_config=config['log'])
@ -290,7 +290,7 @@ def test_allow_temp_keypair_generates_one_on_the_fly(
bigchaindb.config['keypair'] = {'private': None, 'public': None}
args = Namespace(allow_temp_keypair=True, start_rethinkdb=False, config=None, yes=True,
initialize_database=True)
skip_initialize_database=False)
run_start(args)
mocked_setup_logging.assert_called_once_with(
@ -317,7 +317,7 @@ def test_allow_temp_keypair_doesnt_override_if_keypair_found(mock_gen_keypair,
assert isinstance(original_private_key, str)
args = Namespace(allow_temp_keypair=True, start_rethinkdb=False, config=None, yes=True,
initialize_database=True)
skip_initialize_database=False)
run_start(args)
mocked_setup_logging.assert_called_once_with(

View File

@ -25,6 +25,15 @@ USER_PRIVATE_KEY = '8eJ8q9ZQpReWyQT5aFCiwtZ5wDZC4eDnCen88p3tQ6ie'
USER_PUBLIC_KEY = 'JEAkEJqLbbgDRAtMm8YAjGp759Aq2qTn9eaEHUj2XePE'
def pytest_runtest_setup(item):
if isinstance(item, item.Function):
if item.get_marker('skip_travis_rdb'):
if (os.getenv('TRAVIS_CI') == 'true' and
os.getenv('BIGCHAINDB_DATABASE_BACKEND') == 'rethinkdb'):
pytest.skip(
'Skip test during Travis CI build when using rethinkdb')
def pytest_addoption(parser):
from bigchaindb.backend.connection import BACKENDS

View File

@ -97,6 +97,7 @@ def process_vote(steps, result=None):
@pytest.mark.bdb
@pytest.mark.genesis
@pytest.mark.skip_travis_rdb
def test_elect_valid(federation_3):
[bx, (s0, s1, s2)] = federation_3
tx = input_single_create(bx[0])
@ -115,6 +116,7 @@ def test_elect_valid(federation_3):
@pytest.mark.bdb
@pytest.mark.skip_travis_rdb
@pytest.mark.genesis
def test_elect_invalid(federation_3):
[bx, (s0, s1, s2)] = federation_3
@ -135,6 +137,7 @@ def test_elect_invalid(federation_3):
@pytest.mark.bdb
@pytest.mark.genesis
@pytest.mark.skip_travis_rdb
def test_elect_sybill(federation_3):
[bx, (s0, s1, s2)] = federation_3
tx = input_single_create(bx[0])

View File

@ -5,6 +5,7 @@ import pytest
pytestmark = [pytest.mark.bdb, pytest.mark.usefixtures('processes')]
@pytest.mark.skip_travis_rdb
def test_double_create(b, user_pk):
from bigchaindb.models import Transaction
from bigchaindb.backend.query import count_blocks
@ -12,9 +13,9 @@ def test_double_create(b, user_pk):
metadata={'test': 'test'}).sign([b.me_private])
b.write_transaction(tx)
time.sleep(2)
time.sleep(5)
b.write_transaction(tx)
time.sleep(2)
time.sleep(5)
tx_returned = b.get_transaction(tx.id)
# test that the tx can be queried

View File

@ -47,6 +47,82 @@ def test_post_create_transaction_endpoint(b, client):
assert res.json['outputs'][0]['public_keys'][0] == user_pub
@pytest.mark.parametrize("nested", [False, True])
@pytest.mark.parametrize("language,expected_status_code", [
('danish', 202), ('dutch', 202), ('english', 202), ('finnish', 202),
('french', 202), ('german', 202), ('hungarian', 202), ('italian', 202),
('norwegian', 202), ('portuguese', 202), ('romanian', 202), ('none', 202),
('russian', 202), ('spanish', 202), ('swedish', 202), ('turkish', 202),
('da', 202), ('nl', 202), ('en', 202), ('fi', 202), ('fr', 202),
('de', 202), ('hu', 202), ('it', 202), ('nb', 202), ('pt', 202),
('ro', 202), ('ru', 202), ('es', 202), ('sv', 202), ('tr', 202),
('any', 400)
])
@pytest.mark.language
@pytest.mark.bdb
def test_post_create_transaction_with_language(b, client, nested, language,
expected_status_code):
from bigchaindb.models import Transaction
from bigchaindb.backend.mongodb.connection import MongoDBConnection
if isinstance(b.connection, MongoDBConnection):
user_priv, user_pub = crypto.generate_key_pair()
lang_obj = {'language': language}
if nested:
asset = {'root': lang_obj}
else:
asset = lang_obj
tx = Transaction.create([user_pub], [([user_pub], 1)],
asset=asset)
tx = tx.sign([user_priv])
res = client.post(TX_ENDPOINT, data=json.dumps(tx.to_dict()))
assert res.status_code == expected_status_code
if res.status_code == 400:
expected_error_message = (
'Invalid transaction (ValidationError): MongoDB does not support '
'text search for the language "{}". If you do not understand this '
'error message then please rename key/field "language" to something '
'else like "lang".').format(language)
assert res.json['message'] == expected_error_message
@pytest.mark.parametrize("field", ['asset', 'metadata'])
@pytest.mark.parametrize("value,err_key,expected_status_code", [
({'bad.key': 'v'}, 'bad.key', 400),
({'$bad.key': 'v'}, '$bad.key', 400),
({'$badkey': 'v'}, '$badkey', 400),
({'bad\x00key': 'v'}, 'bad\x00key', 400),
({'good_key': {'bad.key': 'v'}}, 'bad.key', 400),
({'good_key': 'v'}, 'good_key', 202)
])
@pytest.mark.bdb
def test_post_create_transaction_with_invalid_key(b, client, field, value,
err_key, expected_status_code):
from bigchaindb.models import Transaction
from bigchaindb.backend.mongodb.connection import MongoDBConnection
user_priv, user_pub = crypto.generate_key_pair()
if isinstance(b.connection, MongoDBConnection):
if field == 'asset':
tx = Transaction.create([user_pub], [([user_pub], 1)],
asset=value)
elif field == 'metadata':
tx = Transaction.create([user_pub], [([user_pub], 1)],
metadata=value)
tx = tx.sign([user_priv])
res = client.post(TX_ENDPOINT, data=json.dumps(tx.to_dict()))
assert res.status_code == expected_status_code
if res.status_code == 400:
expected_error_message = (
'Invalid transaction (ValidationError): Invalid key name "{}" '
'in {} object. The key name cannot contain characters '
'".", "$" or null characters').format(err_key, field)
assert res.json['message'] == expected_error_message
@patch('bigchaindb.web.views.base.logger')
def test_post_create_transaction_with_invalid_id(mock_logger, b, client):
from bigchaindb.common.exceptions import InvalidHash