1
0
mirror of https://github.com/bigchaindb/bigchaindb.git synced 2024-06-23 17:56:41 +02:00

Merge remote-tracking branch 'origin/master' into inputs-outputs

This commit is contained in:
Scott Sadler 2016-12-22 13:45:02 +01:00
commit 04a78aacec
47 changed files with 1276 additions and 1326 deletions

View File

@ -28,7 +28,8 @@ To contribute code or documentation, you need a [GitHub account](https://github.
Familiarize yourself with how we do coding and documentation in the BigchainDB project, including:
* [our Python Style Guide](PYTHON_STYLE_GUIDE.md) (includes how we write tests)
* [our Python Style Guide](PYTHON_STYLE_GUIDE.md)
* [how we write and run tests](./tests/README.md)
* [our documentation strategy](./docs/README.md) (including in-code documentation)
* the GitHub Flow (workflow)
* [GitHub Guide: Understanding the GitHub Flow](https://guides.github.com/introduction/flow/)
@ -103,7 +104,7 @@ git checkout -b new-branch-name
With your new branch checked out locally, make changes or additions to the code or documentation. Remember to:
* follow [our Python Style Guide](PYTHON_STYLE_GUIDE.md).
* write and run tests for any new or changed code. There's a section in [our Python Style Guide](PYTHON_STYLE_GUIDE.md) about writing and running tests.
* write and run tests for any new or changed code. There's [a README file in the `tests/` folder](./tests/README.md) about how to do that.
* add or update documentation as necessary. Follow [our documentation strategy](./docs/README.md).
As you go, git add and git commit your changes or additions, e.g.
@ -119,7 +120,7 @@ git fetch upstream
git merge upstream/master
```
Once you're done commiting a set of new things and you're ready to submit them for inclusion, please be sure to run all the tests (as per the instructions at the end of our [Python Style Guide](PYTHON_STYLE_GUIDE.md)).
Once you're done commiting a set of new things and you're ready to submit them for inclusion, please be sure to run all the tests as per the instructions in [the README file in the `tests/` folder](./tests/README.md).
(When you submit your pull request [following the instructions below], we run all the tests automatically, so we will see if some are failing. If you don't know why some tests are failing, you can still submit your pull request, but be sure to note the failing tests and to ask for help with resolving them.)

View File

@ -47,8 +47,15 @@ It seems the preference is for slashes, but using parentheses is okay too. (Ther
If you need to `import` lots of names from a module or package, and they won't all fit in one line (without making the line too long), then use parentheses to spread the names across multiple lines, like so:
```python
from Tkinter import (
Tk, Frame, Button, Entry, Canvas, Text,
LEFT, DISABLED, NORMAL, RIDGE, END,
)
# Or
from Tkinter import (Tk, Frame, Button, Entry, Canvas, Text,
LEFT, DISABLED, NORMAL, RIDGE, END)
LEFT, DISABLED, NORMAL, RIDGE, END)
```
For the rationale, see [PEP 328](https://www.python.org/dev/peps/pep-0328/#rationale-for-parentheses).
@ -65,7 +72,7 @@ x = 'name: {}; score: {}'.format(name, n)
we use the `format()` version. The [official Python documentation says](https://docs.python.org/2/library/stdtypes.html#str.format), "This method of string formatting is the new standard in Python 3, and should be preferred to the % formatting described in String Formatting Operations in new code."
## Runnng the Flake8 Style Checker
## Running the Flake8 Style Checker
We use [Flake8](http://flake8.pycqa.org/en/latest/index.html) to check our Python code style. Once you have it installed, you can run it using:
```text
@ -75,48 +82,6 @@ flake8 --max-line-length 119 bigchaindb/
## Writing and Running (Python) Tests
We write unit and integration tests for our Python code using the [pytest](http://pytest.org/latest/) framework.
The content of this section was moved to [`bigchiandb/tests/README.md`](./tests/README.md).
All tests go in the `bigchaindb/tests` directory or one of its subdirectories. You can use the tests already in there as templates or examples.
You can run all tests using:
```text
py.test -v
```
or, if that doesn't work, try:
```text
python -m pytest -v
```
or:
```text
python setup.py test
```
If you want to learn about all the things you can do with pytest, see [the pytest documentation](http://pytest.org/latest/).
### Tox
We use [tox](https://tox.readthedocs.io/en/latest/) to run multiple suites of tests against multiple environments during automated testing. Generally you don't need to run this yourself, but it might be useful when troubleshooting a failing CI build.
To run all the tox tests, use:
```text
tox
```
or:
```text
python -m tox
```
To run only a few environments, use the `-e` flag:
```text
tox -e {ENVLIST}
```
where `{ENVLIST}` is one or more of the environments specified in the [tox.ini file](tox.ini).
### Automated testing of pull requests
We use [Travis CI](https://travis-ci.com/), so that whenever someone creates a new BigchainDB pull request on GitHub, Travis CI gets the new code and does _a bunch of stuff_. You can find out what we tell Travis CI to do in [the `.travis.yml` file](.travis.yml): it tells Travis CI how to install BigchainDB, how to run all the tests, and what to do "after success" (e.g. run `codecov`). (We use [Codecov](https://codecov.io/) to get a rough estimate of our test coverage.)
Note: We automatically run all tests on all pull requests (using Travis CI), so you should definitely run all tests locally before you submit a pull request. See the above-linked README file for instructions.

View File

@ -17,7 +17,7 @@ config = {
'database': {
'backend': os.environ.get('BIGCHAINDB_DATABASE_BACKEND', 'rethinkdb'),
'host': os.environ.get('BIGCHAINDB_DATABASE_HOST', 'localhost'),
'port': 28015,
'port': int(os.environ.get('BIGCHAINDB_DATABASE_PORT', 28015)),
'name': 'bigchain',
},
'keypair': {

View File

@ -1,8 +1,10 @@
"""Utils to initialize and drop the database."""
import time
import logging
from pymongo import ASCENDING, DESCENDING
from pymongo import errors
from bigchaindb import backend
from bigchaindb.common import exceptions
@ -24,6 +26,9 @@ def create_database(conn, dbname):
# TODO: read and write concerns can be declared here
conn.conn.get_database(dbname)
# initialize the replica set
initialize_replica_set(conn)
@register_schema(MongoDBConnection)
def create_tables(conn, dbname):
@ -49,9 +54,6 @@ def drop_database(conn, dbname):
def create_bigchain_secondary_index(conn, dbname):
logger.info('Create `bigchain` secondary index.')
# to select blocks by id
conn.conn[dbname]['bigchain'].create_index('id', name='block_id')
# to order blocks by timestamp
conn.conn[dbname]['bigchain'].create_index([('block.timestamp',
ASCENDING)],
@ -62,33 +64,15 @@ def create_bigchain_secondary_index(conn, dbname):
name='transaction_id',
unique=True)
# secondary index for payload data by UUID, this field is unique
conn.conn[dbname]['bigchain']\
.create_index('block.transactions.transaction.metadata.id',
name='metadata_id', unique=True)
# secondary index for asset uuid, this field is unique
conn.conn[dbname]['bigchain']\
.create_index('block.transactions.transaction.asset.id',
name='asset_id', unique=True)
# compound index on fulfillment and transactions id
conn.conn[dbname]['bigchain']\
.create_index([('block.transactions.transaction.fulfillments.txid',
ASCENDING),
('block.transactions.transaction.fulfillments.cid',
ASCENDING)],
name='tx_and_fulfillment')
def create_backlog_secondary_index(conn, dbname):
logger.info('Create `backlog` secondary index.')
# to order transactions by timestamp
conn.conn[dbname]['backlog'].create_index([('transaction.timestamp',
ASCENDING)],
name='transaction_timestamp')
# compound index to read transactions from the backlog per assignee
conn.conn[dbname]['backlog']\
.create_index([('assignee', ASCENDING),
@ -99,10 +83,6 @@ def create_backlog_secondary_index(conn, dbname):
def create_votes_secondary_index(conn, dbname):
logger.info('Create `votes` secondary index.')
# index on block id to quickly poll
conn.conn[dbname]['votes'].create_index('vote.voting_for_block',
name='voting_for')
# is the first index redundant then?
# compound index to order votes by block id and node
conn.conn[dbname]['votes'].create_index([('vote.voting_for_block',
@ -110,3 +90,66 @@ def create_votes_secondary_index(conn, dbname):
('node_pubkey',
ASCENDING)],
name='block_and_voter')
def initialize_replica_set(conn):
"""Initialize a replica set. If already initialized skip."""
replica_set_name = _get_replica_set_name(conn)
config = {'_id': replica_set_name,
'members': [{'_id': 0, 'host': 'localhost:27017'}]}
try:
conn.conn.admin.command('replSetInitiate', config)
except errors.OperationFailure as exc_info:
if exc_info.details['codeName'] == 'AlreadyInitialized':
logger.info('Replica set already initialized')
return
raise
else:
_wait_for_replica_set_initialization(conn)
logger.info('Initialized replica set')
def _get_replica_set_name(conn):
"""Checks if the replSet option was enabled either through the command
line option or config file.
Note:
The setting we are looking for will have a different name depending
if it was set by the config file (`replSetName`) or by command
line arguments (`replSet`).
Returns:
The replica set name if enabled.
Raise:
:exc:`~ConfigurationError`: If mongod was not started with the
replSet option.
"""
options = conn.conn.admin.command('getCmdLineOpts')
try:
repl_opts = options['parsed']['replication']
return repl_opts.get('replSetName', None) or repl_opts['replSet']
except KeyError:
raise exceptions.ConfigurationError('mongod was not started with'
' the replSet option.')
def _wait_for_replica_set_initialization(conn):
"""Wait for a replica set to finish initialization.
If a replica set is being initialized for the first time it takes some
time. Nodes need to discover each other and an election needs to take
place. During this time the database is not writable so we need to wait
before continuing with the rest of the initialization
"""
# I did not find a better way to do this for now.
# To check if the database is ready we will poll the mongodb logs until
# we find the line that says the database is ready
logger.info('Waiting for mongodb replica set initialization')
while True:
logs = conn.conn.admin.command('getLog', 'rs')['log']
if any('database writes are now permitted' in line for line in logs):
return
time.sleep(0.1)

View File

@ -111,12 +111,12 @@ def get_blocks_status_from_transaction(connection, transaction_id):
def get_txids_by_asset_id(connection, asset_id):
"""Retrieves transactions ids related to a particular asset.
A digital asset in bigchaindb is identified by an uuid. This allows us
to query all the transactions related to a particular digital asset,
knowing the id.
A digital asset in bigchaindb is identified by its ``CREATE``
transaction's ID. Knowing this ID allows us to query all the
transactions related to a particular digital asset.
Args:
asset_id (str): the id for this particular metadata.
asset_id (str): the ID of the asset.
Returns:
A list of transactions ids related to the asset. If no transaction

View File

@ -1,3 +1,4 @@
from itertools import chain
from time import time
import rethinkdb as r
@ -75,23 +76,32 @@ def get_blocks_status_from_transaction(connection, transaction_id):
def get_txids_by_asset_id(connection, asset_id):
# here we only want to return the transaction ids since later on when
# we are going to retrieve the transaction with status validation
return connection.run(
# First find the asset's CREATE transaction
create_tx_cursor = connection.run(
_get_asset_create_tx_query(asset_id).get_field('id'))
# Then find any TRANSFER transactions related to the asset
transfer_tx_cursor = connection.run(
r.table('bigchain')
.get_all(asset_id, index='asset_id')
.concat_map(lambda block: block['block']['transactions'])
.filter(lambda transaction: transaction['asset']['id'] == asset_id)
.get_field('id'))
return chain(create_tx_cursor, transfer_tx_cursor)
@register_query(RethinkDBConnection)
def get_asset_by_id(connection, asset_id):
return connection.run(
r.table('bigchain', read_mode=READ_MODE)
.get_all(asset_id, index='asset_id')
.concat_map(lambda block: block['block']['transactions'])
.filter(lambda transaction: transaction['asset']['id'] == asset_id)
.filter(lambda transaction: transaction['operation'] == 'CREATE')
.pluck('asset'))
return connection.run(_get_asset_create_tx_query(asset_id).pluck('asset'))
def _get_asset_create_tx_query(asset_id):
return r.table('bigchain', read_mode=READ_MODE) \
.get_all(asset_id, index='transaction_id') \
.concat_map(lambda block: block['block']['transactions']) \
.filter(lambda transaction: transaction['id'] == asset_id)
@register_query(RethinkDBConnection)
@ -134,7 +144,7 @@ def get_votes_by_block_id_and_voter(connection, block_id, node_pubkey):
def write_block(connection, block):
return connection.run(
r.table('bigchain')
.insert(r.json(block), durability=WRITE_DURABILITY))
.insert(r.json(block.to_str()), durability=WRITE_DURABILITY))
@register_query(RethinkDBConnection)

View File

@ -60,7 +60,7 @@ def create_bigchain_secondary_index(connection, dbname):
.table('bigchain')
.index_create('transaction_id', r.row['block']['transactions']['id'], multi=True))
# secondary index for asset uuid
# secondary index for asset links (in TRANSFER transactions)
connection.run(
r.db(dbname)
.table('bigchain')

View File

@ -87,4 +87,4 @@ class AssetIdMismatch(Exception):
class AmountError(Exception):
"""Raised when the amount of a non-divisible asset is different then 1"""
"""Raised when there is a problem with a transaction's output amounts"""

View File

@ -104,25 +104,14 @@ definitions:
description: |
Description of the asset being transacted. In the case of a ``TRANSFER``
transaction, this field contains only the ID of asset. In the case
of a ``CREATE`` transaction, this field may contain properties:
of a ``CREATE`` transaction, this field contains only the user-defined
payload.
additionalProperties: false
required:
- id
properties:
id:
"$ref": "#/definitions/uuid4"
divisible:
type: boolean
"$ref": "#/definitions/sha3_hexdigest"
description: |
Whether or not the asset has a quantity that may be partially spent.
updatable:
type: boolean
description: |
Whether or not the description of the asset may be updated. Defaults to false.
refillable:
type: boolean
description: |
Whether the amount of the asset can change after its creation. Defaults to false.
ID of the transaction that created the asset.
data:
description: |
User provided metadata associated with the asset. May also be ``null``.
@ -170,6 +159,10 @@ definitions:
"$ref": "#/definitions/public_keys"
description: |
List of public keys associated with the conditions on an output.
amount:
type: integer
description: |
Integral amount of the asset represented by this condition.
input:
type: "object"
description:

View File

@ -1,6 +1,5 @@
from copy import deepcopy
from functools import reduce
from uuid import uuid4
from cryptoconditions import (Fulfillment, ThresholdSha256Fulfillment,
Ed25519Fulfillment)
@ -225,9 +224,12 @@ class Output(object):
"""
if not isinstance(public_keys, list) and public_keys is not None:
raise TypeError('`public_keys` must be a list instance or None')
if not isinstance(amount, int):
raise TypeError('`amount` must be an int')
if amount < 1:
raise AmountError('`amount` must be greater than 0')
self.fulfillment = fulfillment
# TODO: Not sure if we should validate for value here
self.amount = amount
self.public_keys = public_keys
@ -381,189 +383,6 @@ class Output(object):
return cls(fulfillment, data['public_keys'], data['amount'])
class Asset(object):
"""An Asset is a fungible unit to spend and lock with Transactions.
Note:
Currently, the following flags are not yet fully supported:
- `divisible`
- `updatable`
- `refillable`
Attributes:
data (dict): A dictionary of data that can be added to an Asset.
data_id (str): A unique identifier of `data`'s content.
divisible (bool): A flag indicating if an Asset can be divided.
updatable (bool): A flag indicating if an Asset can be updated.
refillable (bool): A flag indicating if an Asset can be refilled.
"""
def __init__(self, data=None, data_id=None, divisible=False,
updatable=False, refillable=False):
"""An Asset is not required to contain any extra data from outside."""
self.data = data
self.data_id = data_id if data_id is not None else self.to_hash()
self.divisible = divisible
self.updatable = updatable
self.refillable = refillable
self.validate_asset()
def __eq__(self, other):
try:
other_dict = other.to_dict()
except AttributeError:
return False
return self.to_dict() == other_dict
def to_dict(self):
"""Transforms the object to a Python dictionary.
Returns:
(dict): The Asset object as an alternative serialization
format.
"""
return {
'id': self.data_id,
'divisible': self.divisible,
'updatable': self.updatable,
'refillable': self.refillable,
'data': self.data,
}
@classmethod
def from_dict(cls, asset):
"""Transforms a Python dictionary to an Asset object.
Args:
asset (dict): The dictionary to be serialized.
Returns:
:class:`~bigchaindb.common.transaction.Asset`
"""
return cls(asset.get('data'), asset['id'],
asset.get('divisible', False),
asset.get('updatable', False),
asset.get('refillable', False))
def to_hash(self):
"""Generates a unqiue uuid for an Asset"""
return str(uuid4())
@staticmethod
def get_asset_id(transactions):
"""Get the asset id from a list of transaction ids.
This is useful when we want to check if the multiple inputs of a
transaction are related to the same asset id.
Args:
transactions (:obj:`list` of :class:`~bigchaindb.common.
transaction.Transaction`): list of transaction usually inputs
that should have a matching asset_id
Returns:
str: uuid of the asset.
Raises:
AssetIdMismatch: If the inputs are related to different assets.
"""
if not isinstance(transactions, list):
transactions = [transactions]
# create a set of asset_ids
asset_ids = {tx.asset.data_id for tx in transactions}
# check that all the transasctions have the same asset_id
if len(asset_ids) > 1:
raise AssetIdMismatch(('All inputs of all transactions passed'
' need to have the same asset id'))
return asset_ids.pop()
def validate_asset(self, amount=None):
"""Validates the asset"""
if self.data is not None and not isinstance(self.data, dict):
raise TypeError('`data` must be a dict instance or None')
if not isinstance(self.divisible, bool):
raise TypeError('`divisible` must be a boolean')
if not isinstance(self.refillable, bool):
raise TypeError('`refillable` must be a boolean')
if not isinstance(self.updatable, bool):
raise TypeError('`updatable` must be a boolean')
if self.refillable:
raise NotImplementedError('Refillable assets are not yet'
' implemented')
if self.updatable:
raise NotImplementedError('Updatable assets are not yet'
' implemented')
# If the amount is supplied we can perform extra validations to
# the asset
if amount is not None:
if not isinstance(amount, int):
raise TypeError('`amount` must be an int')
if self.divisible is False and amount != 1:
raise AmountError('non divisible assets always have'
' amount equal to one')
# Since refillable assets are not yet implemented this should
# raise and exception
if self.divisible is True and amount < 2:
raise AmountError('divisible assets must have an amount'
' greater than one')
class AssetLink(Asset):
"""An object for unidirectional linking to a Asset.
"""
def __init__(self, data_id=None):
"""Create an instance of a :class:`~.AssetLink`.
Args:
data_id (str): A Asset to link to.
"""
self.data_id = data_id
def __bool__(self):
return self.data_id is not None
def __eq__(self, other):
return isinstance(other, AssetLink) and \
self.to_dict() == other.to_dict()
@classmethod
def from_dict(cls, link):
"""Transforms a Python dictionary to a AssetLink object.
Args:
link (dict): The link to be transformed.
Returns:
:class:`~bigchaindb.common.transaction.AssetLink`
"""
try:
return cls(link['id'])
except TypeError:
return cls()
def to_dict(self):
"""Transforms the object to a Python dictionary.
Returns:
(dict|None): The link as an alternative serialization format.
"""
if self.data_id is None:
return None
else:
return {
'id': self.data_id
}
class Transaction(object):
"""A Transaction is used to create and transfer assets.
@ -578,6 +397,10 @@ class Transaction(object):
spend.
outputs (:obj:`list` of :class:`~bigchaindb.common.
transaction.Output`, optional): Define the assets to lock.
asset (dict): Asset payload for this Transaction. ``CREATE`` and
``GENESIS`` Transactions require a dict with a ``data``
property while ``TRANSFER`` Transactions require a dict with a
``id`` property.
metadata (dict):
Metadata to be stored along with the Transaction.
version (int): Defines the version number of a Transaction.
@ -598,28 +421,32 @@ class Transaction(object):
Args:
operation (str): Defines the operation of the Transaction.
asset (:class:`~bigchaindb.common.transaction.Asset`): An Asset
to be transferred or created in a Transaction.
asset (dict): Asset payload for this Transaction.
inputs (:obj:`list` of :class:`~bigchaindb.common.
transaction.Input`, optional): Define the assets to
spend.
outputs (:obj:`list` of :class:`~bigchaindb.common.
transaction.Output`, optional): Define the assets to
lock.
metadata (dict):
Metadata to be stored along with the Transaction.
metadata (dict): Metadata to be stored along with the
Transaction.
version (int): Defines the version number of a Transaction.
"""
if operation not in Transaction.ALLOWED_OPERATIONS:
allowed_ops = ', '.join(self.__class__.ALLOWED_OPERATIONS)
raise ValueError('`operation` must be one of {}'
.format(allowed_ops))
# Only assets for 'CREATE' operations can be un-defined.
if (asset and not isinstance(asset, Asset) or
not asset and operation != Transaction.CREATE):
raise TypeError('`asset` must be an Asset instance')
# Asset payloads for 'CREATE' and 'GENESIS' operations must be None or
# dicts holding a `data` property. Asset payloads for 'TRANSFER'
# operations must be dicts holding an `id` property.
if (operation in [Transaction.CREATE, Transaction.GENESIS] and
asset is not None and not (isinstance(asset, dict) and 'data' in asset)):
raise TypeError(('`asset` must be None or a dict holding a `data` '
" property instance for '{}' Transactions".format(operation)))
elif (operation == Transaction.TRANSFER and
not (isinstance(asset, dict) and 'id' in asset)):
raise TypeError(('`asset` must be a dict holding an `id` property '
"for 'TRANSFER' Transactions".format(operation)))
if outputs and not isinstance(outputs, list):
raise TypeError('`outputs` must be a list instance or None')
@ -632,20 +459,11 @@ class Transaction(object):
self.version = version if version is not None else self.VERSION
self.operation = operation
self.asset = asset if asset else Asset()
self.outputs = outputs if outputs else []
self.inputs = inputs if inputs else []
self.asset = asset
self.inputs = inputs or []
self.outputs = outputs or []
self.metadata = metadata
# validate asset
# we know that each transaction relates to a single asset
# we can sum the amount of all the outputs
# for transactions other then CREATE we only have an id so there is
# nothing we can validate
if self.operation == self.CREATE:
amount = sum([output.amount for output in self.outputs])
self.asset.validate_asset(amount=amount)
@classmethod
def create(cls, tx_signers, recipients, metadata=None, asset=None):
"""A simple way to generate a `CREATE` transaction.
@ -666,10 +484,10 @@ class Transaction(object):
recipients (:obj:`list` of :obj:`str`): A list of keys that
represent the recipients of the outputs of this
Transaction.
metadata (dict): Python dictionary to be stored along with the
metadata (dict): The metadata to be stored along with the
Transaction.
asset (:class:`~bigchaindb.common.transaction.Asset`): An Asset
to be created in this Transaction.
asset (dict): The metadata associated with the asset that will
be created in this Transaction.
Returns:
:class:`~bigchaindb.common.transaction.Transaction`
@ -682,6 +500,8 @@ class Transaction(object):
raise ValueError('`tx_signers` list cannot be empty')
if len(recipients) == 0:
raise ValueError('`recipients` list cannot be empty')
if not (asset is None or isinstance(asset, dict)):
raise TypeError('`asset` must be a dict or None')
inputs = []
outputs = []
@ -698,10 +518,10 @@ class Transaction(object):
# generate inputs
inputs.append(Input.generate(tx_signers))
return cls(cls.CREATE, asset, inputs, outputs, metadata)
return cls(cls.CREATE, {'data': asset}, inputs, outputs, metadata)
@classmethod
def transfer(cls, inputs, recipients, asset, metadata=None):
def transfer(cls, inputs, recipients, asset_id, metadata=None):
"""A simple way to generate a `TRANSFER` transaction.
Note:
@ -731,8 +551,8 @@ class Transaction(object):
recipients (:obj:`list` of :obj:`str`): A list of
([keys],amount) that represent the recipients of this
Transaction.
asset (:class:`~bigchaindb.common.transaction.Asset`): An Asset
to be transferred in this Transaction.
asset_id (str): The asset ID of the asset to be transferred in
this Transaction.
metadata (dict): Python dictionary to be stored along with the
Transaction.
@ -752,13 +572,15 @@ class Transaction(object):
for recipient in recipients:
if not isinstance(recipient, tuple) or len(recipient) != 2:
raise ValueError(('Each `recipient` in the list must be a'
' tuple of `([<list of public keys>],'
' <amount>)`'))
pub_keys, amount = recipient
outputs.append(Output.generate(pub_keys, amount))
if not isinstance(asset_id, str):
raise TypeError('`asset_id` must be a string')
inputs = deepcopy(inputs)
return cls(cls.TRANSFER, asset, inputs, outputs, metadata)
return cls(cls.TRANSFER, {'id': asset_id}, inputs, outputs, metadata)
def __eq__(self, other):
try:
@ -1092,18 +914,12 @@ class Transaction(object):
Returns:
dict: The Transaction as an alternative serialization format.
"""
if self.operation in (self.__class__.GENESIS, self.__class__.CREATE):
asset = self.asset.to_dict()
else:
# NOTE: An `asset` in a `TRANSFER` only contains the asset's id
asset = {'id': self.asset.data_id}
tx = {
'inputs': [input_.to_dict() for input_ in self.inputs],
'outputs': [output.to_dict() for output in self.outputs],
'operation': str(self.operation),
'metadata': self.metadata,
'asset': asset,
'asset': self.asset,
'version': self.version,
}
@ -1157,6 +973,40 @@ class Transaction(object):
tx = Transaction._remove_signatures(self.to_dict())
return Transaction._to_str(tx)
@staticmethod
def get_asset_id(transactions):
"""Get the asset id from a list of :class:`~.Transactions`.
This is useful when we want to check if the multiple inputs of a
transaction are related to the same asset id.
Args:
transactions (:obj:`list` of :class:`~bigchaindb.common.
transaction.Transaction`): A list of Transactions.
Usually input Transactions that should have a matching
asset ID.
Returns:
str: ID of the asset.
Raises:
:exc:`AssetIdMismatch`: If the inputs are related to different
assets.
"""
if not isinstance(transactions, list):
transactions = [transactions]
# create a set of the transactions' asset ids
asset_ids = {tx.id if tx.operation == Transaction.CREATE else tx.asset['id']
for tx in transactions}
# check that all the transasctions have the same asset id
if len(asset_ids) > 1:
raise AssetIdMismatch(('All inputs of all transactions passed'
' need to have the same asset id'))
return asset_ids.pop()
@staticmethod
def validate_structure(tx_body):
"""Validate the transaction ID of a transaction
@ -1193,10 +1043,5 @@ class Transaction(object):
cls.validate_structure(tx)
inputs = [Input.from_dict(input_) for input_ in tx['inputs']]
outputs = [Output.from_dict(output) for output in tx['outputs']]
if tx['operation'] in [cls.CREATE, cls.GENESIS]:
asset = Asset.from_dict(tx['asset'])
else:
asset = AssetLink.from_dict(tx['asset'])
return cls(tx['operation'], asset, inputs, outputs,
return cls(tx['operation'], tx['asset'], inputs, outputs,
tx['metadata'], tx['version'])

View File

@ -6,7 +6,7 @@ from time import time
from itertools import compress
from bigchaindb.common import crypto, exceptions
from bigchaindb.common.util import gen_timestamp, serialize
from bigchaindb.common.transaction import TransactionLink, Asset
from bigchaindb.common.transaction import TransactionLink
import bigchaindb
@ -348,13 +348,12 @@ class Bigchain(object):
asset_id (str): The asset id.
Returns:
:class:`~bigchaindb.common.transaction.Asset` if the asset
exists else None.
dict if the asset exists else None.
"""
cursor = backend.query.get_asset_by_id(self.connection, asset_id)
cursor = list(cursor)
if cursor:
return Asset.from_dict(cursor[0]['asset'])
return cursor[0]['asset']
def get_spent(self, txid, output):
"""Check if a `txid` was already used as an input.
@ -517,7 +516,7 @@ class Bigchain(object):
block (Block): block to write to bigchain.
"""
return backend.query.write_block(self.connection, block.to_str())
return backend.query.write_block(self.connection, block)
def transaction_exists(self, transaction_id):
return backend.query.has_transaction(self.connection, transaction_id)

View File

@ -4,7 +4,7 @@ from bigchaindb.common.exceptions import (InvalidHash, InvalidSignature,
TransactionDoesNotExist,
TransactionNotInValidBlock,
AssetIdMismatch, AmountError)
from bigchaindb.common.transaction import Transaction, Asset
from bigchaindb.common.transaction import Transaction
from bigchaindb.common.util import gen_timestamp, serialize
from bigchaindb.common.schema import validate_transaction_schema
@ -39,21 +39,30 @@ class Transaction(Transaction):
input_conditions = []
inputs_defined = all([input_.fulfills for input_ in self.inputs])
# validate amounts
if any(output.amount < 1 for output in self.outputs):
raise AmountError('`amount` needs to be greater than zero')
if self.operation in (Transaction.CREATE, Transaction.GENESIS):
# validate asset
if self.asset['data'] is not None and not isinstance(self.asset['data'], dict):
raise TypeError(('`asset.data` must be a dict instance or '
'None for `CREATE` transactions'))
# validate inputs
if inputs_defined:
raise ValueError('A CREATE operation has no inputs')
# validate asset
amount = sum([output.amount for output in self.outputs])
self.asset.validate_asset(amount=amount)
elif self.operation == Transaction.TRANSFER:
# validate asset
if not isinstance(self.asset['id'], str):
raise ValueError(('`asset.id` must be a string for '
'`TRANSFER` transations'))
# check inputs
if not inputs_defined:
raise ValueError('Only `CREATE` transactions can have null '
'inputs')
# check inputs
# store the inputs so that we can check if the asset ids match
input_txs = []
input_amount = 0
for input_ in self.inputs:
input_txid = input_.fulfills.txid
input_tx, status = bigchain.\
@ -78,24 +87,21 @@ class Transaction(Transaction):
input_txs.append(input_tx)
if output.amount < 1:
raise AmountError('`amount` needs to be greater than zero')
input_amount += output.amount
# validate asset id
asset_id = Asset.get_asset_id(input_txs)
if asset_id != self.asset.data_id:
raise AssetIdMismatch('The asset id of the input does not '
'match the asset id of the transaction')
asset_id = Transaction.get_asset_id(input_txs)
if asset_id != self.asset['id']:
raise AssetIdMismatch(('The asset id of the input does not'
' match the asset id of the'
' transaction'))
# get the asset creation to see if its divisible or not
asset = bigchain.get_asset_by_id(asset_id)
# validate the asset
asset.validate_asset(amount=input_amount)
# validate the amounts
output_amount = 0
for output in self.outputs:
if output.amount < 1:
raise AmountError('`amount` needs to be greater than zero')
output_amount += output.amount
input_amount = sum([input_condition.amount for input_condition in input_conditions])
output_amount = sum([output_condition.amount for output_condition in self.outputs])
if output_amount != input_amount:
raise AmountError(('The amount used in the inputs `{}`'
@ -110,8 +116,8 @@ class Transaction(Transaction):
if not self.inputs_valid(input_conditions):
raise InvalidSignature()
else:
return self
return self
@classmethod
def from_dict(cls, tx_body):

View File

@ -4,7 +4,7 @@ import json
import os
import os.path
from bigchaindb.common.transaction import Asset, Transaction
from bigchaindb.common.transaction import Transaction
TPLS = {}
@ -62,8 +62,7 @@ def main():
""" Main function """
privkey = 'CfdqtD7sS7FgkMoGPXw55MVGGFwQLAoHYTcBhZDtF99Z'
pubkey = '4K9sWUMFwTgaDGPfdynrbxWqWS6sWmKbZoTjxLtVUibD'
asset = Asset(None, 'e6969f87-4fc9-4467-b62a-f0dfa1c85002')
tx = Transaction.create([pubkey], [([pubkey], 1)], asset=asset)
tx = Transaction.create([pubkey], [([pubkey], 1)])
tx = tx.sign([privkey])
tx_json = json.dumps(tx.to_dict(), indent=2, sort_keys=True)

View File

@ -22,8 +22,6 @@ One can deploy a BigchainDB node on Azure using the template in the `bigchaindb-
1. You should be prompted for a password. Give the `<Admin_password>` you entered into the form.
1. If pull request [#2884](https://github.com/Azure/azure-quickstart-templates/pull/2884) has been merged, then skip this step. Otherwise, go to the `init.sh` script at [https://github.com/Azure/azure-quickstart-templates/blob/master/bigchaindb-on-ubuntu/scripts/init.sh](https://github.com/Azure/azure-quickstart-templates/blob/master/bigchaindb-on-ubuntu/scripts/init.sh). Your goal is to read through that script and check if each line should be run manually in the terminal. For example, is RethinkDB already installed and running? You can figure that out using `pgrep rethinkdb`. And so on. If in doubt, just try running each command in `init.sh`.
1. Configure BigchainDB Server by doing:
```text
bigchaindb configure

View File

@ -5,27 +5,17 @@ To avoid redundant data in transactions, the digital asset model is different fo
A digital asset's properties are defined in a `CREATE` transaction with the following model:
```json
{
"id": "<uuid>",
"divisible": "<true | false>",
"updatable": "<true | false>",
"refillable": "<true | false>",
"data": "<json document>"
}
```
For `TRANSFER` transactions we only keep the asset id.
For `TRANSFER` transactions we only keep the asset ID:
```json
{
"id": "<uuid>"
"id": "<asset's CREATE transaction ID (sha3-256 hash)>"
}
```
- `id`: UUID version 4 (random) converted to a string of hex digits in standard form. Added server side.
- `divisible`: Whether the asset is divisible or not. Defaults to false.
- `updatable`: Whether the data in the asset can be updated in the future or not. Defaults to false.
- `refillable`: Whether the amount of the asset can change after its creation. Defaults to false.
- `id`: The ID of the `CREATE` transaction that created the asset.
- `data`: A user supplied JSON document with custom information about the asset. Defaults to null.
- _amount_: The amount of "shares". Only relevant if the asset is marked as divisible. Defaults to 1. The amount is not specified in the asset, but in the outputs (see next section).
At the time of this writing, updatable and refillable assets are not yet implemented.

View File

@ -5,4 +5,4 @@ Develop & Test BigchainDB Server
:maxdepth: 1
setup-run-node
running-unit-tests
running-all-tests

View File

@ -0,0 +1,3 @@
# Running All Tests
All documentation about writing and running tests (unit and integration tests) was moved to [the file `bigchaindb/tests/README.md`](https://github.com/bigchaindb/bigchaindb/blob/master/tests/README.md).

View File

@ -1,36 +0,0 @@
# Running Unit Tests
Once you've installed BigchainDB Server, you may want to run all the unit tests. This section explains how.
First of all, if you installed BigchainDB Server using `pip` (i.e. by getting the package from PyPI), then you didn't install the tests. **Before you can run all the unit tests, you must [install BigchainDB from source](setup-run-node.html#how-to-install-bigchaindb-from-source).**
To run all the unit tests, first make sure you have RethinkDB running:
```text
$ rethinkdb
```
then in another terminal, do:
```text
$ python setup.py test
```
(Aside: How does the above command work? The documentation for [pytest-runner](https://pypi.python.org/pypi/pytest-runner) explains. We use [pytest](http://docs.pytest.org/en/latest/) to write all unit tests.)
### Using docker-compose to Run the Tests
You can also use `docker-compose` to run the unit tests.
Start `RethinkDB` in the background:
```text
$ docker-compose up -d rdb
```
then run the unit tests using:
```text
$ docker-compose run --rm bdb py.test -v
```

View File

@ -50,6 +50,9 @@ Build the images:
docker-compose build
```
**Note**: If you're upgrading BigchainDB and have previously already built the images, you may need
to rebuild them after the upgrade to install any new dependencies.
Start RethinkDB:
```bash

View File

@ -5,31 +5,23 @@ The HTTP Client-Server API
The HTTP client-server API is currently quite rudimentary. For example,
there is no ability to do complex queries using the HTTP API. We plan to add
querying capabilities in the future.
more querying capabilities in the future.
When you start Bigchaindb using `bigchaindb start`, an HTTP API is exposed at
the address stored in the BigchainDB node configuration settings. The default
is:
This page assumes you already know an API Root URL
for a BigchainDB node or reverse proxy.
It should be something like ``http://apihosting4u.net:9984``
or ``http://12.34.56.78:9984``.
`http://localhost:9984/api/v1/ <http://localhost:9984/api/v1/>`_
but that address can be changed by changing the "API endpoint" configuration
setting (e.g. in a local config file). There's more information about setting
the API endpoint in :doc:`the section about BigchainDB Configuration Settings
<../server-reference/configuration>`.
There are other configuration settings related to the web server (serving the
HTTP API). In particular, the default is for the web server socket to bind to
``localhost:9984`` but that can be changed (e.g. to ``0.0.0.0:9984``). For more
details, see the "server" settings ("bind", "workers" and "threads") in
:doc:`the section about BigchainDB Configuration Settings
<../server-reference/configuration>`.
If you set up a BigchainDB node or reverse proxy yourself,
and you're not sure what the API Root URL is,
then see the last section of this page for help.
API Root URL
------------
If you send an HTTP GET request to e.g. ``http://localhost:9984``
If you send an HTTP GET request to the API Root URL
e.g. ``http://localhost:9984``
or ``http://apihosting4u.net:9984``
(with no ``/api/v1/`` on the end),
then you should get an HTTP response
@ -47,25 +39,6 @@ with something like the following in the body:
"version": "0.6.0"
}
If the API endpoint is publicly-accessible,
then the public API Root URL is determined as follows:
- The public IP address (like 12.34.56.78)
is the public IP address of the machine exposing
the HTTP API to the public internet (e.g. either the machine hosting
Gunicorn or the machine running the reverse proxy such as Nginx).
It's determined by AWS, Azure, Rackspace, or whoever is hosting the machine.
- The DNS hostname (like apihosting4u.net) is determined by DNS records,
such as an "A Record" associating apihosting4u.net with 12.34.56.78
- The port (like 9984) is determined by the ``server.bind`` setting
if Gunicorn is exposed directly to the public Internet.
If a reverse proxy (like Nginx) is exposed directly to the public Internet
instead, then it could expose the HTTP API on whatever port it wants to.
(It should expose the HTTP API on port 9984, but it's not bound to do
that by anything other than convention.)
POST /transactions/
-------------------
@ -195,3 +168,40 @@ GET /unspents/
:statuscode 200: A list of outputs were found and returned in the body of the response.
:statuscode 400: The request wasn't understood by the server, e.g. the ``owner_after`` querystring was not included in the request.
Determining the API Root URL
----------------------------
When you start BigchainDB Server using ``bigchaindb start``,
an HTTP API is exposed at some address. The default is:
`http://localhost:9984/api/v1/ <http://localhost:9984/api/v1/>`_
It's bound to ``localhost``,
so you can access it from the same machine,
but it won't be directly accessible from the outside world.
(The outside world could connect via a SOCKS proxy or whatnot.)
The documentation about BigchainDB Server :any:`Configuration Settings`
has a section about how to set ``server.bind`` so as to make
the HTTP API publicly accessible.
If the API endpoint is publicly accessible,
then the public API Root URL is determined as follows:
- The public IP address (like 12.34.56.78)
is the public IP address of the machine exposing
the HTTP API to the public internet (e.g. either the machine hosting
Gunicorn or the machine running the reverse proxy such as Nginx).
It's determined by AWS, Azure, Rackspace, or whoever is hosting the machine.
- The DNS hostname (like apihosting4u.net) is determined by DNS records,
such as an "A Record" associating apihosting4u.net with 12.34.56.78
- The port (like 9984) is determined by the ``server.bind`` setting
if Gunicorn is exposed directly to the public Internet.
If a reverse proxy (like Nginx) is exposed directly to the public Internet
instead, then it could expose the HTTP API on whatever port it wants to.
(It should expose the HTTP API on port 9984, but it's not bound to do
that by anything other than convention.)

View File

@ -1,18 +1,99 @@
# Tests
# BigchainDB Server Tests
## Test Structure
## The tests/ Folder
Generally all tests are meant to be unit tests, with the exception of those in the [`integration/` folder](./integration/).
The `tests/` folder is where all the tests for BigchainDB Server live. Most of them are unit tests. Integration tests are in the [`tests/integration/` folder](./integration/).
A few notes:
- [`common/`](./common/) contains self-contained tests only testing
- [`tests/common/`](./common/) contains self-contained tests only testing
[`bigchaindb/common/`](../bigchaindb/common/)
- [`db/`](./db/) contains tests requiring the database backend (e.g. RethinkDB)
- [`tests/db/`](./db/) contains tests requiring the database backend (e.g. RethinkDB)
## Pytest Customizations
Customizations we've added to `pytest`:
## Writing Tests
- `--database-backend`: Defines the backend to use for the tests. Must be one of the backends
available in the [server configuration](https://docs.bigchaindb.com/projects/server/en/latest/server-reference/configuration.html)
We write unit and integration tests for our Python code using the [pytest](http://pytest.org/latest/) framework. You can use the tests in the `tests/` folder as templates or examples.
## Running Tests
### Running Tests Directly
If you installed BigchainDB Server using `pip install bigchaindb`, then you didn't install the tests. Before you can run all the tests, you must install BigchainDB from source. The [`CONTRIBUTING.md` file](../CONTRIBUTING.md) has instructions for how to do that.
Next, make sure you have RethinkDB running in the background (e.g. using `rethinkdb --daemon`).
Now you can run all tests using:
```text
py.test -v
```
or, if that doesn't work, try:
```text
python -m pytest -v
```
or:
```text
python setup.py test
```
How does `python setup.py test` work? The documentation for [pytest-runner](https://pypi.python.org/pypi/pytest-runner) explains.
The `pytest` command has many options. If you want to learn about all the things you can do with pytest, see [the pytest documentation](http://pytest.org/latest/). We've also added a customization to pytest:
`--database-backend`: Defines the backend to use for the tests.
It must be one of the backends available in the [server configuration](https://docs.bigchaindb.com/projects/server/en/latest/server-reference/configuration.html).
### Running Tests with Docker Compose
You can also use [Docker Compose](https://docs.docker.com/compose/) to run all the tests.
First, start `RethinkDB` in the background:
```text
$ docker-compose up -d rdb
```
then run the tests using:
```text
$ docker-compose run --rm bdb py.test -v
```
If you've upgraded to a newer version of BigchainDB, you might have to rebuild the images before
being able to run the tests. Run:
```text
$ docker-compose build
```
to rebuild all the images (usually you only need to rebuild the `bdb` image).
## Automated Testing of All Pull Requests
We use [Travis CI](https://travis-ci.com/), so that whenever someone creates a new BigchainDB pull request on GitHub, Travis CI gets the new code and does _a bunch of stuff_. You can find out what we tell Travis CI to do in [the `.travis.yml` file](.travis.yml): it tells Travis CI how to install BigchainDB, how to run all the tests, and what to do "after success" (e.g. run `codecov`). (We use [Codecov](https://codecov.io/) to get a rough estimate of our test coverage.)
### Tox
We use [tox](https://tox.readthedocs.io/en/latest/) to run multiple suites of tests against multiple environments during automated testing. Generally you don't need to run this yourself, but it might be useful when troubleshooting a failing Travis CI build.
To run all the tox tests, use:
```text
tox
```
or:
```text
python -m tox
```
To run only a few environments, use the `-e` flag:
```text
tox -e {ENVLIST}
```
where `{ENVLIST}` is one or more of the environments specified in the [tox.ini file](../tox.ini).

View File

@ -1,7 +1,8 @@
import pytest
from unittest.mock import patch
import random
@pytest.mark.bdb
@pytest.mark.usefixtures('inputs')
def test_asset_transfer(b, user_pk, user_sk):
from bigchaindb.models import Transaction
@ -10,49 +11,26 @@ def test_asset_transfer(b, user_pk, user_sk):
tx_create = b.get_transaction(tx_input.txid)
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([user_pk], 1)],
tx_create.asset)
tx_create.id)
tx_transfer_signed = tx_transfer.sign([user_sk])
assert tx_transfer_signed.validate(b) == tx_transfer_signed
assert tx_transfer_signed.asset.data_id == tx_create.asset.data_id
assert tx_transfer_signed.asset['id'] == tx_create.id
def test_validate_bad_asset_creation(b, user_pk):
from bigchaindb.models import Transaction, Asset
# `divisible` needs to be a boolean
tx = Transaction.create([b.me], [([user_pk], 1)])
tx.asset.divisible = 1
with patch.object(Asset, 'validate_asset', return_value=None):
tx_signed = tx.sign([b.me_private])
with pytest.raises(TypeError):
tx_signed.validate(b)
# `refillable` needs to be a boolean
tx = Transaction.create([b.me], [([user_pk], 1)])
tx.asset.refillable = 1
with patch.object(Asset, 'validate_asset', return_value=None):
tx_signed = tx.sign([b.me_private])
with pytest.raises(TypeError):
b.validate_transaction(tx_signed)
# `updatable` needs to be a boolean
tx = Transaction.create([b.me], [([user_pk], 1)])
tx.asset.updatable = 1
with patch.object(Asset, 'validate_asset', return_value=None):
tx_signed = tx.sign([b.me_private])
with pytest.raises(TypeError):
b.validate_transaction(tx_signed)
from bigchaindb.models import Transaction
# `data` needs to be a dictionary
tx = Transaction.create([b.me], [([user_pk], 1)])
tx.asset.data = 'a'
with patch.object(Asset, 'validate_asset', return_value=None):
tx_signed = tx.sign([b.me_private])
tx.asset['data'] = 'a'
tx_signed = tx.sign([b.me_private])
with pytest.raises(TypeError):
b.validate_transaction(tx_signed)
@pytest.mark.bdb
@pytest.mark.usefixtures('inputs')
def test_validate_transfer_asset_id_mismatch(b, user_pk, user_sk):
from bigchaindb.common.exceptions import AssetIdMismatch
@ -61,31 +39,32 @@ def test_validate_transfer_asset_id_mismatch(b, user_pk, user_sk):
tx_create = b.get_owned_ids(user_pk).pop()
tx_create = b.get_transaction(tx_create.txid)
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([user_pk], 1)],
tx_create.asset)
tx_transfer.asset.data_id = 'aaa'
tx_create.id)
tx_transfer.asset['id'] = 'aaa'
tx_transfer_signed = tx_transfer.sign([user_sk])
with pytest.raises(AssetIdMismatch):
tx_transfer_signed.validate(b)
def test_get_asset_id_create_transaction(b, user_pk):
from bigchaindb.models import Transaction, Asset
from bigchaindb.models import Transaction
tx_create = Transaction.create([b.me], [([user_pk], 1)])
asset_id = Asset.get_asset_id(tx_create)
asset_id = Transaction.get_asset_id(tx_create)
assert asset_id == tx_create.asset.data_id
assert asset_id == tx_create.id
@pytest.mark.bdb
@pytest.mark.usefixtures('inputs')
def test_get_asset_id_transfer_transaction(b, user_pk, user_sk):
from bigchaindb.models import Transaction, Asset
from bigchaindb.models import Transaction
tx_create = b.get_owned_ids(user_pk).pop()
tx_create = b.get_transaction(tx_create.txid)
# create a transfer transaction
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([user_pk], 1)],
tx_create.asset)
tx_create.id)
tx_transfer_signed = tx_transfer.sign([user_sk])
# create a block
block = b.create_block([tx_transfer_signed])
@ -93,38 +72,41 @@ def test_get_asset_id_transfer_transaction(b, user_pk, user_sk):
# vote the block valid
vote = b.vote(block.id, b.get_last_voted_block().id, True)
b.write_vote(vote)
asset_id = Asset.get_asset_id(tx_transfer)
asset_id = Transaction.get_asset_id(tx_transfer)
assert asset_id == tx_transfer.asset.data_id
assert asset_id == tx_transfer.asset['id']
def test_asset_id_mismatch(b, user_pk):
from bigchaindb.models import Transaction, Asset
from bigchaindb.models import Transaction
from bigchaindb.common.exceptions import AssetIdMismatch
tx1 = Transaction.create([b.me], [([user_pk], 1)])
tx2 = Transaction.create([b.me], [([user_pk], 1)])
tx1 = Transaction.create([b.me], [([user_pk], 1)],
metadata={'msg': random.random()})
tx2 = Transaction.create([b.me], [([user_pk], 1)],
metadata={'msg': random.random()})
with pytest.raises(AssetIdMismatch):
Asset.get_asset_id([tx1, tx2])
Transaction.get_asset_id([tx1, tx2])
@pytest.mark.bdb
@pytest.mark.usefixtures('inputs')
def test_get_transactions_by_asset_id(b, user_pk, user_sk):
from bigchaindb.models import Transaction
tx_create = b.get_owned_ids(user_pk).pop()
tx_create = b.get_transaction(tx_create.txid)
asset_id = tx_create.asset.data_id
asset_id = tx_create.id
txs = b.get_transactions_by_asset_id(asset_id)
assert len(txs) == 1
assert txs[0].id == tx_create.id
assert txs[0].asset.data_id == asset_id
assert txs[0].id == asset_id
# create a transfer transaction
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([user_pk], 1)],
tx_create.asset)
tx_create.id)
tx_transfer_signed = tx_transfer.sign([user_sk])
# create the block
block = b.create_block([tx_transfer_signed])
@ -138,26 +120,28 @@ def test_get_transactions_by_asset_id(b, user_pk, user_sk):
assert len(txs) == 2
assert tx_create.id in [t.id for t in txs]
assert tx_transfer.id in [t.id for t in txs]
assert asset_id == txs[0].asset.data_id
assert asset_id == txs[1].asset.data_id
# FIXME: can I rely on the ordering here?
assert asset_id == txs[0].id
assert asset_id == txs[1].asset['id']
@pytest.mark.bdb
@pytest.mark.usefixtures('inputs')
def test_get_transactions_by_asset_id_with_invalid_block(b, user_pk, user_sk):
from bigchaindb.models import Transaction
tx_create = b.get_owned_ids(user_pk).pop()
tx_create = b.get_transaction(tx_create.txid)
asset_id = tx_create.asset.data_id
asset_id = tx_create.id
txs = b.get_transactions_by_asset_id(asset_id)
assert len(txs) == 1
assert txs[0].id == tx_create.id
assert txs[0].asset.data_id == asset_id
assert txs[0].id == asset_id
# create a transfer transaction
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([user_pk], 1)],
tx_create.asset)
tx_create.id)
tx_transfer_signed = tx_transfer.sign([user_sk])
# create the block
block = b.create_block([tx_transfer_signed])
@ -171,17 +155,17 @@ def test_get_transactions_by_asset_id_with_invalid_block(b, user_pk, user_sk):
assert len(txs) == 1
@pytest.mark.bdb
@pytest.mark.usefixtures('inputs')
def test_get_asset_by_id(b, user_pk, user_sk):
from bigchaindb.models import Transaction
tx_create = b.get_owned_ids(user_pk).pop()
tx_create = b.get_transaction(tx_create.txid)
asset_id = tx_create.asset.data_id
# create a transfer transaction
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([user_pk], 1)],
tx_create.asset)
tx_create.id)
tx_transfer_signed = tx_transfer.sign([user_sk])
# create the block
block = b.create_block([tx_transfer_signed])
@ -190,6 +174,7 @@ def test_get_asset_by_id(b, user_pk, user_sk):
vote = b.vote(block.id, b.get_last_voted_block().id, True)
b.write_vote(vote)
asset_id = Transaction.get_asset_id([tx_create, tx_transfer])
txs = b.get_transactions_by_asset_id(asset_id)
assert len(txs) == 2
@ -198,44 +183,21 @@ def test_get_asset_by_id(b, user_pk, user_sk):
def test_create_invalid_divisible_asset(b, user_pk, user_sk):
from bigchaindb.models import Transaction, Asset
from bigchaindb.models import Transaction
from bigchaindb.common.exceptions import AmountError
# non divisible assets cannot have amount > 1
# Transaction.__init__ should raise an exception
asset = Asset(divisible=False)
with pytest.raises(AmountError):
Transaction.create([user_pk], [([user_pk], 2)], asset=asset)
# Asset amount must be more than 0
tx = Transaction.create([user_pk], [([user_pk], 1)])
tx.outputs[0].amount = 0
tx.sign([user_sk])
# divisible assets need to have an amount > 1
# Transaction.__init__ should raise an exception
asset = Asset(divisible=True)
with pytest.raises(AmountError):
Transaction.create([user_pk], [([user_pk], 1)], asset=asset)
# even if a transaction is badly constructed the server should raise the
# exception
asset = Asset(divisible=False)
with patch.object(Asset, 'validate_asset', return_value=None):
tx = Transaction.create([user_pk], [([user_pk], 2)], asset=asset)
tx_signed = tx.sign([user_sk])
with pytest.raises(AmountError):
tx_signed.validate(b)
assert b.is_valid_transaction(tx_signed) is False
asset = Asset(divisible=True)
with patch.object(Asset, 'validate_asset', return_value=None):
tx = Transaction.create([user_pk], [([user_pk], 1)], asset=asset)
tx_signed = tx.sign([user_sk])
with pytest.raises(AmountError):
tx_signed.validate(b)
assert b.is_valid_transaction(tx_signed) is False
b.validate_transaction(tx)
def test_create_valid_divisible_asset(b, user_pk, user_sk):
from bigchaindb.models import Transaction, Asset
from bigchaindb.models import Transaction
asset = Asset(divisible=True)
tx = Transaction.create([user_pk], [([user_pk], 2)], asset=asset)
tx = Transaction.create([user_pk], [([user_pk], 2)])
tx_signed = tx.sign([user_sk])
assert b.is_valid_transaction(tx_signed)

View File

@ -1,7 +1,5 @@
import pytest
from unittest.mock import patch
# CREATE divisible asset
# Single input
@ -10,10 +8,8 @@ from unittest.mock import patch
# Single owners_after
def test_single_in_single_own_single_out_single_own_create(b, user_pk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
asset = Asset(divisible=True)
tx = Transaction.create([b.me], [([user_pk], 100)], asset=asset)
tx = Transaction.create([b.me], [([user_pk], 100)])
tx_signed = tx.sign([b.me_private])
assert tx_signed.validate(b) == tx_signed
@ -29,11 +25,8 @@ def test_single_in_single_own_single_out_single_own_create(b, user_pk):
# Single owners_after per output
def test_single_in_single_own_multiple_out_single_own_create(b, user_pk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
asset = Asset(divisible=True)
tx = Transaction.create([b.me], [([user_pk], 50), ([user_pk], 50)],
asset=asset)
tx = Transaction.create([b.me], [([user_pk], 50), ([user_pk], 50)])
tx_signed = tx.sign([b.me_private])
assert tx_signed.validate(b) == tx_signed
@ -50,10 +43,8 @@ def test_single_in_single_own_multiple_out_single_own_create(b, user_pk):
# Multiple owners_after
def test_single_in_single_own_single_out_multiple_own_create(b, user_pk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
asset = Asset(divisible=True)
tx = Transaction.create([b.me], [([user_pk, user_pk], 100)], asset=asset)
tx = Transaction.create([b.me], [([user_pk, user_pk], 100)])
tx_signed = tx.sign([b.me_private])
assert tx_signed.validate(b) == tx_signed
@ -75,12 +66,8 @@ def test_single_in_single_own_single_out_multiple_own_create(b, user_pk):
# owners_after
def test_single_in_single_own_multiple_out_mix_own_create(b, user_pk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
asset = Asset(divisible=True)
tx = Transaction.create([b.me],
[([user_pk], 50), ([user_pk, user_pk], 50)],
asset=asset)
tx = Transaction.create([b.me], [([user_pk], 50), ([user_pk, user_pk], 50)])
tx_signed = tx.sign([b.me_private])
assert tx_signed.validate(b) == tx_signed
@ -102,10 +89,8 @@ def test_single_in_single_own_multiple_out_mix_own_create(b, user_pk):
def test_single_in_multiple_own_single_out_single_own_create(b, user_pk,
user_sk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
asset = Asset(divisible=True)
tx = Transaction.create([b.me, user_pk], [([user_pk], 100)], asset=asset)
tx = Transaction.create([b.me, user_pk], [([user_pk], 100)])
tx_signed = tx.sign([b.me_private, user_sk])
assert tx_signed.validate(b) == tx_signed
assert len(tx_signed.outputs) == 1
@ -126,15 +111,14 @@ def test_single_in_multiple_own_single_out_single_own_create(b, user_pk,
# else there will be no genesis block and b.get_last_voted_block will
# fail.
# Is there a better way of doing this?
@pytest.mark.bdb
@pytest.mark.usefixtures('inputs')
def test_single_in_single_own_single_out_single_own_transfer(b, user_pk,
user_sk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
# CREATE divisible asset
asset = Asset(divisible=True)
tx_create = Transaction.create([b.me], [([user_pk], 100)], asset=asset)
tx_create = Transaction.create([b.me], [([user_pk], 100)])
tx_create_signed = tx_create.sign([b.me_private])
# create block
block = b.create_block([tx_create_signed])
@ -146,7 +130,7 @@ def test_single_in_single_own_single_out_single_own_transfer(b, user_pk,
# TRANSFER
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([b.me], 100)],
asset=tx_create.asset)
asset_id=tx_create.id)
tx_transfer_signed = tx_transfer.sign([user_sk])
assert tx_transfer_signed.validate(b)
@ -160,15 +144,14 @@ def test_single_in_single_own_single_out_single_own_transfer(b, user_pk,
# Single owners_before
# Multiple output
# Single owners_after
@pytest.mark.bdb
@pytest.mark.usefixtures('inputs')
def test_single_in_single_own_multiple_out_single_own_transfer(b, user_pk,
user_sk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
# CREATE divisible asset
asset = Asset(divisible=True)
tx_create = Transaction.create([b.me], [([user_pk], 100)], asset=asset)
tx_create = Transaction.create([b.me], [([user_pk], 100)])
tx_create_signed = tx_create.sign([b.me_private])
# create block
block = b.create_block([tx_create_signed])
@ -181,7 +164,7 @@ def test_single_in_single_own_multiple_out_single_own_transfer(b, user_pk,
# TRANSFER
tx_transfer = Transaction.transfer(tx_create.to_inputs(),
[([b.me], 50), ([b.me], 50)],
asset=tx_create.asset)
asset_id=tx_create.id)
tx_transfer_signed = tx_transfer.sign([user_sk])
assert tx_transfer_signed.validate(b) == tx_transfer_signed
@ -196,15 +179,14 @@ def test_single_in_single_own_multiple_out_single_own_transfer(b, user_pk,
# Single owners_before
# Single output
# Multiple owners_after
@pytest.mark.bdb
@pytest.mark.usefixtures('inputs')
def test_single_in_single_own_single_out_multiple_own_transfer(b, user_pk,
user_sk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
# CREATE divisible asset
asset = Asset(divisible=True)
tx_create = Transaction.create([b.me], [([user_pk], 100)], asset=asset)
tx_create = Transaction.create([b.me], [([user_pk], 100)])
tx_create_signed = tx_create.sign([b.me_private])
# create block
block = b.create_block([tx_create_signed])
@ -217,7 +199,7 @@ def test_single_in_single_own_single_out_multiple_own_transfer(b, user_pk,
# TRANSFER
tx_transfer = Transaction.transfer(tx_create.to_inputs(),
[([b.me, b.me], 100)],
asset=tx_create.asset)
asset_id=tx_create.id)
tx_transfer_signed = tx_transfer.sign([user_sk])
assert tx_transfer_signed.validate(b) == tx_transfer_signed
@ -237,15 +219,14 @@ def test_single_in_single_own_single_out_multiple_own_transfer(b, user_pk,
# Multiple outputs
# Mix: one output with a single owners_after, one output with multiple
# owners_after
@pytest.mark.bdb
@pytest.mark.usefixtures('inputs')
def test_single_in_single_own_multiple_out_mix_own_transfer(b, user_pk,
user_sk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
# CREATE divisible asset
asset = Asset(divisible=True)
tx_create = Transaction.create([b.me], [([user_pk], 100)], asset=asset)
tx_create = Transaction.create([b.me], [([user_pk], 100)])
tx_create_signed = tx_create.sign([b.me_private])
# create block
block = b.create_block([tx_create_signed])
@ -258,7 +239,7 @@ def test_single_in_single_own_multiple_out_mix_own_transfer(b, user_pk,
# TRANSFER
tx_transfer = Transaction.transfer(tx_create.to_inputs(),
[([b.me], 50), ([b.me, b.me], 50)],
asset=tx_create.asset)
asset_id=tx_create.id)
tx_transfer_signed = tx_transfer.sign([user_sk])
assert tx_transfer_signed.validate(b) == tx_transfer_signed
@ -278,16 +259,14 @@ def test_single_in_single_own_multiple_out_mix_own_transfer(b, user_pk,
# Multiple owners_before
# Single output
# Single owners_after
@pytest.mark.bdb
@pytest.mark.usefixtures('inputs')
def test_single_in_multiple_own_single_out_single_own_transfer(b, user_pk,
user_sk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
# CREATE divisible asset
asset = Asset(divisible=True)
tx_create = Transaction.create([b.me], [([b.me, user_pk], 100)],
asset=asset)
tx_create = Transaction.create([b.me], [([b.me, user_pk], 100)])
tx_create_signed = tx_create.sign([b.me_private])
# create block
block = b.create_block([tx_create_signed])
@ -299,7 +278,7 @@ def test_single_in_multiple_own_single_out_single_own_transfer(b, user_pk,
# TRANSFER
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([b.me], 100)],
asset=tx_create.asset)
asset_id=tx_create.id)
tx_transfer_signed = tx_transfer.sign([b.me_private, user_sk])
assert tx_transfer_signed.validate(b) == tx_transfer_signed
@ -317,16 +296,14 @@ def test_single_in_multiple_own_single_out_single_own_transfer(b, user_pk,
# Single owners_before per input
# Single output
# Single owners_after
@pytest.mark.bdb
@pytest.mark.usefixtures('inputs')
def test_multiple_in_single_own_single_out_single_own_transfer(b, user_pk,
user_sk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
# CREATE divisible asset
asset = Asset(divisible=True)
tx_create = Transaction.create([b.me], [([user_pk], 50), ([user_pk], 50)],
asset=asset)
tx_create = Transaction.create([b.me], [([user_pk], 50), ([user_pk], 50)])
tx_create_signed = tx_create.sign([b.me_private])
# create block
block = b.create_block([tx_create_signed])
@ -338,7 +315,7 @@ def test_multiple_in_single_own_single_out_single_own_transfer(b, user_pk,
# TRANSFER
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([b.me], 100)],
asset=tx_create.asset)
asset_id=tx_create.id)
tx_transfer_signed = tx_transfer.sign([user_sk])
assert tx_transfer_signed.validate(b)
@ -352,18 +329,14 @@ def test_multiple_in_single_own_single_out_single_own_transfer(b, user_pk,
# Multiple owners_before per input
# Single output
# Single owners_after
@pytest.mark.bdb
@pytest.mark.usefixtures('inputs')
def test_multiple_in_multiple_own_single_out_single_own_transfer(b, user_pk,
user_sk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
# CREATE divisible asset
asset = Asset(divisible=True)
tx_create = Transaction.create([b.me],
[([user_pk, b.me], 50),
([user_pk, b.me], 50)],
asset=asset)
tx_create = Transaction.create([b.me], [([user_pk, b.me], 50), ([user_pk, b.me], 50)])
tx_create_signed = tx_create.sign([b.me_private])
# create block
block = b.create_block([tx_create_signed])
@ -375,7 +348,7 @@ def test_multiple_in_multiple_own_single_out_single_own_transfer(b, user_pk,
# TRANSFER
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([b.me], 100)],
asset=tx_create.asset)
asset_id=tx_create.id)
tx_transfer_signed = tx_transfer.sign([b.me_private, user_sk])
assert tx_transfer_signed.validate(b)
@ -397,18 +370,14 @@ def test_multiple_in_multiple_own_single_out_single_own_transfer(b, user_pk,
# owners_before
# Single output
# Single owners_after
@pytest.mark.bdb
@pytest.mark.usefixtures('inputs')
def test_muiltiple_in_mix_own_multiple_out_single_own_transfer(b, user_pk,
user_sk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
# CREATE divisible asset
asset = Asset(divisible=True)
tx_create = Transaction.create([b.me],
[([user_pk], 50),
([user_pk, b.me], 50)],
asset=asset)
tx_create = Transaction.create([b.me], [([user_pk], 50), ([user_pk, b.me], 50)])
tx_create_signed = tx_create.sign([b.me_private])
# create block
block = b.create_block([tx_create_signed])
@ -420,7 +389,7 @@ def test_muiltiple_in_mix_own_multiple_out_single_own_transfer(b, user_pk,
# TRANSFER
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([b.me], 100)],
asset=tx_create.asset)
asset_id=tx_create.id)
tx_transfer_signed = tx_transfer.sign([b.me_private, user_sk])
assert tx_transfer_signed.validate(b) == tx_transfer_signed
@ -442,18 +411,14 @@ def test_muiltiple_in_mix_own_multiple_out_single_own_transfer(b, user_pk,
# Multiple outputs
# Mix: one output with a single owners_after, one output with multiple
# owners_after
@pytest.mark.bdb
@pytest.mark.usefixtures('inputs')
def test_muiltiple_in_mix_own_multiple_out_mix_own_transfer(b, user_pk,
user_sk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
# CREATE divisible asset
asset = Asset(divisible=True)
tx_create = Transaction.create([b.me],
[([user_pk], 50),
([user_pk, b.me], 50)],
asset=asset)
tx_create = Transaction.create([b.me], [([user_pk], 50), ([user_pk, b.me], 50)])
tx_create_signed = tx_create.sign([b.me_private])
# create block
block = b.create_block([tx_create_signed])
@ -466,7 +431,7 @@ def test_muiltiple_in_mix_own_multiple_out_mix_own_transfer(b, user_pk,
# TRANSFER
tx_transfer = Transaction.transfer(tx_create.to_inputs(),
[([b.me], 50), ([b.me, user_pk], 50)],
asset=tx_create.asset)
asset_id=tx_create.id)
tx_transfer_signed = tx_transfer.sign([b.me_private, user_sk])
assert tx_transfer_signed.validate(b) == tx_transfer_signed
@ -493,19 +458,15 @@ def test_muiltiple_in_mix_own_multiple_out_mix_own_transfer(b, user_pk,
# Single owners_before
# Single output
# Single owners_after
@pytest.mark.bdb
@pytest.mark.usefixtures('inputs')
def test_multiple_in_different_transactions(b, user_pk, user_sk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
# CREATE divisible asset
# `b` creates a divisible asset and assigns 50 shares to `b` and
# 50 shares to `user_pk`
asset = Asset(divisible=True)
tx_create = Transaction.create([b.me],
[([user_pk], 50),
([b.me], 50)],
asset=asset)
tx_create = Transaction.create([b.me], [([user_pk], 50), ([b.me], 50)])
tx_create_signed = tx_create.sign([b.me_private])
# create block
block = b.create_block([tx_create_signed])
@ -521,7 +482,7 @@ def test_multiple_in_different_transactions(b, user_pk, user_sk):
# split across two different transactions
tx_transfer1 = Transaction.transfer(tx_create.to_inputs([1]),
[([user_pk], 50)],
asset=tx_create.asset)
asset_id=tx_create.id)
tx_transfer1_signed = tx_transfer1.sign([b.me_private])
# create block
block = b.create_block([tx_transfer1_signed])
@ -537,7 +498,7 @@ def test_multiple_in_different_transactions(b, user_pk, user_sk):
tx_transfer2 = Transaction.transfer(tx_create.to_inputs([0]) +
tx_transfer1.to_inputs([0]),
[([b.me], 100)],
asset=tx_create.asset)
asset_id=tx_create.id)
tx_transfer2_signed = tx_transfer2.sign([user_sk])
assert tx_transfer2_signed.validate(b) == tx_transfer2_signed
@ -554,15 +515,14 @@ def test_multiple_in_different_transactions(b, user_pk, user_sk):
# In a TRANSFER transaction of a divisible asset the amount being spent in the
# inputs needs to match the amount being sent in the outputs.
# In other words `amount_in_inputs - amount_in_outputs == 0`
@pytest.mark.bdb
@pytest.mark.usefixtures('inputs')
def test_amount_error_transfer(b, user_pk, user_sk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
from bigchaindb.common.exceptions import AmountError
# CREATE divisible asset
asset = Asset(divisible=True)
tx_create = Transaction.create([b.me], [([user_pk], 100)], asset=asset)
tx_create = Transaction.create([b.me], [([user_pk], 100)])
tx_create_signed = tx_create.sign([b.me_private])
# create block
block = b.create_block([tx_create_signed])
@ -575,7 +535,7 @@ def test_amount_error_transfer(b, user_pk, user_sk):
# TRANSFER
# output amount less than input amount
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([b.me], 50)],
asset=tx_create.asset)
asset_id=tx_create.id)
tx_transfer_signed = tx_transfer.sign([user_sk])
with pytest.raises(AmountError):
tx_transfer_signed.validate(b)
@ -583,13 +543,14 @@ def test_amount_error_transfer(b, user_pk, user_sk):
# TRANSFER
# output amount greater than input amount
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([b.me], 101)],
asset=tx_create.asset)
asset_id=tx_create.id)
tx_transfer_signed = tx_transfer.sign([user_sk])
with pytest.raises(AmountError):
tx_transfer_signed.validate(b)
@pytest.mark.skip(reason='Figure out how to handle this case')
@pytest.mark.bdb
@pytest.mark.usefixtures('inputs')
def test_threshold_same_public_key(b, user_pk, user_sk):
# If we try to fulfill a threshold condition where each subcondition has
@ -600,12 +561,9 @@ def test_threshold_same_public_key(b, user_pk, user_sk):
# that does not mean that the code shouldn't work.
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
# CREATE divisible asset
asset = Asset(divisible=True)
tx_create = Transaction.create([b.me], [([user_pk, user_pk], 100)],
asset=asset)
tx_create = Transaction.create([b.me], [([user_pk, user_pk], 100)])
tx_create_signed = tx_create.sign([b.me_private])
# create block
block = b.create_block([tx_create_signed])
@ -617,24 +575,19 @@ def test_threshold_same_public_key(b, user_pk, user_sk):
# TRANSFER
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([b.me], 100)],
asset=tx_create.asset)
asset_id=tx_create.id)
tx_transfer_signed = tx_transfer.sign([user_sk, user_sk])
assert tx_transfer_signed.validate(b) == tx_transfer_signed
@pytest.mark.bdb
@pytest.mark.usefixtures('inputs')
def test_sum_amount(b, user_pk, user_sk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
# CREATE divisible asset with 3 outputs with amount 1
asset = Asset(divisible=True)
tx_create = Transaction.create([b.me],
[([user_pk], 1),
([user_pk], 1),
([user_pk], 1)],
asset=asset)
tx_create = Transaction.create([b.me], [([user_pk], 1), ([user_pk], 1), ([user_pk], 1)])
tx_create_signed = tx_create.sign([b.me_private])
# create block
block = b.create_block([tx_create_signed])
@ -647,7 +600,7 @@ def test_sum_amount(b, user_pk, user_sk):
# create a transfer transaction with one output and check if the amount
# is 3
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([b.me], 3)],
asset=tx_create.asset)
asset_id=tx_create.id)
tx_transfer_signed = tx_transfer.sign([user_sk])
assert tx_transfer_signed.validate(b) == tx_transfer_signed
@ -655,15 +608,13 @@ def test_sum_amount(b, user_pk, user_sk):
assert tx_transfer_signed.outputs[0].amount == 3
@pytest.mark.bdb
@pytest.mark.usefixtures('inputs')
def test_divide(b, user_pk, user_sk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
# CREATE divisible asset with 1 output with amount 3
asset = Asset(divisible=True)
tx_create = Transaction.create([b.me], [([user_pk], 3)],
asset=asset)
tx_create = Transaction.create([b.me], [([user_pk], 3)])
tx_create_signed = tx_create.sign([b.me_private])
# create block
block = b.create_block([tx_create_signed])
@ -677,7 +628,7 @@ def test_divide(b, user_pk, user_sk):
# of each output is 1
tx_transfer = Transaction.transfer(tx_create.to_inputs(),
[([b.me], 1), ([b.me], 1), ([b.me], 1)],
asset=tx_create.asset)
asset_id=tx_create.id)
tx_transfer_signed = tx_transfer.sign([user_sk])
assert tx_transfer_signed.validate(b) == tx_transfer_signed
@ -687,16 +638,14 @@ def test_divide(b, user_pk, user_sk):
# Check that negative inputs are caught when creating a TRANSFER transaction
@pytest.mark.bdb
@pytest.mark.usefixtures('inputs')
def test_non_positive_amounts_on_transfer(b, user_pk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
from bigchaindb.common.exceptions import AmountError
# CREATE divisible asset with 1 output with amount 3
asset = Asset(divisible=True)
tx_create = Transaction.create([b.me], [([user_pk], 3)],
asset=asset)
tx_create = Transaction.create([b.me], [([user_pk], 3)])
tx_create_signed = tx_create.sign([b.me_private])
# create block
block = b.create_block([tx_create_signed])
@ -709,20 +658,18 @@ def test_non_positive_amounts_on_transfer(b, user_pk):
with pytest.raises(AmountError):
Transaction.transfer(tx_create.to_inputs(),
[([b.me], 4), ([b.me], -1)],
asset=tx_create.asset)
asset_id=tx_create.id)
# Check that negative inputs are caught when validating a TRANSFER transaction
@pytest.mark.bdb
@pytest.mark.usefixtures('inputs')
def test_non_positive_amounts_on_transfer_validate(b, user_pk, user_sk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
from bigchaindb.common.exceptions import AmountError
# CREATE divisible asset with 1 output with amount 3
asset = Asset(divisible=True)
tx_create = Transaction.create([b.me], [([user_pk], 3)],
asset=asset)
tx_create = Transaction.create([b.me], [([user_pk], 3)])
tx_create_signed = tx_create.sign([b.me_private])
# create block
block = b.create_block([tx_create_signed])
@ -736,7 +683,7 @@ def test_non_positive_amounts_on_transfer_validate(b, user_pk, user_sk):
# of each output is 1
tx_transfer = Transaction.transfer(tx_create.to_inputs(),
[([b.me], 4), ([b.me], 1)],
asset=tx_create.asset)
asset_id=tx_create.id)
tx_transfer.outputs[1].amount = -1
tx_transfer_signed = tx_transfer.sign([user_sk])
@ -745,33 +692,28 @@ def test_non_positive_amounts_on_transfer_validate(b, user_pk, user_sk):
# Check that negative inputs are caught when creating a CREATE transaction
@pytest.mark.bdb
@pytest.mark.usefixtures('inputs')
def test_non_positive_amounts_on_create(b, user_pk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
from bigchaindb.common.exceptions import AmountError
# CREATE divisible asset with 1 output with amount 3
asset = Asset(divisible=True)
with pytest.raises(AmountError):
Transaction.create([b.me], [([user_pk], -3)],
asset=asset)
Transaction.create([b.me], [([user_pk], -3)])
# Check that negative inputs are caught when validating a CREATE transaction
@pytest.mark.bdb
@pytest.mark.usefixtures('inputs')
def test_non_positive_amounts_on_create_validate(b, user_pk):
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
from bigchaindb.common.exceptions import AmountError
# CREATE divisible asset with 1 output with amount 3
asset = Asset(divisible=True)
tx_create = Transaction.create([b.me], [([user_pk], 3)],
asset=asset)
tx_create = Transaction.create([b.me], [([user_pk], 3)])
tx_create.outputs[0].amount = -3
with patch.object(Asset, 'validate_asset', return_value=None):
tx_create_signed = tx_create.sign([b.me_private])
tx_create_signed = tx_create.sign([b.me_private])
with pytest.raises(AmountError):
tx_create_signed.validate(b)

View File

View File

@ -0,0 +1,16 @@
def test_get_connection_returns_the_correct_instance():
from bigchaindb.backend import connect
from bigchaindb.backend.connection import Connection
from bigchaindb.backend.mongodb.connection import MongoDBConnection
config = {
'backend': 'mongodb',
'host': 'localhost',
'port': 27017,
'name': 'test'
}
conn = connect(**config)
assert isinstance(conn, Connection)
assert isinstance(conn, MongoDBConnection)

View File

@ -0,0 +1,156 @@
import pytest
from unittest.mock import patch
pytestmark = pytest.mark.bdb
def test_init_creates_db_tables_and_indexes():
import bigchaindb
from bigchaindb import backend
from bigchaindb.backend.schema import init_database
conn = backend.connect()
dbname = bigchaindb.config['database']['name']
# the db is set up by the fixture so we need to remove it
conn.conn.drop_database(dbname)
init_database()
collection_names = conn.conn[dbname].collection_names()
assert sorted(collection_names) == ['backlog', 'bigchain', 'votes']
indexes = conn.conn[dbname]['bigchain'].index_information().keys()
assert sorted(indexes) == ['_id_', 'asset_id', 'block_timestamp',
'transaction_id']
indexes = conn.conn[dbname]['backlog'].index_information().keys()
assert sorted(indexes) == ['_id_', 'assignee__transaction_timestamp']
indexes = conn.conn[dbname]['votes'].index_information().keys()
assert sorted(indexes) == ['_id_', 'block_and_voter']
def test_init_database_fails_if_db_exists():
import bigchaindb
from bigchaindb import backend
from bigchaindb.backend.schema import init_database
from bigchaindb.common import exceptions
conn = backend.connect()
dbname = bigchaindb.config['database']['name']
# The db is set up by the fixtures
assert dbname in conn.conn.database_names()
with pytest.raises(exceptions.DatabaseAlreadyExists):
init_database()
def test_create_tables():
import bigchaindb
from bigchaindb import backend
from bigchaindb.backend import schema
conn = backend.connect()
dbname = bigchaindb.config['database']['name']
# The db is set up by the fixtures so we need to remove it
conn.conn.drop_database(dbname)
schema.create_database(conn, dbname)
schema.create_tables(conn, dbname)
collection_names = conn.conn[dbname].collection_names()
assert sorted(collection_names) == ['backlog', 'bigchain', 'votes']
def test_create_secondary_indexes():
import bigchaindb
from bigchaindb import backend
from bigchaindb.backend import schema
conn = backend.connect()
dbname = bigchaindb.config['database']['name']
# The db is set up by the fixtures so we need to remove it
conn.conn.drop_database(dbname)
schema.create_database(conn, dbname)
schema.create_tables(conn, dbname)
schema.create_indexes(conn, dbname)
# Bigchain table
indexes = conn.conn[dbname]['bigchain'].index_information().keys()
assert sorted(indexes) == ['_id_', 'asset_id', 'block_timestamp',
'transaction_id']
# Backlog table
indexes = conn.conn[dbname]['backlog'].index_information().keys()
assert sorted(indexes) == ['_id_', 'assignee__transaction_timestamp']
# Votes table
indexes = conn.conn[dbname]['votes'].index_information().keys()
assert sorted(indexes) == ['_id_', 'block_and_voter']
def test_drop():
import bigchaindb
from bigchaindb import backend
from bigchaindb.backend import schema
conn = backend.connect()
dbname = bigchaindb.config['database']['name']
# The db is set up by fixtures
assert dbname in conn.conn.database_names()
schema.drop_database(conn, dbname)
assert dbname not in conn.conn.database_names()
def test_get_replica_set_name_not_enabled():
from pymongo.database import Database
from bigchaindb import backend
from bigchaindb.backend.mongodb.schema import _get_replica_set_name
from bigchaindb.common.exceptions import ConfigurationError
conn = backend.connect()
# no replSet option set
cmd_line_opts = {'argv': ['mongod', '--dbpath=/data'],
'ok': 1.0,
'parsed': {'storage': {'dbPath': '/data'}}}
with patch.object(Database, 'command', return_value=cmd_line_opts):
with pytest.raises(ConfigurationError):
_get_replica_set_name(conn)
def test_get_replica_set_name_command_line():
from pymongo.database import Database
from bigchaindb import backend
from bigchaindb.backend.mongodb.schema import _get_replica_set_name
conn = backend.connect()
# replSet option set through the command line
cmd_line_opts = {'argv': ['mongod', '--dbpath=/data', '--replSet=rs0'],
'ok': 1.0,
'parsed': {'replication': {'replSet': 'rs0'},
'storage': {'dbPath': '/data'}}}
with patch.object(Database, 'command', return_value=cmd_line_opts):
assert _get_replica_set_name(conn) == 'rs0'
def test_get_replica_set_name_config_file():
from pymongo.database import Database
from bigchaindb import backend
from bigchaindb.backend.mongodb.schema import _get_replica_set_name
conn = backend.connect()
# replSet option set through the config file
cmd_line_opts = {'argv': ['mongod', '--dbpath=/data', '--replSet=rs0'],
'ok': 1.0,
'parsed': {'replication': {'replSetName': 'rs0'},
'storage': {'dbPath': '/data'}}}
with patch.object(Database, 'command', return_value=cmd_line_opts):
assert _get_replica_set_name(conn) == 'rs0'

View File

@ -1,18 +1,39 @@
import time
import multiprocessing as mp
from threading import Thread
import pytest
import pytest
import rethinkdb as r
from bigchaindb.backend import connect
def test_get_connection_returns_the_correct_instance():
from bigchaindb.backend import connect
from bigchaindb.backend.connection import Connection
from bigchaindb.backend.rethinkdb.connection import RethinkDBConnection
config = {
'backend': 'rethinkdb',
'host': 'localhost',
'port': 28015,
'name': 'test'
}
conn = connect(**config)
assert isinstance(conn, Connection)
assert isinstance(conn, RethinkDBConnection)
def test_run_a_simple_query():
from bigchaindb.backend import connect
conn = connect()
query = r.expr('1')
assert conn.run(query) == '1'
def test_raise_exception_when_max_tries():
from bigchaindb.backend import connect
class MockQuery:
def run(self, conn):
raise r.ReqlDriverError('mock')
@ -24,7 +45,7 @@ def test_raise_exception_when_max_tries():
def test_reconnect_when_connection_lost():
import time
from bigchaindb.backend import connect
def raise_exception(*args, **kwargs):
raise r.ReqlDriverError('mock')
@ -44,9 +65,6 @@ def test_reconnect_when_connection_lost():
def test_changefeed_reconnects_when_connection_lost(monkeypatch):
import time
import multiprocessing as mp
from bigchaindb.backend.changefeed import ChangeFeed
from bigchaindb.backend.rethinkdb.changefeed import RethinkDBChangeFeed
@ -64,7 +82,8 @@ def test_changefeed_reconnects_when_connection_lost(monkeypatch):
if self.tries == 1:
raise r.ReqlDriverError('mock')
elif self.tries == 2:
return {'new_val': {'fact': 'A group of cats is called a clowder.'},
return {'new_val': {'fact':
'A group of cats is called a clowder.'},
'old_val': None}
if self.tries == 3:
raise r.ReqlDriverError('mock')

View File

@ -6,7 +6,7 @@ from bigchaindb import backend
from bigchaindb.backend.rethinkdb import schema
@pytest.mark.usefixtures('setup_database')
@pytest.mark.bdb
def test_init_creates_db_tables_and_indexes():
from bigchaindb.backend.schema import init_database
conn = backend.connect()
@ -28,7 +28,7 @@ def test_init_creates_db_tables_and_indexes():
'assignee__transaction_timestamp')) is True
@pytest.mark.usefixtures('setup_database')
@pytest.mark.bdb
def test_init_database_fails_if_db_exists():
from bigchaindb.backend.schema import init_database
from bigchaindb.common import exceptions
@ -43,14 +43,13 @@ def test_init_database_fails_if_db_exists():
init_database()
def test_create_database():
def test_create_database(not_yet_created_db):
conn = backend.connect()
dbname = bigchaindb.config['database']['name']
schema.create_database(conn, dbname)
assert conn.run(r.db_list().contains(dbname)) is True
schema.create_database(conn, not_yet_created_db)
assert conn.run(r.db_list().contains(not_yet_created_db)) is True
@pytest.mark.usefixtures('setup_database')
@pytest.mark.bdb
def test_create_tables():
conn = backend.connect()
dbname = bigchaindb.config['database']['name']
@ -67,7 +66,7 @@ def test_create_tables():
assert len(conn.run(r.db(dbname).table_list())) == 3
@pytest.mark.usefixtures('setup_database')
@pytest.mark.bdb
def test_create_secondary_indexes():
conn = backend.connect()
dbname = bigchaindb.config['database']['name']
@ -96,29 +95,17 @@ def test_create_secondary_indexes():
'block_and_voter')) is True
@pytest.mark.usefixtures('setup_database')
def test_drop():
def test_drop(dummy_db):
conn = backend.connect()
dbname = bigchaindb.config['database']['name']
# The db is set up by fixtures
assert conn.run(r.db_list().contains(dbname)) is True
schema.drop_database(conn, dbname)
assert conn.run(r.db_list().contains(dbname)) is False
assert conn.run(r.db_list().contains(dummy_db)) is True
schema.drop_database(conn, dummy_db)
assert conn.run(r.db_list().contains(dummy_db)) is False
@pytest.mark.usefixtures('setup_database')
def test_drop_non_existent_db_raises_an_error():
def test_drop_non_existent_db_raises_an_error(dummy_db):
from bigchaindb.common import exceptions
conn = backend.connect()
dbname = bigchaindb.config['database']['name']
# The db is set up by fixtures
assert conn.run(r.db_list().contains(dbname)) is True
schema.drop_database(conn, dbname)
assert conn.run(r.db_list().contains(dummy_db)) is True
schema.drop_database(conn, dummy_db)
with pytest.raises(exceptions.DatabaseDoesNotExist):
schema.drop_database(conn, dbname)
schema.drop_database(conn, dummy_db)

View File

@ -1,23 +1,6 @@
import pytest
def test_get_connection_returns_the_correct_instance():
from bigchaindb.backend import connect
from bigchaindb.backend.connection import Connection
from bigchaindb.backend.rethinkdb.connection import RethinkDBConnection
config = {
'backend': 'rethinkdb',
'host': 'localhost',
'port': 28015,
'name': 'test'
}
conn = connect(**config)
assert isinstance(conn, Connection)
assert isinstance(conn, RethinkDBConnection)
def test_get_connection_raises_a_configuration_error(monkeypatch):
from bigchaindb.common.exceptions import ConfigurationError
from bigchaindb.backend import connect
@ -28,6 +11,7 @@ def test_get_connection_raises_a_configuration_error(monkeypatch):
with pytest.raises(ConfigurationError):
# We need to force a misconfiguration here
monkeypatch.setattr('bigchaindb.backend.connection.BACKENDS',
{'catsandra': 'bigchaindb.backend.meowmeow.Catsandra'})
{'catsandra':
'bigchaindb.backend.meowmeow.Catsandra'})
connect('catsandra', 'localhost', '1337', 'mydb')

View File

@ -101,7 +101,7 @@ def test_bigchain_run_start_assume_yes_create_default_config(monkeypatch, mock_p
# TODO Please beware, that if debugging, the "-s" switch for pytest will
# interfere with capsys.
# See related issue: https://github.com/pytest-dev/pytest/issues/128
@pytest.mark.usefixtures('restore_config')
@pytest.mark.usefixtures('ignore_local_config_file')
def test_bigchain_show_config(capsys):
from bigchaindb import config
from bigchaindb.commands.bigchain import run_show_config
@ -229,7 +229,7 @@ def test_run_configure_when_config_does_exist(monkeypatch,
@patch('bigchaindb.common.crypto.generate_key_pair',
return_value=('private_key', 'public_key'))
@pytest.mark.usefixtures('restore_config')
@pytest.mark.usefixtures('ignore_local_config_file')
def test_allow_temp_keypair_generates_one_on_the_fly(mock_gen_keypair,
mock_processes_start,
mock_db_init_with_existing_db):
@ -247,7 +247,7 @@ def test_allow_temp_keypair_generates_one_on_the_fly(mock_gen_keypair,
@patch('bigchaindb.common.crypto.generate_key_pair',
return_value=('private_key', 'public_key'))
@pytest.mark.usefixtures('restore_config')
@pytest.mark.usefixtures('ignore_local_config_file')
def test_allow_temp_keypair_doesnt_override_if_keypair_found(mock_gen_keypair,
mock_processes_start,
mock_db_init_with_existing_db):

View File

@ -14,12 +14,19 @@ USER3_PUBLIC_KEY = 'Gbrg7JtxdjedQRmr81ZZbh1BozS7fBW88ZyxNDy7WLNC'
CC_FULFILLMENT_URI = 'cf:0:'
CC_CONDITION_URI = 'cc:0:3:47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU:0'
ASSET_DEFINITION = {
'data': {
'definition': 'Asset definition'
}
}
ASSET_LINK = {
'id': 'a' * 64
}
DATA = {
'msg': 'Hello BigchainDB!'
}
DATA_ID = '872fa6e6f46246cd44afdb2ee9cfae0e72885fb0910e2bcf9a5a2a4eadb417b8'
UUID4 = 'dc568f27-a113-46b4-9bd4-43015859e3e3'
@pytest.fixture
@ -121,25 +128,25 @@ def user2_output(user2_Ed25519, user2_pub):
return Output(user2_Ed25519, [user2_pub])
@pytest.fixture
def asset_definition():
return ASSET_DEFINITION
@pytest.fixture
def asset_link():
return ASSET_LINK
@pytest.fixture
def data():
return DATA
@pytest.fixture
def data_id():
return DATA_ID
@pytest.fixture
def uuid4():
return UUID4
@pytest.fixture
def utx(user_input, user_output):
from bigchaindb.common.transaction import Transaction, Asset
return Transaction(Transaction.CREATE, Asset(), [user_input],
from bigchaindb.common.transaction import Transaction
return Transaction(Transaction.CREATE, {'data': None}, [user_input],
[user_output])
@ -151,12 +158,12 @@ def tx(utx, user_priv):
@pytest.fixture
def transfer_utx(user_output, user2_output, utx):
from bigchaindb.common.transaction import (Input, TransactionLink,
Transaction, Asset)
Transaction)
user_output = user_output.to_dict()
input = Input(utx.outputs[0].fulfillment,
user_output['public_keys'],
TransactionLink(utx.id, 0))
return Transaction('TRANSFER', Asset(), [input], [user2_output])
return Transaction('TRANSFER', {'id': utx.id}, [input], [user2_output])
@pytest.fixture

View File

@ -1,92 +0,0 @@
from pytest import raises
def test_asset_default_values():
from bigchaindb.common.transaction import Asset
asset = Asset()
assert asset.data is None
assert asset.data_id
assert asset.divisible is False
assert asset.updatable is False
assert asset.refillable is False
def test_asset_creation_with_data(data):
from bigchaindb.common.transaction import Asset
asset = Asset(data)
assert asset.data == data
def test_asset_invalid_asset_initialization():
from bigchaindb.common.transaction import Asset
# check types
with raises(TypeError):
Asset(data='some wrong type')
with raises(TypeError):
Asset(divisible=1)
with raises(TypeError):
Asset(refillable=1)
with raises(TypeError):
Asset(updatable=1)
# check for features that are not yet implemented
with raises(NotImplementedError):
Asset(updatable=True)
with raises(NotImplementedError):
Asset(refillable=True)
def test_invalid_asset_comparison(data, data_id):
from bigchaindb.common.transaction import Asset
assert Asset(data, data_id) != 'invalid comparison'
def test_asset_serialization(data, data_id):
from bigchaindb.common.transaction import Asset
expected = {
'id': data_id,
'divisible': False,
'updatable': False,
'refillable': False,
'data': data,
}
asset = Asset(data, data_id)
assert asset.to_dict() == expected
def test_asset_deserialization(data, data_id):
from bigchaindb.common.transaction import Asset
asset_dict = {
'id': data_id,
'divisible': False,
'updatable': False,
'refillable': False,
'data': data,
}
asset = Asset.from_dict(asset_dict)
expected = Asset(data, data_id)
assert asset == expected
def test_validate_asset():
from bigchaindb.common.transaction import Asset
from bigchaindb.common.exceptions import AmountError
# test amount errors
asset = Asset(divisible=False)
with raises(AmountError):
asset.validate_asset(amount=2)
asset = Asset(divisible=True)
with raises(AmountError):
asset.validate_asset(amount=1)
asset = Asset()
with raises(TypeError):
asset.validate_asset(amount='a')

View File

@ -1,5 +1,4 @@
from pytest import raises
from unittest.mock import patch
def test_input_serialization(ffill_uri, user_pub):
@ -145,9 +144,14 @@ def test_output_hashlock_deserialization():
def test_invalid_output_initialization(cond_uri, user_pub):
from bigchaindb.common.transaction import Output
from bigchaindb.common.exceptions import AmountError
with raises(TypeError):
Output(cond_uri, user_pub)
with raises(TypeError):
Output(cond_uri, [user_pub], 'amount')
with raises(AmountError):
Output(cond_uri, [user_pub], 0)
def test_generate_output_split_half_recursive(user_pub, user2_pub,
@ -241,58 +245,50 @@ def test_generate_output_invalid_parameters(user_pub, user2_pub,
Output.generate([[user_pub]], 1)
def test_invalid_transaction_initialization():
from bigchaindb.common.transaction import Transaction, Asset
def test_invalid_transaction_initialization(asset_definition):
from bigchaindb.common.transaction import Transaction
with raises(ValueError):
Transaction(operation='invalid operation', asset=Asset())
Transaction(operation='invalid operation', asset=asset_definition)
with raises(TypeError):
Transaction(operation='CREATE', asset='invalid asset')
with raises(TypeError):
Transaction(operation='TRANSFER', asset={})
with raises(TypeError):
Transaction(
operation='CREATE',
asset=Asset(),
asset=asset_definition,
outputs='invalid outputs'
)
with raises(TypeError):
Transaction(
operation='CREATE',
asset=Asset(),
asset=asset_definition,
outputs=[],
inputs='invalid inputs'
)
with raises(TypeError):
Transaction(
operation='CREATE',
asset=Asset(),
asset=asset_definition,
outputs=[],
inputs=[],
metadata='invalid metadata'
)
def test_create_default_asset_on_tx_initialization():
from bigchaindb.common.transaction import Transaction, Asset
from bigchaindb.common.exceptions import ValidationError
from .util import validate_transaction_model
def test_create_default_asset_on_tx_initialization(asset_definition):
from bigchaindb.common.transaction import Transaction
with patch.object(Asset, 'validate_asset', return_value=None):
tx = Transaction(Transaction.CREATE, None)
expected = Asset()
expected = {'data': None}
tx = Transaction(Transaction.CREATE, asset=expected)
asset = tx.asset
expected.data_id = None
asset.data_id = None
assert asset == expected
# Fails because no asset hash
with raises(ValidationError):
validate_transaction_model(tx)
def test_transaction_serialization(user_input, user_output, data, data_id):
from bigchaindb.common.transaction import Transaction, Asset
from bigchaindb.common.exceptions import ValidationError
def test_transaction_serialization(user_input, user_output, data):
from bigchaindb.common.transaction import Transaction
from .util import validate_transaction_model
tx_id = 'l0l'
@ -307,32 +303,23 @@ def test_transaction_serialization(user_input, user_output, data, data_id):
'operation': Transaction.CREATE,
'metadata': None,
'asset': {
'id': data_id,
'divisible': False,
'updatable': False,
'refillable': False,
'data': data,
}
}
tx = Transaction(Transaction.CREATE, Asset(data, data_id), [user_input],
tx = Transaction(Transaction.CREATE, {'data': data}, [user_input],
[user_output])
tx_dict = tx.to_dict()
tx_dict['id'] = tx_id
tx_dict['asset']['id'] = data_id
assert tx_dict == expected
# Fails because asset id is not a uuid4
with raises(ValidationError):
validate_transaction_model(tx)
def test_transaction_deserialization(user_input, user_output, data, uuid4):
from bigchaindb.common.transaction import Transaction, Asset
def test_transaction_deserialization(user_input, user_output, data):
from bigchaindb.common.transaction import Transaction
from .util import validate_transaction_model
expected_asset = Asset(data, uuid4)
expected_asset = {'data': data}
expected = Transaction(Transaction.CREATE, expected_asset, [user_input],
[user_output], None, Transaction.VERSION)
@ -345,10 +332,6 @@ def test_transaction_deserialization(user_input, user_output, data, uuid4):
'operation': Transaction.CREATE,
'metadata': None,
'asset': {
'id': uuid4,
'divisible': False,
'updatable': False,
'refillable': False,
'data': data,
}
}
@ -454,92 +437,28 @@ def test_cast_transaction_link_to_boolean():
assert bool(TransactionLink(False, False)) is True
def test_asset_link_serialization():
from bigchaindb.common.transaction import AssetLink
def test_add_input_to_tx(user_input, asset_definition):
from bigchaindb.common.transaction import Transaction
data_id = 'a asset id'
expected = {
'id': data_id,
}
asset_link = AssetLink(data_id)
assert asset_link.to_dict() == expected
def test_asset_link_serialization_with_empty_payload():
from bigchaindb.common.transaction import AssetLink
expected = None
asset_link = AssetLink()
assert asset_link.to_dict() == expected
def test_asset_link_deserialization():
from bigchaindb.common.transaction import AssetLink
data_id = 'a asset id'
expected = AssetLink(data_id)
asset_link = {
'id': data_id
}
asset_link = AssetLink.from_dict(asset_link)
assert asset_link == expected
def test_asset_link_deserialization_with_empty_payload():
from bigchaindb.common.transaction import AssetLink
expected = AssetLink()
asset_link = AssetLink.from_dict(None)
assert asset_link == expected
def test_cast_asset_link_to_boolean():
from bigchaindb.common.transaction import AssetLink
assert bool(AssetLink()) is False
assert bool(AssetLink('a')) is True
assert bool(AssetLink(False)) is True
def test_eq_asset_link():
from bigchaindb.common.transaction import AssetLink
asset_id_1 = 'asset_1'
asset_id_2 = 'asset_2'
assert AssetLink(asset_id_1) == AssetLink(asset_id_1)
assert AssetLink(asset_id_1) != AssetLink(asset_id_2)
def test_add_input_to_tx(user_input):
from bigchaindb.common.transaction import Transaction, Asset
with patch.object(Asset, 'validate_asset', return_value=None):
tx = Transaction(Transaction.CREATE, Asset(), [], [])
tx = Transaction(Transaction.CREATE, asset_definition, [], [])
tx.add_input(user_input)
assert len(tx.inputs) == 1
def test_add_input_to_tx_with_invalid_parameters():
from bigchaindb.common.transaction import Transaction, Asset
def test_add_input_to_tx_with_invalid_parameters(asset_definition):
from bigchaindb.common.transaction import Transaction
tx = Transaction(Transaction.CREATE, asset_definition)
with patch.object(Asset, 'validate_asset', return_value=None):
tx = Transaction(Transaction.CREATE, Asset())
with raises(TypeError):
tx.add_input('somewronginput')
def test_add_output_to_tx(user_output):
from bigchaindb.common.transaction import Transaction, Asset
def test_add_output_to_tx(user_output, asset_definition):
from bigchaindb.common.transaction import Transaction
from .util import validate_transaction_model
with patch.object(Asset, 'validate_asset', return_value=None):
tx = Transaction(Transaction.CREATE, Asset())
tx = Transaction(Transaction.CREATE, asset_definition)
tx.add_output(user_output)
assert len(tx.outputs) == 1
@ -547,11 +466,10 @@ def test_add_output_to_tx(user_output):
validate_transaction_model(tx)
def test_add_output_to_tx_with_invalid_parameters():
from bigchaindb.common.transaction import Transaction, Asset
def test_add_output_to_tx_with_invalid_parameters(asset_definition):
from bigchaindb.common.transaction import Transaction
tx = Transaction(Transaction.CREATE, asset_definition, [], [])
with patch.object(Asset, 'validate_asset', return_value=None):
tx = Transaction(Transaction.CREATE, Asset(), [], [])
with raises(TypeError):
tx.add_output('somewronginput')
@ -563,13 +481,14 @@ def test_sign_with_invalid_parameters(utx, user_priv):
utx.sign(user_priv)
def test_validate_tx_simple_create_signature(user_input, user_output, user_priv):
def test_validate_tx_simple_create_signature(user_input, user_output, user_priv,
asset_definition):
from copy import deepcopy
from bigchaindb.common.crypto import PrivateKey
from bigchaindb.common.transaction import Transaction, Asset
from bigchaindb.common.transaction import Transaction
from .util import validate_transaction_model
tx = Transaction(Transaction.CREATE, Asset(), [user_input], [user_output])
tx = Transaction(Transaction.CREATE, asset_definition, [user_input], [user_output])
expected = deepcopy(user_output)
expected.fulfillment.sign(str(tx).encode(), PrivateKey(user_priv))
tx.sign([user_priv])
@ -621,14 +540,15 @@ def test_validate_input_with_invalid_parameters(utx):
assert not valid
def test_validate_multiple_inputs(user_input, user_output, user_priv):
def test_validate_multiple_inputs(user_input, user_output, user_priv,
asset_definition):
from copy import deepcopy
from bigchaindb.common.crypto import PrivateKey
from bigchaindb.common.transaction import Transaction, Asset
from bigchaindb.common.transaction import Transaction
from .util import validate_transaction_model
tx = Transaction(Transaction.CREATE, Asset(divisible=True),
tx = Transaction(Transaction.CREATE, asset_definition,
[user_input, deepcopy(user_input)],
[user_output, deepcopy(user_output)])
@ -659,14 +579,16 @@ def test_validate_tx_threshold_create_signature(user_user2_threshold_input,
user_pub,
user2_pub,
user_priv,
user2_priv):
user2_priv,
asset_definition):
from copy import deepcopy
from bigchaindb.common.crypto import PrivateKey
from bigchaindb.common.transaction import Transaction, Asset
from bigchaindb.common.transaction import Transaction
from .util import validate_transaction_model
tx = Transaction(Transaction.CREATE, Asset(), [user_user2_threshold_input],
tx = Transaction(Transaction.CREATE, asset_definition,
[user_user2_threshold_input],
[user_user2_threshold_output])
expected = deepcopy(user_user2_threshold_output)
expected.fulfillment.subconditions[0]['body'].sign(str(tx).encode(),
@ -685,14 +607,15 @@ def test_validate_tx_threshold_create_signature(user_user2_threshold_input,
def test_multiple_input_validation_of_transfer_tx(user_input, user_output,
user_priv, user2_pub,
user2_priv, user3_pub,
user3_priv):
user3_priv,
asset_definition):
from copy import deepcopy
from bigchaindb.common.transaction import (Transaction, TransactionLink,
Input, Output, Asset)
Input, Output)
from cryptoconditions import Ed25519Fulfillment
from .util import validate_transaction_model
tx = Transaction(Transaction.CREATE, Asset(divisible=True),
tx = Transaction(Transaction.CREATE, asset_definition,
[user_input, deepcopy(user_input)],
[user_output, deepcopy(user_output)])
tx.sign([user_priv])
@ -702,7 +625,7 @@ def test_multiple_input_validation_of_transfer_tx(user_input, user_output,
for index, cond in enumerate(tx.outputs)]
outputs = [Output(Ed25519Fulfillment(public_key=user3_pub), [user3_pub]),
Output(Ed25519Fulfillment(public_key=user3_pub), [user3_pub])]
transfer_tx = Transaction('TRANSFER', tx.asset, inputs, outputs)
transfer_tx = Transaction('TRANSFER', {'id': tx.id}, inputs, outputs)
transfer_tx = transfer_tx.sign([user_priv])
assert transfer_tx.inputs_valid(tx.outputs) is True
@ -732,18 +655,14 @@ def test_validate_inputs_of_transfer_tx_with_invalid_params(
transfer_tx.inputs_valid([utx.outputs[0]])
def test_create_create_transaction_single_io(user_output, user_pub, data, uuid4):
from bigchaindb.common.transaction import Transaction, Asset
def test_create_create_transaction_single_io(user_output, user_pub, data):
from bigchaindb.common.transaction import Transaction
from .util import validate_transaction_model
expected = {
'outputs': [user_output.to_dict()],
'metadata': data,
'asset': {
'id': uuid4,
'divisible': False,
'updatable': False,
'refillable': False,
'data': data,
},
'inputs': [
@ -759,8 +678,8 @@ def test_create_create_transaction_single_io(user_output, user_pub, data, uuid4)
'version': 1,
}
asset = Asset(data, uuid4)
tx = Transaction.create([user_pub], [([user_pub], 1)], data, asset)
tx = Transaction.create([user_pub], [([user_pub], 1)], metadata=data,
asset=data)
tx_dict = tx.to_dict()
tx_dict['inputs'][0]['fulfillment'] = None
tx_dict.pop('id')
@ -770,17 +689,18 @@ def test_create_create_transaction_single_io(user_output, user_pub, data, uuid4)
validate_transaction_model(tx)
def test_validate_single_io_create_transaction(user_pub, user_priv, data):
from bigchaindb.common.transaction import Transaction, Asset
def test_validate_single_io_create_transaction(user_pub, user_priv, data,
asset_definition):
from bigchaindb.common.transaction import Transaction
tx = Transaction.create([user_pub], [([user_pub], 1)], data, Asset())
tx = Transaction.create([user_pub], [([user_pub], 1)], metadata=data)
tx = tx.sign([user_priv])
assert tx.inputs_valid() is True
def test_create_create_transaction_multiple_io(user_output, user2_output, user_pub,
user2_pub):
from bigchaindb.common.transaction import Transaction, Asset, Input
user2_pub, asset_definition):
from bigchaindb.common.transaction import Transaction, Input
# a fulfillment for a create transaction with multiple `owners_before`
# is a fulfillment for an implicit threshold condition with
@ -795,10 +715,8 @@ def test_create_create_transaction_multiple_io(user_output, user2_output, user_p
'operation': 'CREATE',
'version': 1
}
asset = Asset(divisible=True)
tx = Transaction.create([user_pub, user2_pub],
[([user_pub], 1), ([user2_pub], 1)],
asset=asset,
metadata={'message': 'hello'}).to_dict()
tx.pop('id')
tx.pop('asset')
@ -807,14 +725,14 @@ def test_create_create_transaction_multiple_io(user_output, user2_output, user_p
def test_validate_multiple_io_create_transaction(user_pub, user_priv,
user2_pub, user2_priv):
from bigchaindb.common.transaction import Transaction, Asset
user2_pub, user2_priv,
asset_definition):
from bigchaindb.common.transaction import Transaction
from .util import validate_transaction_model
tx = Transaction.create([user_pub, user2_pub],
[([user_pub], 1), ([user2_pub], 1)],
metadata={'message': 'hello'},
asset=Asset(divisible=True))
metadata={'message': 'hello'})
tx = tx.sign([user_priv, user2_priv])
assert tx.inputs_valid() is True
@ -823,18 +741,13 @@ def test_validate_multiple_io_create_transaction(user_pub, user_priv,
def test_create_create_transaction_threshold(user_pub, user2_pub, user3_pub,
user_user2_threshold_output,
user_user2_threshold_input, data,
uuid4):
from bigchaindb.common.transaction import Transaction, Asset
user_user2_threshold_input, data):
from bigchaindb.common.transaction import Transaction
expected = {
'outputs': [user_user2_threshold_output.to_dict()],
'metadata': data,
'asset': {
'id': uuid4,
'divisible': False,
'updatable': False,
'refillable': False,
'data': data,
},
'inputs': [
@ -849,9 +762,8 @@ def test_create_create_transaction_threshold(user_pub, user2_pub, user3_pub,
'operation': 'CREATE',
'version': 1
}
asset = Asset(data, uuid4)
tx = Transaction.create([user_pub], [([user_pub, user2_pub], 1)],
data, asset)
metadata=data, asset=data)
tx_dict = tx.to_dict()
tx_dict.pop('id')
tx_dict['inputs'][0]['fulfillment'] = None
@ -860,12 +772,12 @@ def test_create_create_transaction_threshold(user_pub, user2_pub, user3_pub,
def test_validate_threshold_create_transaction(user_pub, user_priv, user2_pub,
data):
from bigchaindb.common.transaction import Transaction, Asset
data, asset_definition):
from bigchaindb.common.transaction import Transaction
from .util import validate_transaction_model
tx = Transaction.create([user_pub], [([user_pub, user2_pub], 1)],
data, Asset())
metadata=data)
tx = tx.sign([user_priv])
assert tx.inputs_valid() is True
@ -887,6 +799,13 @@ def test_create_create_transaction_with_invalid_parameters(user_pub):
Transaction.create([user_pub], [user_pub])
with raises(ValueError):
Transaction.create([user_pub], [([user_pub],)])
with raises(TypeError):
Transaction.create([user_pub], [([user_pub], 1)],
metadata='not a dict or none')
with raises(TypeError):
Transaction.create([user_pub],
[([user_pub], 1)],
asset='not a dict or none')
def test_outputs_to_inputs(tx):
@ -900,10 +819,10 @@ def test_outputs_to_inputs(tx):
def test_create_transfer_transaction_single_io(tx, user_pub, user2_pub,
user2_output, user_priv, uuid4):
user2_output, user_priv):
from copy import deepcopy
from bigchaindb.common.crypto import PrivateKey
from bigchaindb.common.transaction import Transaction, Asset
from bigchaindb.common.transaction import Transaction
from bigchaindb.common.util import serialize
from .util import validate_transaction_model
@ -911,7 +830,7 @@ def test_create_transfer_transaction_single_io(tx, user_pub, user2_pub,
'outputs': [user2_output.to_dict()],
'metadata': None,
'asset': {
'id': uuid4,
'id': tx.id,
},
'inputs': [
{
@ -929,8 +848,8 @@ def test_create_transfer_transaction_single_io(tx, user_pub, user2_pub,
'version': 1
}
inputs = tx.to_inputs([0])
asset = Asset(None, uuid4)
transfer_tx = Transaction.transfer(inputs, [([user2_pub], 1)], asset=asset)
transfer_tx = Transaction.transfer(inputs, [([user2_pub], 1)],
asset_id=tx.id)
transfer_tx = transfer_tx.sign([user_priv])
transfer_tx = transfer_tx.to_dict()
@ -951,12 +870,12 @@ def test_create_transfer_transaction_single_io(tx, user_pub, user2_pub,
def test_create_transfer_transaction_multiple_io(user_pub, user_priv,
user2_pub, user2_priv,
user3_pub, user2_output):
from bigchaindb.common.transaction import Transaction, Asset
user3_pub, user2_output,
asset_definition):
from bigchaindb.common.transaction import Transaction
asset = Asset(divisible=True)
tx = Transaction.create([user_pub], [([user_pub], 1), ([user2_pub], 1)],
asset=asset, metadata={'message': 'hello'})
metadata={'message': 'hello'})
tx = tx.sign([user_priv])
expected = {
@ -989,7 +908,7 @@ def test_create_transfer_transaction_multiple_io(user_pub, user_priv,
transfer_tx = Transaction.transfer(tx.to_inputs(),
[([user2_pub], 1), ([user2_pub], 1)],
asset=tx.asset)
asset_id=tx.id)
transfer_tx = transfer_tx.sign([user_priv, user2_priv])
assert len(transfer_tx.inputs) == 2
@ -1006,36 +925,40 @@ def test_create_transfer_transaction_multiple_io(user_pub, user_priv,
assert expected == transfer_tx
def test_create_transfer_with_invalid_parameters(user_pub):
from bigchaindb.common.transaction import Transaction, Asset
def test_create_transfer_with_invalid_parameters(tx, user_pub):
from bigchaindb.common.transaction import Transaction
with raises(TypeError):
Transaction.transfer({}, [], Asset())
Transaction.transfer({}, [], tx.id)
with raises(ValueError):
Transaction.transfer([], [], Asset())
Transaction.transfer([], [], tx.id)
with raises(TypeError):
Transaction.transfer(['fulfillment'], {}, Asset())
Transaction.transfer(['fulfillment'], {}, tx.id)
with raises(ValueError):
Transaction.transfer(['fulfillment'], [], Asset())
Transaction.transfer(['fulfillment'], [], tx.id)
with raises(ValueError):
Transaction.transfer(['fulfillment'], [user_pub], Asset())
Transaction.transfer(['fulfillment'], [user_pub], tx.id)
with raises(ValueError):
Transaction.transfer(['fulfillment'], [([user_pub],)], Asset())
Transaction.transfer(['fulfillment'], [([user_pub],)], tx.id)
with raises(TypeError):
Transaction.transfer(['fulfillment'], [([user_pub], 1)],
tx.id, metadata='not a dict or none')
with raises(TypeError):
Transaction.transfer(['fulfillment'], [([user_pub], 1)],
['not a string'])
def test_cant_add_empty_output():
from bigchaindb.common.transaction import Transaction, Asset
from bigchaindb.common.transaction import Transaction
tx = Transaction(Transaction.CREATE, None)
with patch.object(Asset, 'validate_asset', return_value=None):
tx = Transaction(Transaction.CREATE, None)
with raises(TypeError):
tx.add_output(None)
def test_cant_add_empty_input():
from bigchaindb.common.transaction import Transaction, Asset
from bigchaindb.common.transaction import Transaction
tx = Transaction(Transaction.CREATE, None)
with patch.object(Asset, 'validate_asset', return_value=None):
tx = Transaction(Transaction.CREATE, None)
with raises(TypeError):
tx.add_input(None)

View File

@ -8,24 +8,15 @@ Tasks:
import os
import copy
import random
import pytest
from bigchaindb.common import crypto
TEST_DB_NAME = 'bigchain_test'
USER2_SK, USER2_PK = crypto.generate_key_pair()
DB_NAME = 'bigchain_test_{}'.format(os.getpid())
CONFIG = {
'database': {
'name': DB_NAME,
},
'keypair': {
'private': '31Lb1ZGKTyHnmVK3LUMrAUrPNfd4sE2YyBt3UA4A25aA',
'public': '4XYfCbabAWVUCbjTmRTFEu2sc3dFEdkse4r6X498B1s8',
}
}
# Test user. inputs will be created for this user. Cryptography Keys
USER_PRIVATE_KEY = '8eJ8q9ZQpReWyQT5aFCiwtZ5wDZC4eDnCen88p3tQ6ie'
@ -55,6 +46,142 @@ def pytest_ignore_collect(path, config):
return True
def pytest_configure(config):
config.addinivalue_line(
'markers',
'bdb(): Mark the test as needing BigchainDB, i.e. a database with '
'the three tables: "backlog", "bigchain", "votes". BigchainDB will '
'be configured such that the database and tables are available for an '
'entire test session. For distributed tests, the database name will '
'be suffixed with the process identifier, e.g.: "bigchain_test_gw0", '
'to ensure that each process session has its own separate database.'
)
config.addinivalue_line(
'markers',
'genesis(): Mark the test as needing a genesis block in place. The '
'prerequisite steps of configuration and database setup are taken '
'care of at session scope (if needed), prior to creating the genesis '
'block. The genesis block has function scope: it is destroyed after '
'each test function/method.'
)
@pytest.fixture(autouse=True)
def _bdb_marker(request):
if request.keywords.get('bdb', None):
request.getfixturevalue('_bdb')
@pytest.fixture(autouse=True)
def _genesis_marker(request):
if request.keywords.get('genesis', None):
request.getfixturevalue('_genesis')
@pytest.fixture(autouse=True)
def _restore_config(_configure_bigchaindb):
from bigchaindb import config, config_utils
config_before_test = copy.deepcopy(config)
yield
config_utils.set_config(config_before_test)
@pytest.fixture
def _restore_dbs(request):
from bigchaindb.backend import connect, schema
from bigchaindb.common.exceptions import DatabaseDoesNotExist
from .utils import list_dbs
conn = connect()
dbs_before_test = list_dbs(conn)
yield
dbs_after_test = list_dbs(conn)
dbs_to_delete = (
db for db in set(dbs_after_test) - set(dbs_before_test)
if TEST_DB_NAME not in db
)
print(dbs_to_delete)
for db in dbs_to_delete:
try:
schema.drop_database(conn, db)
except DatabaseDoesNotExist:
pass
@pytest.fixture(scope='session')
def _configure_bigchaindb(request):
from bigchaindb import config_utils
test_db_name = TEST_DB_NAME
# Put a suffix like _gw0, _gw1 etc on xdist processes
xdist_suffix = getattr(request.config, 'slaveinput', {}).get('slaveid')
if xdist_suffix:
test_db_name = '{}_{}'.format(TEST_DB_NAME, xdist_suffix)
config = {
'database': {
'name': test_db_name,
'backend': request.config.getoption('--database-backend'),
},
'keypair': {
'private': '31Lb1ZGKTyHnmVK3LUMrAUrPNfd4sE2YyBt3UA4A25aA',
'public': '4XYfCbabAWVUCbjTmRTFEu2sc3dFEdkse4r6X498B1s8',
}
}
# FIXME
if config['database']['backend'] == 'mongodb':
# not a great way to do this
config['database']['port'] = 27017
config_utils.set_config(config)
@pytest.fixture(scope='session')
def _setup_database(_configure_bigchaindb):
from bigchaindb import config
from bigchaindb.backend import connect, schema
from bigchaindb.common.exceptions import DatabaseDoesNotExist
print('Initializing test db')
dbname = config['database']['name']
conn = connect()
try:
schema.drop_database(conn, dbname)
except DatabaseDoesNotExist:
pass
schema.init_database(conn)
print('Finishing init database')
yield
print('Deleting `{}` database'.format(dbname))
conn = connect()
try:
schema.drop_database(conn, dbname)
except DatabaseDoesNotExist:
pass
print('Finished deleting `{}`'.format(dbname))
@pytest.fixture
def _bdb(_setup_database, _configure_bigchaindb):
yield
from bigchaindb import config
from bigchaindb.backend import connect
from .utils import flush_db
dbname = config['database']['name']
conn = connect()
flush_db(conn, dbname)
@pytest.fixture
def _genesis(_bdb, genesis_block):
# TODO for precision's sake, delete the block once the test is done. The
# deletion is done indirectly via the teardown code of _bdb but explicit
# deletion of the block would make things clearer. E.g.:
# yield
# tests.utils.delete_genesis_block(conn, dbname)
pass
# We need this function to avoid loading an existing
# conf file located in the home of the user running
# the tests. If it's too aggressive we can change it
@ -68,22 +195,6 @@ def ignore_local_config_file(monkeypatch):
mock_file_config)
@pytest.fixture
def restore_config(ignore_local_config_file, node_config):
from bigchaindb import config_utils
config_utils.set_config(node_config)
@pytest.fixture(scope='module')
def node_config(request):
config = copy.deepcopy(CONFIG)
config['database']['backend'] = request.config.getoption('--database-backend')
if config['database']['backend'] == 'mongodb':
# not a great way to do this
config['database']['port'] = 27017
return config
@pytest.fixture
def user_sk():
return USER_PRIVATE_KEY
@ -105,7 +216,7 @@ def user2_pk():
@pytest.fixture
def b(restore_config):
def b():
from bigchaindb import Bigchain
return Bigchain()
@ -125,7 +236,7 @@ def signed_create_tx(b, create_tx):
def signed_transfer_tx(signed_create_tx, user_pk, user_sk):
from bigchaindb.models import Transaction
inputs = signed_create_tx.to_inputs()
tx = Transaction.transfer(inputs, [([user_pk], 1)], signed_create_tx.asset)
tx = Transaction.transfer(inputs, [([user_pk], 1)], asset_id=signed_create_tx.id)
return tx.sign([user_sk])
@ -145,85 +256,91 @@ def structurally_valid_vote():
@pytest.fixture
def setup_database(restore_config, node_config):
def genesis_block(b):
return b.create_genesis_block()
@pytest.fixture
def inputs(user_pk, b, genesis_block):
from bigchaindb.models import Transaction
# create blocks with transactions for `USER` to spend
prev_block_id = genesis_block.id
for block in range(4):
transactions = [
Transaction.create([b.me], [([user_pk], 1)],
metadata={'msg': random.random()})
.sign([b.me_private])
for _ in range(10)
]
block = b.create_block(transactions)
b.write_block(block)
# vote the blocks valid, so that the inputs are valid
vote = b.vote(block.id, prev_block_id, True)
prev_block_id = block.id
b.write_vote(vote)
@pytest.fixture
def inputs_shared(user_pk, user2_pk, genesis_block):
from bigchaindb.models import Transaction
# create blocks with transactions for `USER` to spend
prev_block_id = genesis_block.id
for block in range(4):
transactions = [
Transaction.create([b.me], [user_pk, user2_pk],
metadata={'msg': random.random()})
.sign([b.me_private])
for _ in range(10)
]
block = b.create_block(transactions)
b.write_block(block)
# vote the blocks valid, so that the inputs are valid
vote = b.vote(block.id, prev_block_id, True)
prev_block_id = block.id
b.write_vote(vote)
@pytest.fixture
def dummy_db(request):
from bigchaindb.backend import connect, schema
from bigchaindb.common.exceptions import (DatabaseDoesNotExist,
DatabaseAlreadyExists)
conn = connect()
dbname = request.fixturename
xdist_suffix = getattr(request.config, 'slaveinput', {}).get('slaveid')
if xdist_suffix:
dbname = '{}_{}'.format(dbname, xdist_suffix)
try:
schema.create_database(conn, dbname)
except DatabaseAlreadyExists:
schema.drop_database(conn, dbname)
schema.create_database(conn, dbname)
yield dbname
try:
schema.drop_database(conn, dbname)
except DatabaseDoesNotExist:
pass
@pytest.fixture
def not_yet_created_db(request):
from bigchaindb.backend import connect, schema
from bigchaindb.common.exceptions import DatabaseDoesNotExist
print('Initializing test db')
db_name = node_config['database']['name']
conn = connect()
dbname = request.fixturename
xdist_suffix = getattr(request.config, 'slaveinput', {}).get('slaveid')
if xdist_suffix:
dbname = '{}_{}'.format(dbname, xdist_suffix)
try:
schema.drop_database(conn, db_name)
schema.drop_database(conn, dbname)
except DatabaseDoesNotExist:
pass
schema.init_database(conn)
print('Finishing init database')
yield
print('Deleting `{}` database'.format(db_name))
conn = connect()
yield dbname
try:
schema.drop_database(conn, db_name)
schema.drop_database(conn, dbname)
except DatabaseDoesNotExist:
pass
print('Finished deleting `{}`'.format(db_name))
@pytest.fixture
def inputs(user_pk, setup_database):
from bigchaindb import Bigchain
from bigchaindb.models import Transaction
from bigchaindb.common.exceptions import GenesisBlockAlreadyExistsError
# 1. create the genesis block
b = Bigchain()
try:
g = b.create_genesis_block()
except GenesisBlockAlreadyExistsError:
pass
# 2. create blocks with transactions for `USER` to spend
prev_block_id = g.id
for block in range(4):
transactions = [
Transaction.create([b.me], [([user_pk], 1)]).sign([b.me_private])
for i in range(10)
]
block = b.create_block(transactions)
b.write_block(block)
# 3. vote the blocks valid, so that the inputs are valid
vote = b.vote(block.id, prev_block_id, True)
prev_block_id = block.id
b.write_vote(vote)
@pytest.fixture
def inputs_shared(user_pk, user2_pk, setup_database):
from bigchaindb import Bigchain
from bigchaindb.models import Transaction
from bigchaindb.common.exceptions import GenesisBlockAlreadyExistsError
# 1. create the genesis block
b = Bigchain()
try:
g = b.create_genesis_block()
except GenesisBlockAlreadyExistsError:
pass
# 2. create blocks with transactions for `USER` to spend
prev_block_id = g.id
for block in range(4):
transactions = [
Transaction.create(
[b.me], [user_pk, user2_pk], payload={'i': i}).sign([b.me_private])
for i in range(10)
]
block = b.create_block(transactions)
b.write_block(block)
# 3. vote the blocks valid, so that the inputs are valid
vote = b.vote(block.id, prev_block_id, True)
prev_block_id = block.id
b.write_vote(vote)

View File

@ -1,6 +1,9 @@
from time import sleep
import pytest
import random
pytestmark = pytest.mark.bdb
@pytest.mark.skipif(reason='Some tests throw a ResourceWarning that might result in some weird '
@ -28,16 +31,15 @@ def dummy_block():
return block
@pytest.mark.usefixtures('setup_database')
class TestBigchainApi(object):
@pytest.mark.genesis
def test_get_last_voted_block_cyclic_blockchain(self, b, monkeypatch):
from bigchaindb.common.crypto import PrivateKey
from bigchaindb.common.exceptions import CyclicBlockchainError
from bigchaindb.common.util import serialize
from bigchaindb.models import Transaction
b.create_genesis_block()
tx = Transaction.create([b.me], [([b.me], 1)])
tx = tx.sign([b.me_private])
monkeypatch.setattr('time.time', lambda: 1)
@ -54,13 +56,12 @@ class TestBigchainApi(object):
with pytest.raises(CyclicBlockchainError):
b.get_last_voted_block()
@pytest.mark.genesis
def test_try_voting_while_constructing_cyclic_blockchain(self, b,
monkeypatch):
from bigchaindb.common.exceptions import CyclicBlockchainError
from bigchaindb.models import Transaction
b.create_genesis_block()
tx = Transaction.create([b.me], [([b.me], 1)])
tx = tx.sign([b.me_private])
block1 = b.create_block([tx])
@ -70,11 +71,10 @@ class TestBigchainApi(object):
with pytest.raises(CyclicBlockchainError):
b.vote(block1.id, block1.id, True)
@pytest.mark.genesis
def test_has_previous_vote_when_already_voted(self, b, monkeypatch):
from bigchaindb.models import Transaction
b.create_genesis_block()
tx = Transaction.create([b.me], [([b.me], 1)])
tx = tx.sign([b.me_private])
@ -89,12 +89,11 @@ class TestBigchainApi(object):
assert b.has_previous_vote(block.id, block.voters) is True
@pytest.mark.genesis
def test_get_spent_with_double_spend(self, b, monkeypatch):
from bigchaindb.common.exceptions import DoubleSpend
from bigchaindb.models import Transaction
b.create_genesis_block()
tx = Transaction.create([b.me], [([b.me], 1)])
tx = tx.sign([b.me_private])
@ -104,14 +103,14 @@ class TestBigchainApi(object):
monkeypatch.setattr('time.time', lambda: 2)
transfer_tx = Transaction.transfer(tx.to_inputs(), [([b.me], 1)],
tx.asset)
asset_id=tx.id)
transfer_tx = transfer_tx.sign([b.me_private])
block2 = b.create_block([transfer_tx])
b.write_block(block2)
monkeypatch.setattr('time.time', lambda: 3333333333)
transfer_tx2 = Transaction.transfer(tx.to_inputs(), [([b.me], 1)],
tx.asset)
asset_id=tx.id)
transfer_tx2 = transfer_tx2.sign([b.me_private])
block3 = b.create_block([transfer_tx2])
b.write_block(block3)
@ -125,12 +124,11 @@ class TestBigchainApi(object):
with pytest.raises(DoubleSpend):
b.get_spent(tx.id, 0)
@pytest.mark.genesis
def test_get_block_status_for_tx_with_double_spend(self, b, monkeypatch):
from bigchaindb.common.exceptions import DoubleSpend
from bigchaindb.models import Transaction
b.create_genesis_block()
tx = Transaction.create([b.me], [([b.me], 1)])
tx = tx.sign([b.me_private])
@ -151,19 +149,20 @@ class TestBigchainApi(object):
with pytest.raises(DoubleSpend):
b.get_blocks_status_containing_tx(tx.id)
@pytest.mark.genesis
def test_get_transaction_in_invalid_and_valid_block(self, monkeypatch, b):
from bigchaindb.models import Transaction
b.create_genesis_block()
monkeypatch.setattr('time.time', lambda: 1)
tx1 = Transaction.create([b.me], [([b.me], 1)])
tx1 = Transaction.create([b.me], [([b.me], 1)],
metadata={'msg': random.random()})
tx1 = tx1.sign([b.me_private])
block1 = b.create_block([tx1])
b.write_block(block1)
monkeypatch.setattr('time.time', lambda: 2222222222)
tx2 = Transaction.create([b.me], [([b.me], 1)])
tx2 = Transaction.create([b.me], [([b.me], 1)],
metadata={'msg': random.random()})
tx2 = tx2.sign([b.me_private])
block2 = b.create_block([tx2])
b.write_block(block2)
@ -186,7 +185,8 @@ class TestBigchainApi(object):
input_tx = b.get_owned_ids(user_pk).pop()
input_tx = b.get_transaction(input_tx.txid)
inputs = input_tx.to_inputs()
tx = Transaction.transfer(inputs, [([user_pk], 1)], input_tx.asset)
tx = Transaction.transfer(inputs, [([user_pk], 1)],
asset_id=input_tx.id)
tx = tx.sign([user_sk])
response = b.write_transaction(tx)
@ -204,7 +204,8 @@ class TestBigchainApi(object):
input_tx = b.get_owned_ids(user_pk).pop()
input_tx = b.get_transaction(input_tx.txid)
inputs = input_tx.to_inputs()
tx = Transaction.transfer(inputs, [([user_pk], 1)], input_tx.asset)
tx = Transaction.transfer(inputs, [([user_pk], 1)],
asset_id=input_tx.id)
tx = tx.sign([user_sk])
b.write_transaction(tx)
@ -224,7 +225,8 @@ class TestBigchainApi(object):
input_tx = b.get_owned_ids(user_pk).pop()
input_tx = b.get_transaction(input_tx.txid)
inputs = input_tx.to_inputs()
tx = Transaction.transfer(inputs, [([user_pk], 1)], input_tx.asset)
tx = Transaction.transfer(inputs, [([user_pk], 1)],
asset_id=input_tx.id)
tx = tx.sign([user_sk])
# There's no need to b.write_transaction(tx) to the backlog
@ -248,7 +250,8 @@ class TestBigchainApi(object):
input_tx = b.get_owned_ids(user_pk).pop()
input_tx = b.get_transaction(input_tx.txid)
inputs = input_tx.to_inputs()
tx = Transaction.transfer(inputs, [([user_pk], 1)], input_tx.asset)
tx = Transaction.transfer(inputs, [([user_pk], 1)],
asset_id=input_tx.id)
tx = tx.sign([user_sk])
# Make sure there's a copy of tx in the backlog
@ -278,11 +281,10 @@ class TestBigchainApi(object):
assert block['block']['transactions'][0]['operation'] == 'GENESIS'
assert block['block']['transactions'][0]['inputs'][0]['fulfills'] is None
@pytest.mark.genesis
def test_create_genesis_block_fails_if_table_not_empty(self, b):
from bigchaindb.common.exceptions import GenesisBlockAlreadyExistsError
b.create_genesis_block()
with pytest.raises(GenesisBlockAlreadyExistsError):
b.create_genesis_block()
@ -341,21 +343,22 @@ class TestBigchainApi(object):
block, status = b.get_block(new_block.id, include_status=True)
assert status == b.BLOCK_UNDECIDED
@pytest.mark.genesis
def test_get_last_voted_block_returns_genesis_if_no_votes_has_been_casted(self, b):
from bigchaindb.models import Block
from bigchaindb.backend import query
b.create_genesis_block()
genesis = query.get_genesis_block(b.connection)
genesis = Block.from_dict(genesis)
gb = b.get_last_voted_block()
assert gb == genesis
assert b.validate_block(gb) == gb
def test_get_last_voted_block_returns_the_correct_block_same_timestamp(self, b, monkeypatch):
genesis = b.create_genesis_block()
assert b.get_last_voted_block() == genesis
def test_get_last_voted_block_returns_the_correct_block_same_timestamp(self,
b,
monkeypatch,
genesis_block):
assert b.get_last_voted_block() == genesis_block
monkeypatch.setattr('time.time', lambda: 1)
block_1 = dummy_block()
@ -379,10 +382,11 @@ class TestBigchainApi(object):
b.write_vote(b.vote(block_3.id, b.get_last_voted_block().id, True))
assert b.get_last_voted_block().id == block_3.id
def test_get_last_voted_block_returns_the_correct_block_different_timestamps(self, b, monkeypatch):
genesis = b.create_genesis_block()
assert b.get_last_voted_block() == genesis
def test_get_last_voted_block_returns_the_correct_block_different_timestamps(self,
b,
monkeypatch,
genesis_block):
assert b.get_last_voted_block() == genesis_block
monkeypatch.setattr('time.time', lambda: 1)
block_1 = dummy_block()
@ -408,28 +412,27 @@ class TestBigchainApi(object):
b.write_vote(b.vote(block_3.id, b.get_last_voted_block().id, True))
assert b.get_last_voted_block().id == block_3.id
def test_no_vote_written_if_block_already_has_vote(self, b):
def test_no_vote_written_if_block_already_has_vote(self, b, genesis_block):
from bigchaindb.models import Block
genesis = b.create_genesis_block()
block_1 = dummy_block()
b.write_block(block_1)
b.write_vote(b.vote(block_1.id, genesis.id, True))
b.write_vote(b.vote(block_1.id, genesis_block.id, True))
retrieved_block_1 = b.get_block(block_1.id)
retrieved_block_1 = Block.from_dict(retrieved_block_1)
# try to vote again on the retrieved block, should do nothing
b.write_vote(b.vote(retrieved_block_1.id, genesis.id, True))
b.write_vote(b.vote(retrieved_block_1.id, genesis_block.id, True))
retrieved_block_2 = b.get_block(block_1.id)
retrieved_block_2 = Block.from_dict(retrieved_block_2)
assert retrieved_block_1 == retrieved_block_2
@pytest.mark.genesis
def test_more_votes_than_voters(self, b):
from bigchaindb.common.exceptions import MultipleVotesError
b.create_genesis_block()
block_1 = dummy_block()
b.write_block(block_1)
# insert duplicate votes
@ -444,15 +447,14 @@ class TestBigchainApi(object):
assert excinfo.value.args[0] == 'Block {block_id} has {n_votes} votes cast, but only {n_voters} voters'\
.format(block_id=block_1.id, n_votes=str(2), n_voters=str(1))
def test_multiple_votes_single_node(self, b):
def test_multiple_votes_single_node(self, b, genesis_block):
from bigchaindb.common.exceptions import MultipleVotesError
genesis = b.create_genesis_block()
block_1 = dummy_block()
b.write_block(block_1)
# insert duplicate votes
for i in range(2):
b.write_vote(b.vote(block_1.id, genesis.id, True))
b.write_vote(b.vote(block_1.id, genesis_block.id, True))
with pytest.raises(MultipleVotesError) as excinfo:
b.block_election_status(block_1.id, block_1.voters)
@ -464,10 +466,10 @@ class TestBigchainApi(object):
assert excinfo.value.args[0] == 'Block {block_id} has {n_votes} votes from public key {me}'\
.format(block_id=block_1.id, n_votes=str(2), me=b.me)
@pytest.mark.genesis
def test_improper_vote_error(selfs, b):
from bigchaindb.common.exceptions import ImproperVoteError
b.create_genesis_block()
block_1 = dummy_block()
b.write_block(block_1)
vote_1 = b.vote(block_1.id, b.get_last_voted_block().id, True)
@ -487,7 +489,8 @@ class TestBigchainApi(object):
input_tx = b.get_owned_ids(user_pk).pop()
input_tx = b.get_transaction(input_tx.txid)
inputs = input_tx.to_inputs()
tx = Transaction.transfer(inputs, [([user_pk], 1)], input_tx.asset)
tx = Transaction.transfer(inputs, [([user_pk], 1)],
asset_id=input_tx.id)
tx = tx.sign([user_sk])
b.write_transaction(tx)
@ -512,7 +515,9 @@ class TestBigchainApi(object):
input_tx = b.get_owned_ids(user_pk).pop()
input_tx = b.get_transaction(input_tx.txid)
inputs = input_tx.to_inputs()
tx = Transaction.transfer(inputs, [([user_pk], 1)], input_tx.asset)
tx = Transaction.transfer(inputs, [([user_pk], 1)],
asset_id=input_tx.id,
metadata={'msg': random.random()})
tx = tx.sign([user_sk])
b.write_transaction(tx)
@ -527,8 +532,7 @@ class TestBigchainApi(object):
def test_non_create_input_not_found(self, b, user_pk):
from cryptoconditions import Ed25519Fulfillment
from bigchaindb.common.exceptions import TransactionDoesNotExist
from bigchaindb.common.transaction import (Input, Asset,
TransactionLink)
from bigchaindb.common.transaction import Input, TransactionLink
from bigchaindb.models import Transaction
from bigchaindb import Bigchain
@ -536,7 +540,8 @@ class TestBigchainApi(object):
input = Input(Ed25519Fulfillment(public_key=user_pk),
[user_pk],
TransactionLink('somethingsomething', 0))
tx = Transaction.transfer([input], [([user_pk], 1)], Asset())
tx = Transaction.transfer([input], [([user_pk], 1)],
asset_id='mock_asset_link')
with pytest.raises(TransactionDoesNotExist):
tx.validate(Bigchain())
@ -546,8 +551,9 @@ class TestBigchainApi(object):
from bigchaindb.models import Transaction
for _ in range(4):
tx = Transaction.create([b.me],
[([user_pk], 1)]).sign([b.me_private])
tx = Transaction.create([b.me], [([user_pk], 1)],
metadata={'msg': random.random()}) \
.sign([b.me_private])
b.write_transaction(tx)
assert query.count_backlog(b.connection) == 4
@ -572,7 +578,7 @@ class TestTransactionValidation(object):
assert excinfo.value.args[0] == 'Only `CREATE` transactions can have null inputs'
@pytest.mark.usefixtures('setup_database')
def test_non_create_input_not_found(self, b, user_pk, signed_transfer_tx):
from bigchaindb.common.exceptions import TransactionDoesNotExist
from bigchaindb.common.transaction import TransactionLink
@ -592,7 +598,7 @@ class TestTransactionValidation(object):
sk, pk = generate_key_pair()
tx = Transaction.create([pk], [([user_pk], 1)])
tx.operation = 'TRANSFER'
tx.asset = input_transaction.asset
tx.asset = {'id': input_transaction.id}
tx.inputs[0].fulfills = input_tx
with pytest.raises(InvalidSignature):
@ -635,7 +641,7 @@ class TestTransactionValidation(object):
input_tx = b.get_transaction(input_tx.txid)
inputs = input_tx.to_inputs()
transfer_tx = Transaction.transfer(inputs, [([user_pk], 1)],
input_tx.asset)
asset_id=input_tx.id)
transfer_tx = transfer_tx.sign([user_sk])
assert transfer_tx == b.validate_transaction(transfer_tx)
@ -660,7 +666,7 @@ class TestTransactionValidation(object):
# create a transaction that's valid but not in a voted valid block
transfer_tx = Transaction.transfer(inputs, [([user_pk], 1)],
input_tx.asset)
asset_id=input_tx.id)
transfer_tx = transfer_tx.sign([user_sk])
assert transfer_tx == b.validate_transaction(transfer_tx)
@ -672,7 +678,7 @@ class TestTransactionValidation(object):
# create transaction with the undecided input
tx_invalid = Transaction.transfer(transfer_tx.to_inputs(),
[([user_pk], 1)],
transfer_tx.asset)
asset_id=transfer_tx.asset['id'])
tx_invalid = tx_invalid.sign([user_sk])
with pytest.raises(TransactionNotInValidBlock):
@ -723,7 +729,7 @@ class TestBlockValidation(object):
assert excinfo.value.args[0] == 'owner_before `a` does not own the input `{}`'.format(valid_input)
@pytest.mark.usefixtures('setup_database')
def test_invalid_signature(self, b):
from bigchaindb.common.exceptions import InvalidSignature
from bigchaindb.common import crypto
@ -738,7 +744,7 @@ class TestBlockValidation(object):
with pytest.raises(InvalidSignature):
b.validate_block(block)
@pytest.mark.usefixtures('setup_database')
def test_invalid_node_pubkey(self, b):
from bigchaindb.common.exceptions import OperationError
from bigchaindb.common import crypto
@ -772,7 +778,8 @@ class TestMultipleInputs(object):
tx_link = b.get_owned_ids(user_pk).pop()
input_tx = b.get_transaction(tx_link.txid)
inputs = input_tx.to_inputs()
tx = Transaction.transfer(inputs, [([user2_pk], 1)], input_tx.asset)
tx = Transaction.transfer(inputs, [([user2_pk], 1)],
asset_id=input_tx.id)
tx = tx.sign([user_sk])
# validate transaction
@ -794,7 +801,8 @@ class TestMultipleInputs(object):
tx_link = owned_inputs.pop()
input_tx = b.get_transaction(tx_link.txid)
tx = Transaction.transfer(input_tx.to_inputs(),
[([user2_pk, user3_pk], 1)], input_tx.asset)
[([user2_pk, user3_pk], 1)],
asset_id=input_tx.id)
tx = tx.sign([user_sk])
assert b.is_valid_transaction(tx) == tx
@ -825,7 +833,7 @@ class TestMultipleInputs(object):
inputs = input_tx.to_inputs()
transfer_tx = Transaction.transfer(inputs, [([user3_pk], 1)],
input_tx.asset)
asset_id=input_tx.id)
transfer_tx = transfer_tx.sign([user_sk, user2_sk])
# validate transaction
@ -858,14 +866,14 @@ class TestMultipleInputs(object):
tx_input = b.get_transaction(tx_link.txid)
tx = Transaction.transfer(tx_input.to_inputs(),
[([user3_pk, user4_pk], 1)], tx_input.asset)
[([user3_pk, user4_pk], 1)],
asset_id=tx_input.id)
tx = tx.sign([user_sk, user2_sk])
assert b.is_valid_transaction(tx) == tx
assert len(tx.inputs) == 1
assert len(tx.outputs) == 1
@pytest.mark.usefixtures('setup_database')
def test_get_owned_ids_single_tx_single_output(self, b, user_sk, user_pk):
from bigchaindb.common import crypto
from bigchaindb.common.transaction import TransactionLink
@ -883,7 +891,8 @@ class TestMultipleInputs(object):
assert owned_inputs_user1 == [TransactionLink(tx.id, 0)]
assert owned_inputs_user2 == []
tx = Transaction.transfer(tx.to_inputs(), [([user2_pk], 1)], tx.asset)
tx = Transaction.transfer(tx.to_inputs(), [([user2_pk], 1)],
asset_id=tx.id)
tx = tx.sign([user_sk])
block = b.create_block([tx])
b.write_block(block)
@ -893,15 +902,14 @@ class TestMultipleInputs(object):
assert owned_inputs_user1 == []
assert owned_inputs_user2 == [TransactionLink(tx.id, 0)]
@pytest.mark.usefixtures('setup_database')
def test_get_owned_ids_single_tx_single_output_invalid_block(self, b,
user_sk,
user_pk):
user_pk,
genesis_block):
from bigchaindb.common import crypto
from bigchaindb.common.transaction import TransactionLink
from bigchaindb.models import Transaction
genesis = b.create_genesis_block()
user2_sk, user2_pk = crypto.generate_key_pair()
tx = Transaction.create([b.me], [([user_pk], 1)])
@ -910,7 +918,7 @@ class TestMultipleInputs(object):
b.write_block(block)
# vote the block VALID
vote = b.vote(block.id, genesis.id, True)
vote = b.vote(block.id, genesis_block.id, True)
b.write_vote(vote)
owned_inputs_user1 = b.get_owned_ids(user_pk)
@ -921,7 +929,7 @@ class TestMultipleInputs(object):
# NOTE: The transaction itself is valid, still will mark the block
# as invalid to mock the behavior.
tx_invalid = Transaction.transfer(tx.to_inputs(), [([user2_pk], 1)],
tx.asset)
asset_id=tx.id)
tx_invalid = tx_invalid.sign([user_sk])
block = b.create_block([tx_invalid])
b.write_block(block)
@ -937,20 +945,16 @@ class TestMultipleInputs(object):
assert owned_inputs_user1 == [TransactionLink(tx.id, 0)]
assert owned_inputs_user2 == []
@pytest.mark.usefixtures('setup_database')
def test_get_owned_ids_single_tx_multiple_outputs(self, b, user_sk,
user_pk):
from bigchaindb.common import crypto
from bigchaindb.common.transaction import TransactionLink, Asset
from bigchaindb.common.transaction import TransactionLink
from bigchaindb.models import Transaction
user2_sk, user2_pk = crypto.generate_key_pair()
# create divisible asset
asset = Asset(divisible=True)
tx_create = Transaction.create([b.me],
[([user_pk], 1), ([user_pk], 1)],
asset=asset)
tx_create = Transaction.create([b.me], [([user_pk], 1), ([user_pk], 1)])
tx_create_signed = tx_create.sign([b.me_private])
block = b.create_block([tx_create_signed])
b.write_block(block)
@ -967,7 +971,7 @@ class TestMultipleInputs(object):
# transfer divisible asset divided in two outputs
tx_transfer = Transaction.transfer(tx_create.to_inputs(),
[([user2_pk], 1), ([user2_pk], 1)],
asset=tx_create.asset)
asset_id=tx_create.id)
tx_transfer_signed = tx_transfer.sign([user_sk])
block = b.create_block([tx_transfer_signed])
b.write_block(block)
@ -978,7 +982,6 @@ class TestMultipleInputs(object):
assert owned_inputs_user2 == [TransactionLink(tx_transfer.id, 0),
TransactionLink(tx_transfer.id, 1)]
@pytest.mark.usefixtures('setup_database')
def test_get_owned_ids_multiple_owners(self, b, user_sk, user_pk):
from bigchaindb.common import crypto
from bigchaindb.common.transaction import TransactionLink
@ -999,7 +1002,8 @@ class TestMultipleInputs(object):
assert owned_inputs_user1 == owned_inputs_user2
assert owned_inputs_user1 == expected_owned_inputs_user1
tx = Transaction.transfer(tx.to_inputs(), [([user3_pk], 1)], tx.asset)
tx = Transaction.transfer(tx.to_inputs(), [([user3_pk], 1)],
asset_id=tx.id)
tx = tx.sign([user_sk, user2_sk])
block = b.create_block([tx])
b.write_block(block)
@ -1009,7 +1013,6 @@ class TestMultipleInputs(object):
assert owned_inputs_user1 == owned_inputs_user2
assert owned_inputs_user1 == []
@pytest.mark.usefixtures('setup_database')
def test_get_spent_single_tx_single_output(self, b, user_sk, user_pk):
from bigchaindb.common import crypto
from bigchaindb.models import Transaction
@ -1030,7 +1033,8 @@ class TestMultipleInputs(object):
assert spent_inputs_user1 is None
# create a transaction and block
tx = Transaction.transfer(tx.to_inputs(), [([user2_pk], 1)], tx.asset)
tx = Transaction.transfer(tx.to_inputs(), [([user2_pk], 1)],
asset_id=tx.id)
tx = tx.sign([user_sk])
block = b.create_block([tx])
b.write_block(block)
@ -1038,13 +1042,13 @@ class TestMultipleInputs(object):
spent_inputs_user1 = b.get_spent(input_txid, input_idx)
assert spent_inputs_user1 == tx
@pytest.mark.usefixtures('setup_database')
def test_get_spent_single_tx_single_output_invalid_block(self, b, user_sk, user_pk):
def test_get_spent_single_tx_single_output_invalid_block(self, b,
user_sk,
user_pk,
genesis_block):
from bigchaindb.common import crypto
from bigchaindb.models import Transaction
genesis = b.create_genesis_block()
# create a new users
user2_sk, user2_pk = crypto.generate_key_pair()
@ -1054,7 +1058,7 @@ class TestMultipleInputs(object):
b.write_block(block)
# vote the block VALID
vote = b.vote(block.id, genesis.id, True)
vote = b.vote(block.id, genesis_block.id, True)
b.write_vote(vote)
owned_inputs_user1 = b.get_owned_ids(user_pk).pop()
@ -1066,7 +1070,8 @@ class TestMultipleInputs(object):
assert spent_inputs_user1 is None
# create a transaction and block
tx = Transaction.transfer(tx.to_inputs(), [([user2_pk], 1)], tx.asset)
tx = Transaction.transfer(tx.to_inputs(), [([user2_pk], 1)],
asset_id=tx.id)
tx = tx.sign([user_sk])
block = b.create_block([tx])
b.write_block(block)
@ -1081,22 +1086,18 @@ class TestMultipleInputs(object):
# Now there should be no spents (the block is invalid)
assert spent_inputs_user1 is None
@pytest.mark.usefixtures('setup_database')
def test_get_spent_single_tx_multiple_outputs(self, b, user_sk, user_pk):
from bigchaindb.common import crypto
from bigchaindb.models import Transaction
from bigchaindb.common.transaction import Asset
# create a new users
user2_sk, user2_pk = crypto.generate_key_pair()
# create a divisible asset with 3 outputs
asset = Asset(divisible=True)
tx_create = Transaction.create([b.me],
[([user_pk], 1),
([user_pk], 1),
([user_pk], 1)],
asset=asset)
([user_pk], 1)])
tx_create_signed = tx_create.sign([b.me_private])
block = b.create_block([tx_create_signed])
b.write_block(block)
@ -1110,7 +1111,7 @@ class TestMultipleInputs(object):
# transfer the first 2 inputs
tx_transfer = Transaction.transfer(tx_create.to_inputs()[:2],
[([user2_pk], 1), ([user2_pk], 1)],
asset=tx_create.asset)
asset_id=tx_create.id)
tx_transfer_signed = tx_transfer.sign([user_sk])
block = b.create_block([tx_transfer_signed])
b.write_block(block)
@ -1124,9 +1125,7 @@ class TestMultipleInputs(object):
# spendable by BigchainDB
assert b.get_spent(tx_create.to_inputs()[2].fulfills.txid, 2) is None
@pytest.mark.usefixtures('setup_database')
def test_get_spent_multiple_owners(self, b, user_sk, user_pk):
import random
from bigchaindb.common import crypto
from bigchaindb.models import Transaction
@ -1151,7 +1150,8 @@ class TestMultipleInputs(object):
# create a transaction
tx = Transaction.transfer(transactions[0].to_inputs(),
[([user3_pk], 1)], transactions[0].asset)
[([user3_pk], 1)],
asset_id=transactions[0].id)
tx = tx.sign([user_sk, user2_sk])
block = b.create_block([tx])
b.write_block(block)

View File

@ -3,8 +3,7 @@ from bigchaindb.pipelines import block, election, vote, stale
@pytest.fixture
def processes(b, setup_database):
b.create_genesis_block()
def processes(genesis_block):
block_maker = block.start()
voter = vote.start()
election_runner = election.start()

View File

@ -2,8 +2,9 @@ import time
import pytest
pytestmark = [pytest.mark.bdb, pytest.mark.usefixtures('processes')]
@pytest.mark.usefixtures('processes')
def test_fast_double_create(b, user_pk):
from bigchaindb.models import Transaction
from bigchaindb.backend.query import count_blocks
@ -25,7 +26,6 @@ def test_fast_double_create(b, user_pk):
assert count_blocks(b.connection) == 2
@pytest.mark.usefixtures('processes')
def test_double_create(b, user_pk):
from bigchaindb.models import Transaction
from bigchaindb.backend.query import count_blocks

View File

@ -1,3 +1,4 @@
import random
import time
from unittest.mock import patch
@ -26,7 +27,7 @@ def test_filter_by_assignee(b, signed_create_tx):
assert block_maker.filter_tx(tx) is None
@pytest.mark.usefixtures('setup_database')
@pytest.mark.bdb
def test_validate_transaction(b, create_tx):
from bigchaindb.pipelines.block import BlockPipeline
@ -44,8 +45,9 @@ def test_create_block(b, user_pk):
block_maker = BlockPipeline()
for i in range(100):
tx = Transaction.create([b.me], [([user_pk], 1)])
for _ in range(100):
tx = Transaction.create([b.me], [([user_pk], 1)],
metadata={'msg': random.random()})
tx = tx.sign([b.me_private])
block_maker.create(tx)
@ -55,7 +57,7 @@ def test_create_block(b, user_pk):
assert len(block_doc.transactions) == 100
@pytest.mark.usefixtures('setup_database')
@pytest.mark.bdb
def test_write_block(b, user_pk):
from bigchaindb.models import Block, Transaction
from bigchaindb.pipelines.block import BlockPipeline
@ -63,8 +65,9 @@ def test_write_block(b, user_pk):
block_maker = BlockPipeline()
txs = []
for i in range(100):
tx = Transaction.create([b.me], [([user_pk], 1)])
for _ in range(100):
tx = Transaction.create([b.me], [([user_pk], 1)],
metadata={'msg': random.random()})
tx = tx.sign([b.me_private])
txs.append(tx)
@ -76,15 +79,16 @@ def test_write_block(b, user_pk):
assert expected == block_doc
@pytest.mark.usefixtures('setup_database')
@pytest.mark.bdb
def test_duplicate_transaction(b, user_pk):
from bigchaindb.models import Transaction
from bigchaindb.pipelines import block
block_maker = block.BlockPipeline()
txs = []
for i in range(10):
tx = Transaction.create([b.me], [([user_pk], 1)])
for _ in range(10):
tx = Transaction.create([b.me], [([user_pk], 1)],
metadata={'msg': random.random()})
tx = tx.sign([b.me_private])
txs.append(tx)
@ -108,13 +112,14 @@ def test_duplicate_transaction(b, user_pk):
assert status != b.TX_IN_BACKLOG
@pytest.mark.usefixtures('setup_database')
@pytest.mark.bdb
def test_delete_tx(b, user_pk):
from bigchaindb.models import Transaction
from bigchaindb.pipelines.block import BlockPipeline
block_maker = BlockPipeline()
for i in range(100):
tx = Transaction.create([b.me], [([user_pk], 1)])
tx = Transaction.create([b.me], [([user_pk], 1)],
metadata={'msg': random.random()})
tx = tx.sign([b.me_private])
block_maker.create(tx)
# make sure the tx appears in the backlog
@ -137,7 +142,7 @@ def test_delete_tx(b, user_pk):
@patch('bigchaindb.pipelines.block.create_pipeline')
@pytest.mark.usefixtures('setup_database')
@pytest.mark.bdb
def test_start(create_pipeline):
from bigchaindb.pipelines import block
@ -148,12 +153,10 @@ def test_start(create_pipeline):
assert pipeline == create_pipeline.return_value
@pytest.mark.usefixtures('setup_database')
@pytest.mark.bdb
def test_full_pipeline(b, user_pk):
import random
from bigchaindb.backend import query
from bigchaindb.models import Block, Transaction
from bigchaindb.pipelines.block import create_pipeline, get_changefeed
from bigchaindb.pipelines.block import create_pipeline
outpipe = Pipe()
@ -166,7 +169,7 @@ def test_full_pipeline(b, user_pk):
number_assigned_to_others = 0
for i in range(100):
tx = Transaction.create([b.me], [([user_pk], 1)],
{'msg': random.random()})
metadata={'msg': random.random()})
tx = tx.sign([b.me_private])
tx = tx.to_dict()

View File

@ -10,7 +10,7 @@ from bigchaindb import Bigchain
from bigchaindb.pipelines import election
@pytest.mark.usefixtures('setup_database')
@pytest.mark.bdb
def test_check_for_quorum_invalid(b, user_pk):
from bigchaindb.models import Transaction
@ -44,7 +44,7 @@ def test_check_for_quorum_invalid(b, user_pk):
assert e.check_for_quorum(votes[-1]) == test_block
@pytest.mark.usefixtures('setup_database')
@pytest.mark.bdb
def test_check_for_quorum_invalid_prev_node(b, user_pk):
from bigchaindb.models import Transaction
e = election.Election()
@ -79,7 +79,7 @@ def test_check_for_quorum_invalid_prev_node(b, user_pk):
assert e.check_for_quorum(votes[-1]) == test_block
@pytest.mark.usefixtures('setup_database')
@pytest.mark.bdb
def test_check_for_quorum_valid(b, user_pk):
from bigchaindb.models import Transaction
@ -112,7 +112,7 @@ def test_check_for_quorum_valid(b, user_pk):
assert e.check_for_quorum(votes[-1]) is None
@pytest.mark.usefixtures('setup_database')
@pytest.mark.bdb
def test_check_requeue_transaction(b, user_pk):
from bigchaindb.models import Transaction
@ -140,7 +140,7 @@ def test_start(mock_start):
mock_start.assert_called_with()
@pytest.mark.usefixtures('setup_database')
@pytest.mark.bdb
def test_full_pipeline(b, user_pk):
import random
from bigchaindb.backend import query

View File

@ -1,14 +1,15 @@
import os
import random
from bigchaindb import Bigchain
from bigchaindb.pipelines import stale
from multipipes import Pipe, Pipeline
from unittest.mock import patch
from bigchaindb import config_utils
import os
import pytest
@pytest.mark.usefixtures('setup_database')
@pytest.mark.bdb
def test_get_stale(b, user_pk):
from bigchaindb.models import Transaction
tx = Transaction.create([b.me], [([user_pk], 1)])
@ -25,7 +26,7 @@ def test_get_stale(b, user_pk):
assert tx.to_dict() == _tx
@pytest.mark.usefixtures('setup_database')
@pytest.mark.bdb
def test_reassign_transactions(b, user_pk):
from bigchaindb.backend import query
from bigchaindb.models import Transaction
@ -65,22 +66,15 @@ def test_reassign_transactions(b, user_pk):
assert tx['assignee'] != 'lol'
@pytest.mark.usefixtures('setup_database')
@pytest.mark.bdb
def test_full_pipeline(monkeypatch, user_pk):
from bigchaindb.backend import query
from bigchaindb.models import Transaction
CONFIG = {
'database': {
'name': 'bigchain_test_{}'.format(os.getpid())
},
'keypair': {
'private': '31Lb1ZGKTyHnmVK3LUMrAUrPNfd4sE2YyBt3UA4A25aA',
'public': '4XYfCbabAWVUCbjTmRTFEu2sc3dFEdkse4r6X498B1s8'
},
'keyring': ['aaa', 'bbb'],
'backlog_reassign_delay': 0.01
}
config_utils.set_config(CONFIG)
config_utils.update_config(CONFIG)
b = Bigchain()
original_txs = {}
@ -89,7 +83,8 @@ def test_full_pipeline(monkeypatch, user_pk):
monkeypatch.setattr('time.time', lambda: 1)
for i in range(100):
tx = Transaction.create([b.me], [([user_pk], 1)])
tx = Transaction.create([b.me], [([user_pk], 1)],
metadata={'msg': random.random()})
tx = tx.sign([b.me_private])
original_txc.append(tx.to_dict())

View File

@ -1,3 +1,4 @@
import random
import time
from unittest.mock import patch
@ -5,9 +6,11 @@ from multipipes import Pipe, Pipeline
import pytest
# TODO: dummy_tx and dummy_block could be fixtures
def dummy_tx(b):
from bigchaindb.models import Transaction
tx = Transaction.create([b.me], [([b.me], 1)])
tx = Transaction.create([b.me], [([b.me], 1)],
metadata={'msg': random.random()})
tx = tx.sign([b.me_private])
return tx
@ -59,11 +62,10 @@ def test_vote_creation_invalid(b):
vote['signature']) is True
@pytest.mark.usefixtures('setup_database')
@pytest.mark.genesis
def test_vote_ungroup_returns_a_set_of_results(b):
from bigchaindb.pipelines import vote
b.create_genesis_block()
block = dummy_block(b)
vote_obj = vote.Vote()
txs = list(vote_obj.ungroup(block.id, block.transactions))
@ -71,11 +73,10 @@ def test_vote_ungroup_returns_a_set_of_results(b):
assert len(txs) == 10
@pytest.mark.usefixtures('setup_database')
@pytest.mark.genesis
def test_vote_validate_block(b):
from bigchaindb.pipelines import vote
b.create_genesis_block()
tx = dummy_tx(b)
block = b.create_block([tx])
@ -96,11 +97,10 @@ def test_vote_validate_block(b):
assert tx1 == tx2
@pytest.mark.usefixtures('setup_database')
@pytest.mark.genesis
def test_validate_block_with_invalid_id(b):
from bigchaindb.pipelines import vote
b.create_genesis_block()
tx = dummy_tx(b)
block = b.create_block([tx]).to_dict()
block['id'] = 'an invalid id'
@ -111,11 +111,10 @@ def test_validate_block_with_invalid_id(b):
assert invalid_dummy_tx == [vote_obj.invalid_dummy_tx]
@pytest.mark.usefixtures('setup_database')
@pytest.mark.genesis
def test_validate_block_with_invalid_signature(b):
from bigchaindb.pipelines import vote
b.create_genesis_block()
tx = dummy_tx(b)
block = b.create_block([tx]).to_dict()
block['signature'] = 'an invalid signature'
@ -126,12 +125,11 @@ def test_validate_block_with_invalid_signature(b):
assert invalid_dummy_tx == [vote_obj.invalid_dummy_tx]
@pytest.mark.usefixtures('setup_database')
@pytest.mark.genesis
def test_vote_validate_transaction(b):
from bigchaindb.pipelines import vote
from bigchaindb.models import Transaction
b.create_genesis_block()
tx = dummy_tx(b)
vote_obj = vote.Vote()
validation = vote_obj.validate_tx(tx, 123, 1)
@ -143,11 +141,10 @@ def test_vote_validate_transaction(b):
assert validation == (False, 456, 10)
@pytest.mark.usefixtures('setup_database')
@pytest.mark.genesis
def test_vote_accumulates_transactions(b):
from bigchaindb.pipelines import vote
b.create_genesis_block()
vote_obj = vote.Vote()
for _ in range(10):
@ -162,14 +159,13 @@ def test_vote_accumulates_transactions(b):
assert validation == (False, 456, 10)
@pytest.mark.usefixtures('setup_database')
def test_valid_block_voting_sequential(b, monkeypatch):
@pytest.mark.bdb
def test_valid_block_voting_sequential(b, genesis_block, monkeypatch):
from bigchaindb.backend import query
from bigchaindb.common import crypto, util
from bigchaindb.pipelines import vote
monkeypatch.setattr('time.time', lambda: 1111111111)
genesis = b.create_genesis_block()
vote_obj = vote.Vote()
block = dummy_block(b)
@ -181,7 +177,7 @@ def test_valid_block_voting_sequential(b, monkeypatch):
vote_doc = vote_rs.next()
assert vote_doc['vote'] == {'voting_for_block': block.id,
'previous_block': genesis.id,
'previous_block': genesis_block.id,
'is_block_valid': True,
'invalid_reason': None,
'timestamp': '1111111111'}
@ -192,8 +188,8 @@ def test_valid_block_voting_sequential(b, monkeypatch):
vote_doc['signature']) is True
@pytest.mark.usefixtures('setup_database')
def test_valid_block_voting_multiprocessing(b, monkeypatch):
@pytest.mark.bdb
def test_valid_block_voting_multiprocessing(b, genesis_block, monkeypatch):
from bigchaindb.backend import query
from bigchaindb.common import crypto, util
from bigchaindb.pipelines import vote
@ -202,7 +198,6 @@ def test_valid_block_voting_multiprocessing(b, monkeypatch):
outpipe = Pipe()
monkeypatch.setattr('time.time', lambda: 1111111111)
genesis = b.create_genesis_block()
vote_pipeline = vote.create_pipeline()
vote_pipeline.setup(indata=inpipe, outdata=outpipe)
@ -217,7 +212,7 @@ def test_valid_block_voting_multiprocessing(b, monkeypatch):
vote_doc = vote_rs.next()
assert vote_out['vote'] == vote_doc['vote']
assert vote_doc['vote'] == {'voting_for_block': block.id,
'previous_block': genesis.id,
'previous_block': genesis_block.id,
'is_block_valid': True,
'invalid_reason': None,
'timestamp': '1111111111'}
@ -228,15 +223,15 @@ def test_valid_block_voting_multiprocessing(b, monkeypatch):
vote_doc['signature']) is True
@pytest.mark.usefixtures('setup_database')
def test_valid_block_voting_with_create_transaction(b, monkeypatch):
@pytest.mark.bdb
def test_valid_block_voting_with_create_transaction(b,
genesis_block,
monkeypatch):
from bigchaindb.backend import query
from bigchaindb.common import crypto, util
from bigchaindb.models import Transaction
from bigchaindb.pipelines import vote
genesis = b.create_genesis_block()
# create a `CREATE` transaction
test_user_priv, test_user_pub = crypto.generate_key_pair()
tx = Transaction.create([b.me], [([test_user_pub], 1)])
@ -260,7 +255,7 @@ def test_valid_block_voting_with_create_transaction(b, monkeypatch):
vote_doc = vote_rs.next()
assert vote_out['vote'] == vote_doc['vote']
assert vote_doc['vote'] == {'voting_for_block': block.id,
'previous_block': genesis.id,
'previous_block': genesis_block.id,
'is_block_valid': True,
'invalid_reason': None,
'timestamp': '1111111111'}
@ -271,15 +266,14 @@ def test_valid_block_voting_with_create_transaction(b, monkeypatch):
vote_doc['signature']) is True
@pytest.mark.usefixtures('setup_database')
def test_valid_block_voting_with_transfer_transactions(monkeypatch, b):
@pytest.mark.bdb
def test_valid_block_voting_with_transfer_transactions(monkeypatch,
b, genesis_block):
from bigchaindb.backend import query
from bigchaindb.common import crypto, util
from bigchaindb.models import Transaction
from bigchaindb.pipelines import vote
genesis = b.create_genesis_block()
# create a `CREATE` transaction
test_user_priv, test_user_pub = crypto.generate_key_pair()
tx = Transaction.create([b.me], [([test_user_pub], 1)])
@ -292,7 +286,7 @@ def test_valid_block_voting_with_transfer_transactions(monkeypatch, b):
# create a `TRANSFER` transaction
test_user2_priv, test_user2_pub = crypto.generate_key_pair()
tx2 = Transaction.transfer(tx.to_inputs(), [([test_user2_pub], 1)],
tx.asset)
asset_id=tx.id)
tx2 = tx2.sign([test_user_priv])
monkeypatch.setattr('time.time', lambda: 2222222222)
@ -317,7 +311,7 @@ def test_valid_block_voting_with_transfer_transactions(monkeypatch, b):
vote_doc = vote_rs.next()
assert vote_out['vote'] == vote_doc['vote']
assert vote_doc['vote'] == {'voting_for_block': block.id,
'previous_block': genesis.id,
'previous_block': genesis_block.id,
'is_block_valid': True,
'invalid_reason': None,
'timestamp': '2222222222'}
@ -342,8 +336,8 @@ def test_valid_block_voting_with_transfer_transactions(monkeypatch, b):
vote2_doc['signature']) is True
@pytest.mark.usefixtures('setup_database')
def test_unsigned_tx_in_block_voting(monkeypatch, b, user_pk):
@pytest.mark.bdb
def test_unsigned_tx_in_block_voting(monkeypatch, b, user_pk, genesis_block):
from bigchaindb.backend import query
from bigchaindb.common import crypto, util
from bigchaindb.models import Transaction
@ -353,7 +347,6 @@ def test_unsigned_tx_in_block_voting(monkeypatch, b, user_pk):
outpipe = Pipe()
monkeypatch.setattr('time.time', lambda: 1111111111)
genesis = b.create_genesis_block()
vote_pipeline = vote.create_pipeline()
vote_pipeline.setup(indata=inpipe, outdata=outpipe)
@ -370,7 +363,7 @@ def test_unsigned_tx_in_block_voting(monkeypatch, b, user_pk):
vote_doc = vote_rs.next()
assert vote_out['vote'] == vote_doc['vote']
assert vote_doc['vote'] == {'voting_for_block': block.id,
'previous_block': genesis.id,
'previous_block': genesis_block.id,
'is_block_valid': False,
'invalid_reason': None,
'timestamp': '1111111111'}
@ -381,8 +374,8 @@ def test_unsigned_tx_in_block_voting(monkeypatch, b, user_pk):
vote_doc['signature']) is True
@pytest.mark.usefixtures('setup_database')
def test_invalid_id_tx_in_block_voting(monkeypatch, b, user_pk):
@pytest.mark.bdb
def test_invalid_id_tx_in_block_voting(monkeypatch, b, user_pk, genesis_block):
from bigchaindb.backend import query
from bigchaindb.common import crypto, util
from bigchaindb.models import Transaction
@ -392,7 +385,6 @@ def test_invalid_id_tx_in_block_voting(monkeypatch, b, user_pk):
outpipe = Pipe()
monkeypatch.setattr('time.time', lambda: 1111111111)
genesis = b.create_genesis_block()
vote_pipeline = vote.create_pipeline()
vote_pipeline.setup(indata=inpipe, outdata=outpipe)
@ -411,7 +403,7 @@ def test_invalid_id_tx_in_block_voting(monkeypatch, b, user_pk):
vote_doc = vote_rs.next()
assert vote_out['vote'] == vote_doc['vote']
assert vote_doc['vote'] == {'voting_for_block': block['id'],
'previous_block': genesis.id,
'previous_block': genesis_block.id,
'is_block_valid': False,
'invalid_reason': None,
'timestamp': '1111111111'}
@ -422,8 +414,9 @@ def test_invalid_id_tx_in_block_voting(monkeypatch, b, user_pk):
vote_doc['signature']) is True
@pytest.mark.usefixtures('setup_database')
def test_invalid_content_in_tx_in_block_voting(monkeypatch, b, user_pk):
@pytest.mark.bdb
def test_invalid_content_in_tx_in_block_voting(monkeypatch, b,
user_pk, genesis_block):
from bigchaindb.backend import query
from bigchaindb.common import crypto, util
from bigchaindb.models import Transaction
@ -433,7 +426,6 @@ def test_invalid_content_in_tx_in_block_voting(monkeypatch, b, user_pk):
outpipe = Pipe()
monkeypatch.setattr('time.time', lambda: 1111111111)
genesis = b.create_genesis_block()
vote_pipeline = vote.create_pipeline()
vote_pipeline.setup(indata=inpipe, outdata=outpipe)
@ -452,7 +444,7 @@ def test_invalid_content_in_tx_in_block_voting(monkeypatch, b, user_pk):
vote_doc = vote_rs.next()
assert vote_out['vote'] == vote_doc['vote']
assert vote_doc['vote'] == {'voting_for_block': block['id'],
'previous_block': genesis.id,
'previous_block': genesis_block.id,
'is_block_valid': False,
'invalid_reason': None,
'timestamp': '1111111111'}
@ -463,8 +455,8 @@ def test_invalid_content_in_tx_in_block_voting(monkeypatch, b, user_pk):
vote_doc['signature']) is True
@pytest.mark.usefixtures('setup_database')
def test_invalid_block_voting(monkeypatch, b, user_pk):
@pytest.mark.bdb
def test_invalid_block_voting(monkeypatch, b, user_pk, genesis_block):
from bigchaindb.backend import query
from bigchaindb.common import crypto, util
from bigchaindb.pipelines import vote
@ -473,7 +465,6 @@ def test_invalid_block_voting(monkeypatch, b, user_pk):
outpipe = Pipe()
monkeypatch.setattr('time.time', lambda: 1111111111)
genesis = b.create_genesis_block()
vote_pipeline = vote.create_pipeline()
vote_pipeline.setup(indata=inpipe, outdata=outpipe)
@ -489,7 +480,7 @@ def test_invalid_block_voting(monkeypatch, b, user_pk):
vote_doc = vote_rs.next()
assert vote_out['vote'] == vote_doc['vote']
assert vote_doc['vote'] == {'voting_for_block': block['id'],
'previous_block': genesis.id,
'previous_block': genesis_block.id,
'is_block_valid': False,
'invalid_reason': None,
'timestamp': '1111111111'}
@ -500,7 +491,7 @@ def test_invalid_block_voting(monkeypatch, b, user_pk):
vote_doc['signature']) is True
@pytest.mark.usefixtures('setup_database')
@pytest.mark.genesis
def test_voter_considers_unvoted_blocks_when_single_node(monkeypatch, b):
from bigchaindb.backend import query
from bigchaindb.pipelines import vote
@ -508,7 +499,6 @@ def test_voter_considers_unvoted_blocks_when_single_node(monkeypatch, b):
outpipe = Pipe()
monkeypatch.setattr('time.time', lambda: 1111111111)
b.create_genesis_block()
block_ids = []
# insert blocks in the database while the voter process is not listening
@ -549,7 +539,7 @@ def test_voter_considers_unvoted_blocks_when_single_node(monkeypatch, b):
assert all(vote['node_pubkey'] == b.me for vote in votes)
@pytest.mark.usefixtures('setup_database')
@pytest.mark.genesis
def test_voter_chains_blocks_with_the_previous_ones(monkeypatch, b):
from bigchaindb.backend import query
from bigchaindb.pipelines import vote
@ -557,7 +547,6 @@ def test_voter_chains_blocks_with_the_previous_ones(monkeypatch, b):
outpipe = Pipe()
monkeypatch.setattr('time.time', lambda: 1111111111)
b.create_genesis_block()
block_ids = []
monkeypatch.setattr('time.time', lambda: 2222222222)
@ -591,7 +580,7 @@ def test_voter_chains_blocks_with_the_previous_ones(monkeypatch, b):
{block['id'] for block in blocks})
@pytest.mark.usefixtures('setup_database')
@pytest.mark.genesis
def test_voter_checks_for_previous_vote(monkeypatch, b):
from bigchaindb.backend import query
from bigchaindb.pipelines import vote
@ -600,7 +589,6 @@ def test_voter_checks_for_previous_vote(monkeypatch, b):
outpipe = Pipe()
monkeypatch.setattr('time.time', lambda: 1111111111)
b.create_genesis_block()
monkeypatch.setattr('time.time', lambda: 2222222222)
block_1 = dummy_block(b)
@ -633,14 +621,11 @@ def test_voter_checks_for_previous_vote(monkeypatch, b):
@patch.object(Pipeline, 'start')
@pytest.mark.usefixtures('setup_database')
@pytest.mark.genesis
def test_start(mock_start, b):
# TODO: `block.start` is just a wrapper around `vote.create_pipeline`,
# that is tested by `test_full_pipeline`.
# If anyone has better ideas on how to test this, please do a PR :)
from bigchaindb.pipelines import vote
b.create_genesis_block()
vote.start()
mock_start.assert_called_with()

View File

@ -9,19 +9,6 @@ import bigchaindb
ORIGINAL_CONFIG = copy.deepcopy(bigchaindb._config)
@pytest.fixture
def ignore_local_config_file(monkeypatch):
"""
This fixture's purpose is to override the one under
:module:`tests/conftest.py` so that the original behaviour of
:func:`bigchaindb.config_utils.file_config` is restored, so that it can be
tested.
"""
from bigchaindb.config_utils import file_config
monkeypatch.setattr('bigchaindb.config_utils.file_config', file_config)
@pytest.fixture(scope='function', autouse=True)
def clean_config(monkeypatch):
monkeypatch.setattr('bigchaindb.config', copy.deepcopy(ORIGINAL_CONFIG))

View File

@ -91,9 +91,10 @@ def test_has_previous_vote(monkeypatch):
bigchain.has_previous_vote(block)
@pytest.mark.parametrize('count,exists', ((1, True), (0, False)))
def test_transaction_exists(monkeypatch, count, exists):
@pytest.mark.parametrize('exists', (True, False))
def test_transaction_exists(monkeypatch, exists):
from bigchaindb.core import Bigchain
monkeypatch.setattr(RqlQuery, 'run', lambda x, y: count)
monkeypatch.setattr(
'bigchaindb.backend.query.has_transaction', lambda x, y: exists)
bigchain = Bigchain(public_key='pubkey', private_key='privkey')
assert bigchain.transaction_exists('txid') is exists

43
tests/utils.py Normal file
View File

@ -0,0 +1,43 @@
from functools import singledispatch
import rethinkdb as r
from bigchaindb.backend.mongodb.connection import MongoDBConnection
from bigchaindb.backend.rethinkdb.connection import RethinkDBConnection
@singledispatch
def list_dbs(connection):
raise NotImplementedError
@list_dbs.register(RethinkDBConnection)
def list_rethink_dbs(connection):
return connection.run(r.db_list())
@list_dbs.register(MongoDBConnection)
def list_mongo_dbs(connection):
raise NotImplementedError
@singledispatch
def flush_db(connection, dbname):
raise NotImplementedError
@flush_db.register(RethinkDBConnection)
def flush_rethink_db(connection, dbname):
try:
connection.run(r.db(dbname).table('bigchain').delete())
connection.run(r.db(dbname).table('backlog').delete())
connection.run(r.db(dbname).table('votes').delete())
except r.ReqlOpFailedError:
pass
@flush_db.register(MongoDBConnection)
def flush_mongo_db(connection, dbname):
connection.conn[dbname].bigchain.delete_many({})
connection.conn[dbname].backlog.delete_many({})
connection.conn[dbname].votes.delete_many({})

View File

@ -2,7 +2,7 @@ import pytest
@pytest.fixture
def app(request, restore_config):
def app(request):
from bigchaindb.web import server
app = server.create_app(debug=True)
return app

View File

@ -8,6 +8,7 @@ from bigchaindb.common import crypto
TX_ENDPOINT = '/api/v1/transactions/'
@pytest.mark.bdb
@pytest.mark.usefixtures('inputs')
def test_get_transaction_endpoint(b, client, user_pk):
input_tx = b.get_owned_ids(user_pk).pop()
@ -17,6 +18,7 @@ def test_get_transaction_endpoint(b, client, user_pk):
assert res.status_code == 200
@pytest.mark.bdb
@pytest.mark.usefixtures('inputs')
def test_get_transaction_returns_404_if_not_found(client):
res = client.get(TX_ENDPOINT + '123')
@ -26,7 +28,7 @@ def test_get_transaction_returns_404_if_not_found(client):
assert res.status_code == 404
@pytest.mark.usefixtures('setup_database')
@pytest.mark.bdb
def test_post_create_transaction_endpoint(b, client):
from bigchaindb.models import Transaction
user_priv, user_pub = crypto.generate_key_pair()
@ -120,6 +122,7 @@ def test_post_invalid_transaction(client, exc, msg, monkeypatch):
'Invalid transaction ({}): {}'.format(exc, msg))
@pytest.mark.bdb
@pytest.mark.usefixtures('inputs')
def test_post_transfer_transaction_endpoint(b, client, user_pk, user_sk):
sk, pk = crypto.generate_key_pair()
@ -130,7 +133,8 @@ def test_post_transfer_transaction_endpoint(b, client, user_pk, user_sk):
input_valid = b.get_owned_ids(user_pk).pop()
create_tx = b.get_transaction(input_valid.txid)
transfer_tx = Transaction.transfer(create_tx.to_inputs(),
[([user_pub], 1)], create_tx.asset)
[([user_pub], 1)],
asset_id=create_tx.id)
transfer_tx = transfer_tx.sign([user_sk])
res = client.post(TX_ENDPOINT, data=json.dumps(transfer_tx.to_dict()))
@ -139,6 +143,7 @@ def test_post_transfer_transaction_endpoint(b, client, user_pk, user_sk):
assert res.json['outputs'][0]['public_keys'][0] == user_pub
@pytest.mark.bdb
@pytest.mark.usefixtures('inputs')
def test_post_invalid_transfer_transaction_returns_400(b, client, user_pk, user_sk):
from bigchaindb.models import Transaction
@ -148,12 +153,14 @@ def test_post_invalid_transfer_transaction_returns_400(b, client, user_pk, user_
input_valid = b.get_owned_ids(user_pk).pop()
create_tx = b.get_transaction(input_valid.txid)
transfer_tx = Transaction.transfer(create_tx.to_inputs(),
[([user_pub], 1)], create_tx.asset)
[([user_pub], 1)],
asset_id=create_tx.id)
res = client.post(TX_ENDPOINT, data=json.dumps(transfer_tx.to_dict()))
assert res.status_code == 400
@pytest.mark.bdb
@pytest.mark.usefixtures('inputs')
def test_get_transaction_status_endpoint(b, client, user_pk):
input_tx = b.get_owned_ids(user_pk).pop()
@ -167,6 +174,7 @@ def test_get_transaction_status_endpoint(b, client, user_pk):
assert res.status_code == 200
@pytest.mark.bdb
@pytest.mark.usefixtures('inputs')
def test_get_transaction_status_returns_404_if_not_found(client):
res = client.get(TX_ENDPOINT + '123' + "/status")

View File

@ -1,10 +1,10 @@
import pytest
pytestmark = [pytest.mark.bdb, pytest.mark.usefixtures('inputs')]
UNSPENTS_ENDPOINT = '/api/v1/unspents/'
@pytest.mark.usefixtures('inputs')
def test_get_unspents_endpoint(b, client, user_pk):
expected = [u.to_uri('..') for u in b.get_owned_ids(user_pk)]
res = client.get(UNSPENTS_ENDPOINT + '?public_key={}'.format(user_pk))
@ -12,13 +12,11 @@ def test_get_unspents_endpoint(b, client, user_pk):
assert res.status_code == 200
@pytest.mark.usefixtures('inputs')
def test_get_unspents_endpoint_without_public_key(client):
res = client.get(UNSPENTS_ENDPOINT)
assert res.status_code == 400
@pytest.mark.usefixtures('inputs')
def test_get_unspents_endpoint_with_unused_public_key(client):
expected = []
res = client.get(UNSPENTS_ENDPOINT + '?public_key=abc')