mirror of
https://github.com/bigchaindb/bigchaindb.git
synced 2024-10-13 13:34:05 +00:00
Merge remote-tracking branch 'origin/master' into feat/746/new-naming-convention-for-keys
This commit is contained in:
commit
eead7dbdda
@ -82,7 +82,7 @@ How? Let's split the command down into its components:
|
|||||||
- `install` tells pip to use the *install* action
|
- `install` tells pip to use the *install* action
|
||||||
- `-e` installs a project in [editable mode](https://pip.pypa.io/en/stable/reference/pip_install/#editable-installs)
|
- `-e` installs a project in [editable mode](https://pip.pypa.io/en/stable/reference/pip_install/#editable-installs)
|
||||||
- `.` installs what's in the current directory
|
- `.` installs what's in the current directory
|
||||||
- `[dev]` adds some [extra requirements](https://pythonhosted.org/setuptools/setuptools.html#declaring-extras-optional-features-with-their-own-dependencies) to the installation. (If you are curious, open `setup.py` and look for `dev` in the `extras_require` section.)
|
- `[dev]` adds some [extra requirements](https://setuptools.readthedocs.io/en/latest/setuptools.html#declaring-extras-optional-features-with-their-own-dependencies) to the installation. (If you are curious, open `setup.py` and look for `dev` in the `extras_require` section.)
|
||||||
|
|
||||||
Aside: An alternative to `pip install -e .[dev]` is `python setup.py develop`.
|
Aside: An alternative to `pip install -e .[dev]` is `python setup.py develop`.
|
||||||
|
|
||||||
|
@ -65,6 +65,14 @@ 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."
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
flake8 --max-line-length 119 bigchaindb/
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Writing and Running (Python) Unit Tests
|
## Writing and Running (Python) Unit Tests
|
||||||
|
|
||||||
We write unit tests for our Python code using the [pytest](http://pytest.org/latest/) framework.
|
We write unit tests for our Python code using the [pytest](http://pytest.org/latest/) framework.
|
||||||
|
23
Release_Process.md
Normal file
23
Release_Process.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# Our Release Process
|
||||||
|
|
||||||
|
This is a summary of the steps we go through to release a new version of BigchainDB Server.
|
||||||
|
|
||||||
|
1. Update the `CHANGELOG.md` file
|
||||||
|
2. Update the version numbers in `bigchaindb/version.py`. Note that we try to use [semantic versioning](http://semver.org/) (i.e. MAJOR.MINOR.PATCH)
|
||||||
|
3. Go to the [bigchaindb/bigchaindb Releases page on GitHub](https://github.com/bigchaindb/bigchaindb/releases)
|
||||||
|
and click the "Draft a new release" button
|
||||||
|
4. Name the tag something like v0.7.0
|
||||||
|
5. The target should be a specific commit: the one when the update of `bigchaindb/version.py` got merged into master
|
||||||
|
6. The release title should be something like v0.7.0
|
||||||
|
7. The description should be copied from the `CHANGELOG.md` file updated above
|
||||||
|
8. Generate and send the latest `bigchaindb` package to PyPI. Dimi and Sylvain can do this, maybe others
|
||||||
|
9. Login to readthedocs.org as a maintainer of the BigchainDB Server docs.
|
||||||
|
Go to Admin --> Versions and under **Choose Active Versions**, make sure that the new version's tag is
|
||||||
|
"Active" and "Public"
|
||||||
|
|
||||||
|
After the release:
|
||||||
|
|
||||||
|
1. Update `bigchaindb/version.py` again, to be something like 0.8.0.dev (with a dev on the end).
|
||||||
|
This is so people reading the latest docs will know that they're for the latest (master branch)
|
||||||
|
version of BigchainDB Server, not the docs at the time of the most recent release (which are also
|
||||||
|
available).
|
@ -1,6 +1,5 @@
|
|||||||
"""Implementation of the `bigchaindb` command,
|
"""Implementation of the `bigchaindb` command,
|
||||||
which is one of the commands in the BigchainDB
|
the command-line interface (CLI) for BigchainDB Server.
|
||||||
command-line interface.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
@ -194,7 +193,7 @@ def _run_load(tx_left, stats):
|
|||||||
b = bigchaindb.Bigchain()
|
b = bigchaindb.Bigchain()
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
tx = Transaction.create([b.me], [b.me])
|
tx = Transaction.create([b.me], [([b.me], 1)])
|
||||||
tx = tx.sign([b.me_private])
|
tx = tx.sign([b.me_private])
|
||||||
b.write_transaction(tx)
|
b.write_transaction(tx)
|
||||||
|
|
||||||
|
@ -14,5 +14,6 @@ def generate_key_pair():
|
|||||||
private_key, public_key = crypto.ed25519_generate_key_pair()
|
private_key, public_key = crypto.ed25519_generate_key_pair()
|
||||||
return private_key.decode(), public_key.decode()
|
return private_key.decode(), public_key.decode()
|
||||||
|
|
||||||
|
|
||||||
PrivateKey = crypto.Ed25519SigningKey
|
PrivateKey = crypto.Ed25519SigningKey
|
||||||
PublicKey = crypto.Ed25519VerifyingKey
|
PublicKey = crypto.Ed25519VerifyingKey
|
||||||
|
@ -69,9 +69,9 @@ class CyclicBlockchainError(Exception):
|
|||||||
"""Raised when there is a cycle in the blockchain"""
|
"""Raised when there is a cycle in the blockchain"""
|
||||||
|
|
||||||
|
|
||||||
class FulfillmentNotInValidBlock(Exception):
|
class TransactionNotInValidBlock(Exception):
|
||||||
"""Raised when a transaction depends on an invalid or undecided
|
"""Raised when a transfer transaction is attempting to fulfill the
|
||||||
fulfillment"""
|
conditions of a transaction that is in an invalid or undecided block"""
|
||||||
|
|
||||||
|
|
||||||
class AssetIdMismatch(Exception):
|
class AssetIdMismatch(Exception):
|
||||||
|
@ -3,13 +3,13 @@ from functools import reduce
|
|||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
from cryptoconditions import (Fulfillment as CCFulfillment,
|
from cryptoconditions import (Fulfillment as CCFulfillment,
|
||||||
ThresholdSha256Fulfillment, Ed25519Fulfillment,
|
ThresholdSha256Fulfillment, Ed25519Fulfillment)
|
||||||
PreimageSha256Fulfillment)
|
|
||||||
from cryptoconditions.exceptions import ParsingError
|
from cryptoconditions.exceptions import ParsingError
|
||||||
|
|
||||||
from bigchaindb.common.crypto import PrivateKey, hash_data
|
from bigchaindb.common.crypto import PrivateKey, hash_data
|
||||||
from bigchaindb.common.exceptions import (KeypairMismatchException,
|
from bigchaindb.common.exceptions import (KeypairMismatchException,
|
||||||
InvalidHash, InvalidSignature)
|
InvalidHash, InvalidSignature,
|
||||||
|
AmountError, AssetIdMismatch)
|
||||||
from bigchaindb.common.util import serialize, gen_timestamp
|
from bigchaindb.common.util import serialize, gen_timestamp
|
||||||
|
|
||||||
|
|
||||||
@ -96,6 +96,14 @@ class Fulfillment(object):
|
|||||||
ffill['fid'] = fid
|
ffill['fid'] = fid
|
||||||
return ffill
|
return ffill
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def generate(cls, owners_before):
|
||||||
|
# TODO: write docstring
|
||||||
|
# The amount here does not really matter. It is only use on the
|
||||||
|
# condition data model but here we only care about the fulfillment
|
||||||
|
condition = Condition.generate(owners_before, 1)
|
||||||
|
return cls(condition.fulfillment, condition.owners_after)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, ffill):
|
def from_dict(cls, ffill):
|
||||||
"""Transforms a Python dictionary to a Fulfillment object.
|
"""Transforms a Python dictionary to a Fulfillment object.
|
||||||
@ -265,7 +273,7 @@ class Condition(object):
|
|||||||
return cond
|
return cond
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generate(cls, owners_after):
|
def generate(cls, owners_after, amount):
|
||||||
"""Generates a Condition from a specifically formed tuple or list.
|
"""Generates a Condition from a specifically formed tuple or list.
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
@ -275,34 +283,24 @@ class Condition(object):
|
|||||||
|
|
||||||
[(address|condition)*, [(address|condition)*, ...], ...]
|
[(address|condition)*, [(address|condition)*, ...], ...]
|
||||||
|
|
||||||
If however, the thresholds of individual threshold conditions
|
|
||||||
to be created have to be set specifically, a tuple of the
|
|
||||||
following structure is necessary:
|
|
||||||
|
|
||||||
([(address|condition)*,
|
|
||||||
([(address|condition)*, ...], subthreshold),
|
|
||||||
...], threshold)
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
owners_after (:obj:`list` of :obj:`str`|tuple): The users that
|
owners_after (:obj:`list` of :obj:`str`): The public key of
|
||||||
should be able to fulfill the Condition that is being
|
the users that should be able to fulfill the Condition
|
||||||
created.
|
that is being created.
|
||||||
|
amount (:obj:`int`): The amount locked by the condition.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A Condition that can be used in a Transaction.
|
A Condition that can be used in a Transaction.
|
||||||
|
|
||||||
Returns:
|
Raises:
|
||||||
TypeError: If `owners_after` is not an instance of `list`.
|
TypeError: If `owners_after` is not an instance of `list`.
|
||||||
TypeError: If `owners_after` is an empty list.
|
ValueError: If `owners_after` is an empty list.
|
||||||
"""
|
"""
|
||||||
# TODO: We probably want to remove the tuple logic for weights here
|
threshold = len(owners_after)
|
||||||
# again:
|
if not isinstance(amount, int):
|
||||||
# github.com/bigchaindb/bigchaindb/issues/730#issuecomment-255144756
|
raise TypeError('`amount` must be a int')
|
||||||
if isinstance(owners_after, tuple):
|
if amount < 1:
|
||||||
owners_after, threshold = owners_after
|
raise AmountError('`amount` needs to be greater than zero')
|
||||||
else:
|
|
||||||
threshold = len(owners_after)
|
|
||||||
|
|
||||||
if not isinstance(owners_after, list):
|
if not isinstance(owners_after, list):
|
||||||
raise TypeError('`owners_after` must be an instance of list')
|
raise TypeError('`owners_after` must be an instance of list')
|
||||||
if len(owners_after) == 0:
|
if len(owners_after) == 0:
|
||||||
@ -313,12 +311,12 @@ class Condition(object):
|
|||||||
ffill = Ed25519Fulfillment(public_key=owners_after[0])
|
ffill = Ed25519Fulfillment(public_key=owners_after[0])
|
||||||
except TypeError:
|
except TypeError:
|
||||||
ffill = owners_after[0]
|
ffill = owners_after[0]
|
||||||
return cls(ffill, owners_after)
|
return cls(ffill, owners_after, amount=amount)
|
||||||
else:
|
else:
|
||||||
initial_cond = ThresholdSha256Fulfillment(threshold=threshold)
|
initial_cond = ThresholdSha256Fulfillment(threshold=threshold)
|
||||||
threshold_cond = reduce(cls._gen_condition, owners_after,
|
threshold_cond = reduce(cls._gen_condition, owners_after,
|
||||||
initial_cond)
|
initial_cond)
|
||||||
return cls(threshold_cond, owners_after)
|
return cls(threshold_cond, owners_after, amount=amount)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _gen_condition(cls, initial, current):
|
def _gen_condition(cls, initial, current):
|
||||||
@ -338,14 +336,11 @@ class Condition(object):
|
|||||||
Returns:
|
Returns:
|
||||||
:class:`cryptoconditions.ThresholdSha256Fulfillment`:
|
:class:`cryptoconditions.ThresholdSha256Fulfillment`:
|
||||||
"""
|
"""
|
||||||
if isinstance(current, tuple):
|
owners_after = current
|
||||||
owners_after, threshold = current
|
try:
|
||||||
else:
|
threshold = len(owners_after)
|
||||||
owners_after = current
|
except TypeError:
|
||||||
try:
|
threshold = None
|
||||||
threshold = len(owners_after)
|
|
||||||
except TypeError:
|
|
||||||
threshold = None
|
|
||||||
|
|
||||||
if isinstance(owners_after, list) and len(owners_after) > 1:
|
if isinstance(owners_after, list) and len(owners_after) > 1:
|
||||||
ffill = ThresholdSha256Fulfillment(threshold=threshold)
|
ffill = ThresholdSha256Fulfillment(threshold=threshold)
|
||||||
@ -420,7 +415,7 @@ class Asset(object):
|
|||||||
self.updatable = updatable
|
self.updatable = updatable
|
||||||
self.refillable = refillable
|
self.refillable = refillable
|
||||||
|
|
||||||
self._validate_asset()
|
self.validate_asset()
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
try:
|
try:
|
||||||
@ -463,7 +458,38 @@ class Asset(object):
|
|||||||
"""Generates a unqiue uuid for an Asset"""
|
"""Generates a unqiue uuid for an Asset"""
|
||||||
return str(uuid4())
|
return str(uuid4())
|
||||||
|
|
||||||
def _validate_asset(self):
|
@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"""
|
"""Validates the asset"""
|
||||||
if self.data is not None and not isinstance(self.data, dict):
|
if self.data is not None and not isinstance(self.data, dict):
|
||||||
raise TypeError('`data` must be a dict instance or None')
|
raise TypeError('`data` must be a dict instance or None')
|
||||||
@ -474,6 +500,77 @@ class Asset(object):
|
|||||||
if not isinstance(self.updatable, bool):
|
if not isinstance(self.updatable, bool):
|
||||||
raise TypeError('`updatable` must be a boolean')
|
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):
|
||||||
|
"""Used to point to a specific Asset.
|
||||||
|
|
||||||
|
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 Metadata(object):
|
class Metadata(object):
|
||||||
"""Metadata is used to store a dictionary and its hash in a Transaction."""
|
"""Metadata is used to store a dictionary and its hash in a Transaction."""
|
||||||
@ -612,9 +709,17 @@ class Transaction(object):
|
|||||||
self.fulfillments = fulfillments if fulfillments else []
|
self.fulfillments = fulfillments if fulfillments else []
|
||||||
self.metadata = metadata
|
self.metadata = metadata
|
||||||
|
|
||||||
|
# validate asset
|
||||||
|
# we know that each transaction relates to a single asset
|
||||||
|
# we can sum the amount of all the conditions
|
||||||
|
# for transactions other then CREATE we only have an id so there is
|
||||||
|
# nothing we can validate
|
||||||
|
if self.operation == self.CREATE:
|
||||||
|
amount = sum([condition.amount for condition in self.conditions])
|
||||||
|
self.asset.validate_asset(amount=amount)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, owners_before, owners_after, metadata=None, asset=None,
|
def create(cls, owners_before, owners_after, metadata=None, asset=None):
|
||||||
secret=None, time_expire=None):
|
|
||||||
"""A simple way to generate a `CREATE` transaction.
|
"""A simple way to generate a `CREATE` transaction.
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
@ -622,7 +727,6 @@ class Transaction(object):
|
|||||||
use cases:
|
use cases:
|
||||||
- Ed25519
|
- Ed25519
|
||||||
- ThresholdSha256
|
- ThresholdSha256
|
||||||
- PreimageSha256.
|
|
||||||
|
|
||||||
Additionally, it provides support for the following BigchainDB
|
Additionally, it provides support for the following BigchainDB
|
||||||
use cases:
|
use cases:
|
||||||
@ -637,10 +741,6 @@ class Transaction(object):
|
|||||||
Transaction.
|
Transaction.
|
||||||
asset (:class:`~bigchaindb.common.transaction.Asset`): An Asset
|
asset (:class:`~bigchaindb.common.transaction.Asset`): An Asset
|
||||||
to be created in this Transaction.
|
to be created in this Transaction.
|
||||||
secret (binarystr, optional): A secret string to create a hash-
|
|
||||||
lock Condition.
|
|
||||||
time_expire (int, optional): The UNIX time a Transaction is
|
|
||||||
valid.
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
:class:`~bigchaindb.common.transaction.Transaction`
|
:class:`~bigchaindb.common.transaction.Transaction`
|
||||||
@ -649,54 +749,28 @@ class Transaction(object):
|
|||||||
raise TypeError('`owners_before` must be a list instance')
|
raise TypeError('`owners_before` must be a list instance')
|
||||||
if not isinstance(owners_after, list):
|
if not isinstance(owners_after, list):
|
||||||
raise TypeError('`owners_after` must be a list instance')
|
raise TypeError('`owners_after` must be a list instance')
|
||||||
|
if len(owners_before) == 0:
|
||||||
|
raise ValueError('`owners_before` list cannot be empty')
|
||||||
|
if len(owners_after) == 0:
|
||||||
|
raise ValueError('`owners_after` list cannot be empty')
|
||||||
|
|
||||||
metadata = Metadata(metadata)
|
metadata = Metadata(metadata)
|
||||||
if len(owners_before) == len(owners_after) and len(owners_after) == 1:
|
fulfillments = []
|
||||||
# NOTE: Standard case, one owner before, one after.
|
conditions = []
|
||||||
# NOTE: For this case its sufficient to use the same
|
|
||||||
# fulfillment for the fulfillment and condition.
|
|
||||||
ffill = Ed25519Fulfillment(public_key=owners_before[0])
|
|
||||||
ffill_tx = Fulfillment(ffill, owners_before)
|
|
||||||
cond_tx = Condition.generate(owners_after)
|
|
||||||
return cls(cls.CREATE, asset, [ffill_tx], [cond_tx], metadata)
|
|
||||||
|
|
||||||
elif len(owners_before) == len(owners_after) and len(owners_after) > 1:
|
# generate_conditions
|
||||||
raise NotImplementedError('Multiple inputs and outputs not'
|
for owner_after in owners_after:
|
||||||
'available for CREATE')
|
if not isinstance(owner_after, tuple) or len(owner_after) != 2:
|
||||||
# NOTE: Multiple inputs and outputs case. Currently not supported.
|
raise ValueError(('Each `owner_after` in the list must be a'
|
||||||
ffills = [Fulfillment(Ed25519Fulfillment(public_key=owner_before),
|
' tuple of `([<list of public keys>],'
|
||||||
[owner_before])
|
' <amount>)`'))
|
||||||
for owner_before in owners_before]
|
pub_keys, amount = owner_after
|
||||||
conds = [Condition.generate(owners) for owners in owners_after]
|
conditions.append(Condition.generate(pub_keys, amount))
|
||||||
return cls(cls.CREATE, asset, ffills, conds, metadata)
|
|
||||||
|
|
||||||
elif len(owners_before) == 1 and len(owners_after) > 1:
|
# generate fulfillments
|
||||||
# NOTE: Multiple owners case
|
fulfillments.append(Fulfillment.generate(owners_before))
|
||||||
cond_tx = Condition.generate(owners_after)
|
|
||||||
ffill = Ed25519Fulfillment(public_key=owners_before[0])
|
|
||||||
ffill_tx = Fulfillment(ffill, owners_before)
|
|
||||||
return cls(cls.CREATE, asset, [ffill_tx], [cond_tx], metadata)
|
|
||||||
|
|
||||||
elif (len(owners_before) == 1 and len(owners_after) == 0 and
|
return cls(cls.CREATE, asset, fulfillments, conditions, metadata)
|
||||||
secret is not None):
|
|
||||||
# NOTE: Hashlock condition case
|
|
||||||
hashlock = PreimageSha256Fulfillment(preimage=secret)
|
|
||||||
cond_tx = Condition(hashlock.condition_uri)
|
|
||||||
ffill = Ed25519Fulfillment(public_key=owners_before[0])
|
|
||||||
ffill_tx = Fulfillment(ffill, owners_before)
|
|
||||||
return cls(cls.CREATE, asset, [ffill_tx], [cond_tx], metadata)
|
|
||||||
|
|
||||||
elif (len(owners_before) > 0 and len(owners_after) == 0 and
|
|
||||||
time_expire is not None):
|
|
||||||
raise NotImplementedError('Timeout conditions will be implemented '
|
|
||||||
'later')
|
|
||||||
|
|
||||||
elif (len(owners_before) > 0 and len(owners_after) == 0 and
|
|
||||||
secret is None):
|
|
||||||
raise ValueError('Define a secret to create a hashlock condition')
|
|
||||||
|
|
||||||
else:
|
|
||||||
raise ValueError("These are not the cases you're looking for ;)")
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def transfer(cls, inputs, owners_after, asset, metadata=None):
|
def transfer(cls, inputs, owners_after, asset, metadata=None):
|
||||||
@ -743,17 +817,17 @@ class Transaction(object):
|
|||||||
raise ValueError('`inputs` must contain at least one item')
|
raise ValueError('`inputs` must contain at least one item')
|
||||||
if not isinstance(owners_after, list):
|
if not isinstance(owners_after, list):
|
||||||
raise TypeError('`owners_after` must be a list instance')
|
raise TypeError('`owners_after` must be a list instance')
|
||||||
|
if len(owners_after) == 0:
|
||||||
|
raise ValueError('`owners_after` list cannot be empty')
|
||||||
|
|
||||||
# NOTE: See doc strings `Note` for description.
|
conditions = []
|
||||||
if len(inputs) == len(owners_after):
|
for owner_after in owners_after:
|
||||||
if len(owners_after) == 1:
|
if not isinstance(owner_after, tuple) or len(owner_after) != 2:
|
||||||
conditions = [Condition.generate(owners_after)]
|
raise ValueError(('Each `owner_after` in the list must be a'
|
||||||
elif len(owners_after) > 1:
|
' tuple of `([<list of public keys>],'
|
||||||
conditions = [Condition.generate(owners) for owners
|
' <amount>)`'))
|
||||||
in owners_after]
|
pub_keys, amount = owner_after
|
||||||
else:
|
conditions.append(Condition.generate(pub_keys, amount))
|
||||||
raise ValueError("`inputs` and `owners_after`'s count must be the "
|
|
||||||
"same")
|
|
||||||
|
|
||||||
metadata = Metadata(metadata)
|
metadata = Metadata(metadata)
|
||||||
inputs = deepcopy(inputs)
|
inputs = deepcopy(inputs)
|
||||||
@ -786,20 +860,14 @@ class Transaction(object):
|
|||||||
:obj:`list` of :class:`~bigchaindb.common.transaction.
|
:obj:`list` of :class:`~bigchaindb.common.transaction.
|
||||||
Fulfillment`
|
Fulfillment`
|
||||||
"""
|
"""
|
||||||
inputs = []
|
# NOTE: If no condition indices are passed, we just assume to
|
||||||
if condition_indices is None or len(condition_indices) == 0:
|
# take all conditions as inputs.
|
||||||
# NOTE: If no condition indices are passed, we just assume to
|
return [
|
||||||
# take all conditions as inputs.
|
Fulfillment(self.conditions[cid].fulfillment,
|
||||||
condition_indices = [index for index, _
|
self.conditions[cid].owners_after,
|
||||||
in enumerate(self.conditions)]
|
TransactionLink(self.id, cid))
|
||||||
|
for cid in condition_indices or range(len(self.conditions))
|
||||||
for cid in condition_indices:
|
]
|
||||||
input_cond = self.conditions[cid]
|
|
||||||
ffill = Fulfillment(input_cond.fulfillment,
|
|
||||||
input_cond.owners_after,
|
|
||||||
TransactionLink(self.id, cid))
|
|
||||||
inputs.append(ffill)
|
|
||||||
return inputs
|
|
||||||
|
|
||||||
def add_fulfillment(self, fulfillment):
|
def add_fulfillment(self, fulfillment):
|
||||||
"""Adds a Fulfillment to a Transaction's list of Fulfillments.
|
"""Adds a Fulfillment to a Transaction's list of Fulfillments.
|
||||||
@ -868,13 +936,12 @@ class Transaction(object):
|
|||||||
key_pairs = {gen_public_key(PrivateKey(private_key)):
|
key_pairs = {gen_public_key(PrivateKey(private_key)):
|
||||||
PrivateKey(private_key) for private_key in private_keys}
|
PrivateKey(private_key) for private_key in private_keys}
|
||||||
|
|
||||||
zippedIO = enumerate(zip(self.fulfillments, self.conditions))
|
for index, fulfillment in enumerate(self.fulfillments):
|
||||||
for index, (fulfillment, condition) in zippedIO:
|
|
||||||
# NOTE: We clone the current transaction but only add the condition
|
# NOTE: We clone the current transaction but only add the condition
|
||||||
# and fulfillment we're currently working on plus all
|
# and fulfillment we're currently working on plus all
|
||||||
# previously signed ones.
|
# previously signed ones.
|
||||||
tx_partial = Transaction(self.operation, self.asset, [fulfillment],
|
tx_partial = Transaction(self.operation, self.asset, [fulfillment],
|
||||||
[condition], self.metadata,
|
self.conditions, self.metadata,
|
||||||
self.timestamp, self.version)
|
self.timestamp, self.version)
|
||||||
|
|
||||||
tx_partial_dict = tx_partial.to_dict()
|
tx_partial_dict = tx_partial.to_dict()
|
||||||
@ -1031,14 +1098,13 @@ class Transaction(object):
|
|||||||
"""
|
"""
|
||||||
input_condition_uris_count = len(input_condition_uris)
|
input_condition_uris_count = len(input_condition_uris)
|
||||||
fulfillments_count = len(self.fulfillments)
|
fulfillments_count = len(self.fulfillments)
|
||||||
conditions_count = len(self.conditions)
|
|
||||||
|
|
||||||
def gen_tx(fulfillment, condition, input_condition_uri=None):
|
def gen_tx(fulfillment, condition, input_condition_uri=None):
|
||||||
"""Splits multiple IO Transactions into partial single IO
|
"""Splits multiple IO Transactions into partial single IO
|
||||||
Transactions.
|
Transactions.
|
||||||
"""
|
"""
|
||||||
tx = Transaction(self.operation, self.asset, [fulfillment],
|
tx = Transaction(self.operation, self.asset, [fulfillment],
|
||||||
[condition], self.metadata, self.timestamp,
|
self.conditions, self.metadata, self.timestamp,
|
||||||
self.version)
|
self.version)
|
||||||
tx_dict = tx.to_dict()
|
tx_dict = tx.to_dict()
|
||||||
tx_dict = Transaction._remove_signatures(tx_dict)
|
tx_dict = Transaction._remove_signatures(tx_dict)
|
||||||
@ -1049,14 +1115,13 @@ class Transaction(object):
|
|||||||
tx_serialized,
|
tx_serialized,
|
||||||
input_condition_uri)
|
input_condition_uri)
|
||||||
|
|
||||||
if not fulfillments_count == conditions_count == \
|
if not fulfillments_count == input_condition_uris_count:
|
||||||
input_condition_uris_count:
|
raise ValueError('Fulfillments and '
|
||||||
raise ValueError('Fulfillments, conditions and '
|
|
||||||
'input_condition_uris must have the same count')
|
'input_condition_uris must have the same count')
|
||||||
else:
|
|
||||||
partial_transactions = map(gen_tx, self.fulfillments,
|
partial_transactions = map(gen_tx, self.fulfillments,
|
||||||
self.conditions, input_condition_uris)
|
self.conditions, input_condition_uris)
|
||||||
return all(partial_transactions)
|
return all(partial_transactions)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _fulfillment_valid(fulfillment, operation, tx_serialized,
|
def _fulfillment_valid(fulfillment, operation, tx_serialized,
|
||||||
@ -1214,7 +1279,10 @@ class Transaction(object):
|
|||||||
conditions = [Condition.from_dict(condition) for condition
|
conditions = [Condition.from_dict(condition) for condition
|
||||||
in tx['conditions']]
|
in tx['conditions']]
|
||||||
metadata = Metadata.from_dict(tx['metadata'])
|
metadata = Metadata.from_dict(tx['metadata'])
|
||||||
asset = Asset.from_dict(tx['asset'])
|
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, fulfillments, conditions,
|
return cls(tx['operation'], asset, fulfillments, conditions,
|
||||||
metadata, tx['timestamp'], tx_body['version'])
|
metadata, tx['timestamp'], tx_body['version'])
|
||||||
|
@ -6,7 +6,7 @@ from time import time
|
|||||||
from itertools import compress
|
from itertools import compress
|
||||||
from bigchaindb.common import crypto, exceptions
|
from bigchaindb.common import crypto, exceptions
|
||||||
from bigchaindb.common.util import gen_timestamp, serialize
|
from bigchaindb.common.util import gen_timestamp, serialize
|
||||||
from bigchaindb.common.transaction import TransactionLink
|
from bigchaindb.common.transaction import TransactionLink, Asset
|
||||||
|
|
||||||
import bigchaindb
|
import bigchaindb
|
||||||
|
|
||||||
@ -182,12 +182,35 @@ class Bigchain(object):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
return self.validate_transaction(transaction)
|
return self.validate_transaction(transaction)
|
||||||
except (ValueError, exceptions.OperationError, exceptions.TransactionDoesNotExist,
|
except (ValueError, exceptions.OperationError,
|
||||||
|
exceptions.TransactionDoesNotExist,
|
||||||
exceptions.TransactionOwnerError, exceptions.DoubleSpend,
|
exceptions.TransactionOwnerError, exceptions.DoubleSpend,
|
||||||
exceptions.InvalidHash, exceptions.InvalidSignature,
|
exceptions.InvalidHash, exceptions.InvalidSignature,
|
||||||
exceptions.FulfillmentNotInValidBlock):
|
exceptions.TransactionNotInValidBlock, exceptions.AmountError):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def get_block(self, block_id, include_status=False):
|
||||||
|
"""Get the block with the specified `block_id` (and optionally its status)
|
||||||
|
|
||||||
|
Returns the block corresponding to `block_id` or None if no match is
|
||||||
|
found.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
block_id (str): transaction id of the transaction to get
|
||||||
|
include_status (bool): also return the status of the block
|
||||||
|
the return value is then a tuple: (block, status)
|
||||||
|
"""
|
||||||
|
block = self.backend.get_block(block_id)
|
||||||
|
status = None
|
||||||
|
|
||||||
|
if include_status:
|
||||||
|
if block:
|
||||||
|
status = self.block_election_status(block_id,
|
||||||
|
block['block']['voters'])
|
||||||
|
return block, status
|
||||||
|
else:
|
||||||
|
return block
|
||||||
|
|
||||||
def get_transaction(self, txid, include_status=False):
|
def get_transaction(self, txid, include_status=False):
|
||||||
"""Get the transaction with the specified `txid` (and optionally its status)
|
"""Get the transaction with the specified `txid` (and optionally its status)
|
||||||
|
|
||||||
@ -343,6 +366,21 @@ class Bigchain(object):
|
|||||||
cursor = self.backend.get_transactions_by_asset_id(asset_id)
|
cursor = self.backend.get_transactions_by_asset_id(asset_id)
|
||||||
return [Transaction.from_dict(tx) for tx in cursor]
|
return [Transaction.from_dict(tx) for tx in cursor]
|
||||||
|
|
||||||
|
def get_asset_by_id(self, asset_id):
|
||||||
|
"""Returns the asset associated with an asset_id.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
asset_id (str): The asset id.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
:class:`~bigchaindb.common.transaction.Asset` if the asset
|
||||||
|
exists else None.
|
||||||
|
"""
|
||||||
|
cursor = self.backend.get_asset_by_id(asset_id)
|
||||||
|
cursor = list(cursor)
|
||||||
|
if cursor:
|
||||||
|
return Asset.from_dict(cursor[0]['transaction']['asset'])
|
||||||
|
|
||||||
def get_spent(self, txid, cid):
|
def get_spent(self, txid, cid):
|
||||||
"""Check if a `txid` was already used as an input.
|
"""Check if a `txid` was already used as an input.
|
||||||
|
|
||||||
@ -511,7 +549,7 @@ class Bigchain(object):
|
|||||||
"""Prepare a genesis block."""
|
"""Prepare a genesis block."""
|
||||||
|
|
||||||
metadata = {'message': 'Hello World from the BigchainDB'}
|
metadata = {'message': 'Hello World from the BigchainDB'}
|
||||||
transaction = Transaction.create([self.me], [self.me],
|
transaction = Transaction.create([self.me], [([self.me], 1)],
|
||||||
metadata=metadata)
|
metadata=metadata)
|
||||||
|
|
||||||
# NOTE: The transaction model doesn't expose an API to generate a
|
# NOTE: The transaction model doesn't expose an API to generate a
|
||||||
|
@ -178,7 +178,27 @@ class RethinkDBBackend:
|
|||||||
r.table('bigchain', read_mode=self.read_mode)
|
r.table('bigchain', read_mode=self.read_mode)
|
||||||
.get_all(asset_id, index='asset_id')
|
.get_all(asset_id, index='asset_id')
|
||||||
.concat_map(lambda block: block['block']['transactions'])
|
.concat_map(lambda block: block['block']['transactions'])
|
||||||
.filter(lambda transaction: transaction['transaction']['asset']['id'] == asset_id))
|
.filter(lambda transaction:
|
||||||
|
transaction['transaction']['asset']['id'] == asset_id))
|
||||||
|
|
||||||
|
def get_asset_by_id(self, asset_id):
|
||||||
|
"""Returns the asset associated with an asset_id.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
asset_id (str): The asset id.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Returns a rethinkdb cursor.
|
||||||
|
"""
|
||||||
|
return self.connection.run(
|
||||||
|
r.table('bigchain', read_mode=self.read_mode)
|
||||||
|
.get_all(asset_id, index='asset_id')
|
||||||
|
.concat_map(lambda block: block['block']['transactions'])
|
||||||
|
.filter(lambda transaction:
|
||||||
|
transaction['transaction']['asset']['id'] == asset_id)
|
||||||
|
.filter(lambda transaction:
|
||||||
|
transaction['transaction']['operation'] == 'CREATE')
|
||||||
|
.pluck({'transaction': 'asset'}))
|
||||||
|
|
||||||
def get_spent(self, transaction_id, condition_id):
|
def get_spent(self, transaction_id, condition_id):
|
||||||
"""Check if a `txid` was already used as an input.
|
"""Check if a `txid` was already used as an input.
|
||||||
@ -258,6 +278,17 @@ class RethinkDBBackend:
|
|||||||
r.table('bigchain')
|
r.table('bigchain')
|
||||||
.insert(r.json(block), durability=durability))
|
.insert(r.json(block), durability=durability))
|
||||||
|
|
||||||
|
def get_block(self, block_id):
|
||||||
|
"""Get a block from the bigchain table
|
||||||
|
|
||||||
|
Args:
|
||||||
|
block_id (str): block id of the block to get
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
block (dict): the block or `None`
|
||||||
|
"""
|
||||||
|
return self.connection.run(r.table('bigchain').get(block_id))
|
||||||
|
|
||||||
def has_transaction(self, transaction_id):
|
def has_transaction(self, transaction_id):
|
||||||
"""Check if a transaction exists in the bigchain table.
|
"""Check if a transaction exists in the bigchain table.
|
||||||
|
|
||||||
@ -282,6 +313,17 @@ class RethinkDBBackend:
|
|||||||
r.table('bigchain', read_mode=self.read_mode)
|
r.table('bigchain', read_mode=self.read_mode)
|
||||||
.count())
|
.count())
|
||||||
|
|
||||||
|
def count_backlog(self):
|
||||||
|
"""Count the number of transactions in the backlog table.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The number of transactions in the backlog.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self.connection.run(
|
||||||
|
r.table('backlog', read_mode=self.read_mode)
|
||||||
|
.count())
|
||||||
|
|
||||||
def write_vote(self, vote):
|
def write_vote(self, vote):
|
||||||
"""Write a vote to the votes table.
|
"""Write a vote to the votes table.
|
||||||
|
|
||||||
@ -295,6 +337,17 @@ class RethinkDBBackend:
|
|||||||
r.table('votes')
|
r.table('votes')
|
||||||
.insert(vote))
|
.insert(vote))
|
||||||
|
|
||||||
|
def get_genesis_block(self):
|
||||||
|
"""Get the genesis block
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The genesis block
|
||||||
|
"""
|
||||||
|
return self.connection.run(
|
||||||
|
r.table('bigchain', read_mode=self.read_mode)
|
||||||
|
.filter(util.is_genesis_block)
|
||||||
|
.nth(0))
|
||||||
|
|
||||||
def get_last_voted_block(self, node_pubkey):
|
def get_last_voted_block(self, node_pubkey):
|
||||||
"""Get the last voted block for a specific node.
|
"""Get the last voted block for a specific node.
|
||||||
|
|
||||||
@ -319,10 +372,7 @@ class RethinkDBBackend:
|
|||||||
|
|
||||||
except r.ReqlNonExistenceError:
|
except r.ReqlNonExistenceError:
|
||||||
# return last vote if last vote exists else return Genesis block
|
# return last vote if last vote exists else return Genesis block
|
||||||
return self.connection.run(
|
return self.get_genesis_block()
|
||||||
r.table('bigchain', read_mode=self.read_mode)
|
|
||||||
.filter(util.is_genesis_block)
|
|
||||||
.nth(0))
|
|
||||||
|
|
||||||
# Now the fun starts. Since the resolution of timestamp is a second,
|
# Now the fun starts. Since the resolution of timestamp is a second,
|
||||||
# we might have more than one vote per timestamp. If this is the case
|
# we might have more than one vote per timestamp. If this is the case
|
||||||
|
@ -2,42 +2,12 @@ from bigchaindb.common.crypto import hash_data, PublicKey, PrivateKey
|
|||||||
from bigchaindb.common.exceptions import (InvalidHash, InvalidSignature,
|
from bigchaindb.common.exceptions import (InvalidHash, InvalidSignature,
|
||||||
OperationError, DoubleSpend,
|
OperationError, DoubleSpend,
|
||||||
TransactionDoesNotExist,
|
TransactionDoesNotExist,
|
||||||
FulfillmentNotInValidBlock,
|
TransactionNotInValidBlock,
|
||||||
AssetIdMismatch)
|
AssetIdMismatch, AmountError)
|
||||||
from bigchaindb.common.transaction import Transaction, Asset
|
from bigchaindb.common.transaction import Transaction, Asset
|
||||||
from bigchaindb.common.util import gen_timestamp, serialize
|
from bigchaindb.common.util import gen_timestamp, serialize
|
||||||
|
|
||||||
|
|
||||||
class Asset(Asset):
|
|
||||||
@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 (list): 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 a transaction need to have the same asset id.")
|
|
||||||
return asset_ids.pop()
|
|
||||||
|
|
||||||
|
|
||||||
class Transaction(Transaction):
|
class Transaction(Transaction):
|
||||||
def validate(self, bigchain):
|
def validate(self, bigchain):
|
||||||
"""Validate a transaction.
|
"""Validate a transaction.
|
||||||
@ -54,6 +24,8 @@ class Transaction(Transaction):
|
|||||||
OperationError: if the transaction operation is not supported
|
OperationError: if the transaction operation is not supported
|
||||||
TransactionDoesNotExist: if the input of the transaction is not
|
TransactionDoesNotExist: if the input of the transaction is not
|
||||||
found
|
found
|
||||||
|
TransactionNotInValidBlock: if the input of the transaction is not
|
||||||
|
in a valid block
|
||||||
TransactionOwnerError: if the new transaction is using an input it
|
TransactionOwnerError: if the new transaction is using an input it
|
||||||
doesn't own
|
doesn't own
|
||||||
DoubleSpend: if the transaction is a double spend
|
DoubleSpend: if the transaction is a double spend
|
||||||
@ -71,7 +43,8 @@ class Transaction(Transaction):
|
|||||||
if inputs_defined:
|
if inputs_defined:
|
||||||
raise ValueError('A CREATE operation has no inputs')
|
raise ValueError('A CREATE operation has no inputs')
|
||||||
# validate asset
|
# validate asset
|
||||||
self.asset._validate_asset()
|
amount = sum([condition.amount for condition in self.conditions])
|
||||||
|
self.asset.validate_asset(amount=amount)
|
||||||
elif self.operation == Transaction.TRANSFER:
|
elif self.operation == Transaction.TRANSFER:
|
||||||
if not inputs_defined:
|
if not inputs_defined:
|
||||||
raise ValueError('Only `CREATE` transactions can have null '
|
raise ValueError('Only `CREATE` transactions can have null '
|
||||||
@ -79,6 +52,7 @@ class Transaction(Transaction):
|
|||||||
# check inputs
|
# check inputs
|
||||||
# store the inputs so that we can check if the asset ids match
|
# store the inputs so that we can check if the asset ids match
|
||||||
input_txs = []
|
input_txs = []
|
||||||
|
input_amount = 0
|
||||||
for ffill in self.fulfillments:
|
for ffill in self.fulfillments:
|
||||||
input_txid = ffill.tx_input.txid
|
input_txid = ffill.tx_input.txid
|
||||||
input_cid = ffill.tx_input.cid
|
input_cid = ffill.tx_input.cid
|
||||||
@ -90,7 +64,7 @@ class Transaction(Transaction):
|
|||||||
.format(input_txid))
|
.format(input_txid))
|
||||||
|
|
||||||
if status != bigchain.TX_VALID:
|
if status != bigchain.TX_VALID:
|
||||||
raise FulfillmentNotInValidBlock(
|
raise TransactionNotInValidBlock(
|
||||||
'input `{}` does not exist in a valid block'.format(
|
'input `{}` does not exist in a valid block'.format(
|
||||||
input_txid))
|
input_txid))
|
||||||
|
|
||||||
@ -101,11 +75,34 @@ class Transaction(Transaction):
|
|||||||
|
|
||||||
input_conditions.append(input_tx.conditions[input_cid])
|
input_conditions.append(input_tx.conditions[input_cid])
|
||||||
input_txs.append(input_tx)
|
input_txs.append(input_tx)
|
||||||
|
if input_tx.conditions[input_cid].amount < 1:
|
||||||
|
raise AmountError('`amount` needs to be greater than zero')
|
||||||
|
input_amount += input_tx.conditions[input_cid].amount
|
||||||
|
|
||||||
# validate asset id
|
# validate asset id
|
||||||
asset_id = Asset.get_asset_id(input_txs)
|
asset_id = Asset.get_asset_id(input_txs)
|
||||||
if asset_id != self.asset.data_id:
|
if asset_id != self.asset.data_id:
|
||||||
raise AssetIdMismatch('The asset id of the input does not match the asset id of the transaction')
|
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 condition in self.conditions:
|
||||||
|
if condition.amount < 1:
|
||||||
|
raise AmountError('`amount` needs to be greater than zero')
|
||||||
|
output_amount += condition.amount
|
||||||
|
|
||||||
|
if output_amount != input_amount:
|
||||||
|
raise AmountError(('The amount used in the inputs `{}`'
|
||||||
|
' needs to be same as the amount used'
|
||||||
|
' in the outputs `{}`')
|
||||||
|
.format(input_amount, output_amount))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
allowed_operations = ', '.join(Transaction.ALLOWED_OPERATIONS)
|
allowed_operations = ', '.join(Transaction.ALLOWED_OPERATIONS)
|
||||||
raise TypeError('`operation`: `{}` must be either {}.'
|
raise TypeError('`operation`: `{}` must be either {}.'
|
||||||
@ -118,8 +115,36 @@ class Transaction(Transaction):
|
|||||||
|
|
||||||
|
|
||||||
class Block(object):
|
class Block(object):
|
||||||
|
"""Bundle a list of Transactions in a Block. Nodes vote on its validity.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
transaction (:obj:`list` of :class:`~.Transaction`):
|
||||||
|
Transactions to be included in the Block.
|
||||||
|
node_pubkey (str): The public key of the node creating the
|
||||||
|
Block.
|
||||||
|
timestamp (str): The Unix time a Block was created.
|
||||||
|
voters (:obj:`list` of :obj:`str`): A list of a federation
|
||||||
|
nodes' public keys supposed to vote on the Block.
|
||||||
|
signature (str): A cryptographic signature ensuring the
|
||||||
|
integrity and validity of the creator of a Block.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, transactions=None, node_pubkey=None, timestamp=None,
|
def __init__(self, transactions=None, node_pubkey=None, timestamp=None,
|
||||||
voters=None, signature=None):
|
voters=None, signature=None):
|
||||||
|
"""The Block model is mainly used for (de)serialization and integrity
|
||||||
|
checking.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
transaction (:obj:`list` of :class:`~.Transaction`):
|
||||||
|
Transactions to be included in the Block.
|
||||||
|
node_pubkey (str): The public key of the node creating the
|
||||||
|
Block.
|
||||||
|
timestamp (str): The Unix time a Block was created.
|
||||||
|
voters (:obj:`list` of :obj:`str`): A list of a federation
|
||||||
|
nodes' public keys supposed to vote on the Block.
|
||||||
|
signature (str): A cryptographic signature ensuring the
|
||||||
|
integrity and validity of the creator of a Block.
|
||||||
|
"""
|
||||||
if transactions is not None and not isinstance(transactions, list):
|
if transactions is not None and not isinstance(transactions, list):
|
||||||
raise TypeError('`transactions` must be a list instance or None')
|
raise TypeError('`transactions` must be a list instance or None')
|
||||||
else:
|
else:
|
||||||
@ -146,18 +171,20 @@ class Block(object):
|
|||||||
return self.to_dict() == other
|
return self.to_dict() == other
|
||||||
|
|
||||||
def validate(self, bigchain):
|
def validate(self, bigchain):
|
||||||
"""Validate a block.
|
"""Validate the Block.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
bigchain (Bigchain): an instantiated bigchaindb.Bigchain object.
|
bigchain (:class:`~bigchaindb.Bigchain`): An instantiated Bigchain
|
||||||
|
object.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
block (Block): The block as a `Block` object if it is valid.
|
:class:`~.Block`: If valid, return a `Block` object. Else an
|
||||||
Else it raises an appropriate exception describing
|
appropriate exception describing the reason of invalidity is
|
||||||
the reason of invalidity.
|
raised.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
OperationError: if a non-federation node signed the block.
|
OperationError: If a non-federation node signed the Block.
|
||||||
|
InvalidSignature: If a Block's signature is invalid.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# First, make sure this node hasn't already voted on this block
|
# First, make sure this node hasn't already voted on this block
|
||||||
@ -182,6 +209,15 @@ class Block(object):
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
def sign(self, private_key):
|
def sign(self, private_key):
|
||||||
|
"""Create a signature for the Block and overwrite `self.signature`.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
private_key (str): A private key corresponding to
|
||||||
|
`self.node_pubkey`.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
:class:`~.Block`
|
||||||
|
"""
|
||||||
block_body = self.to_dict()
|
block_body = self.to_dict()
|
||||||
block_serialized = serialize(block_body['block'])
|
block_serialized = serialize(block_body['block'])
|
||||||
private_key = PrivateKey(private_key)
|
private_key = PrivateKey(private_key)
|
||||||
@ -189,8 +225,13 @@ class Block(object):
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
def is_signature_valid(self):
|
def is_signature_valid(self):
|
||||||
|
"""Check the validity of a Block's signature.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: Stating the validity of the Block's signature.
|
||||||
|
"""
|
||||||
block = self.to_dict()['block']
|
block = self.to_dict()['block']
|
||||||
# cc only accepts bytesting messages
|
# cc only accepts bytestring messages
|
||||||
block_serialized = serialize(block).encode()
|
block_serialized = serialize(block).encode()
|
||||||
public_key = PublicKey(block['node_pubkey'])
|
public_key = PublicKey(block['node_pubkey'])
|
||||||
try:
|
try:
|
||||||
@ -202,6 +243,21 @@ class Block(object):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, block_body):
|
def from_dict(cls, block_body):
|
||||||
|
"""Transform a Python dictionary to a Block object.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
block_body (dict): A block dictionary to be transformed.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
:class:`~Block`
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
InvalidHash: If the block's id is not corresponding to its
|
||||||
|
data.
|
||||||
|
InvalidSignature: If the block's signature is not corresponding
|
||||||
|
to it's data or `node_pubkey`.
|
||||||
|
"""
|
||||||
|
# TODO: Reuse `is_signature_valid` method here.
|
||||||
block = block_body['block']
|
block = block_body['block']
|
||||||
block_serialized = serialize(block)
|
block_serialized = serialize(block)
|
||||||
block_id = hash_data(block_serialized)
|
block_id = hash_data(block_serialized)
|
||||||
@ -237,6 +293,14 @@ class Block(object):
|
|||||||
return self.to_dict()['id']
|
return self.to_dict()['id']
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
|
"""Transform the Block to a Python dictionary.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: The Block as a dict.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
OperationError: If the Block doesn't contain any transactions.
|
||||||
|
"""
|
||||||
if len(self.transactions) == 0:
|
if len(self.transactions) == 0:
|
||||||
raise OperationError('Empty block creation is not allowed')
|
raise OperationError('Empty block creation is not allowed')
|
||||||
|
|
||||||
|
@ -40,10 +40,11 @@ class Vote:
|
|||||||
self.validity = {}
|
self.validity = {}
|
||||||
|
|
||||||
self.invalid_dummy_tx = Transaction.create([self.bigchain.me],
|
self.invalid_dummy_tx = Transaction.create([self.bigchain.me],
|
||||||
[self.bigchain.me])
|
[([self.bigchain.me], 1)])
|
||||||
|
|
||||||
def validate_block(self, block):
|
def validate_block(self, block):
|
||||||
if not self.bigchain.has_previous_vote(block['id'], block['block']['voters']):
|
if not self.bigchain.has_previous_vote(block['id'],
|
||||||
|
block['block']['voters']):
|
||||||
try:
|
try:
|
||||||
block = Block.from_dict(block)
|
block = Block.from_dict(block)
|
||||||
except (exceptions.InvalidHash, exceptions.InvalidSignature):
|
except (exceptions.InvalidHash, exceptions.InvalidSignature):
|
||||||
|
@ -49,19 +49,22 @@ class StandaloneApplication(gunicorn.app.base.BaseApplication):
|
|||||||
return self.application
|
return self.application
|
||||||
|
|
||||||
|
|
||||||
def create_app(settings):
|
def create_app(*, debug=False, threads=4):
|
||||||
"""Return an instance of the Flask application.
|
"""Return an instance of the Flask application.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
debug (bool): a flag to activate the debug mode for the app
|
debug (bool): a flag to activate the debug mode for the app
|
||||||
(default: False).
|
(default: False).
|
||||||
|
threads (int): number of threads to use
|
||||||
|
Return:
|
||||||
|
an instance of the Flask application.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
app.debug = settings.get('debug', False)
|
app.debug = debug
|
||||||
|
|
||||||
app.config['bigchain_pool'] = util.pool(Bigchain, size=settings.get('threads', 4))
|
app.config['bigchain_pool'] = util.pool(Bigchain, size=threads)
|
||||||
app.config['monitor'] = Monitor()
|
app.config['monitor'] = Monitor()
|
||||||
|
|
||||||
app.register_blueprint(info_views, url_prefix='/')
|
app.register_blueprint(info_views, url_prefix='/')
|
||||||
@ -88,6 +91,7 @@ def create_server(settings):
|
|||||||
if not settings.get('threads'):
|
if not settings.get('threads'):
|
||||||
settings['threads'] = (multiprocessing.cpu_count() * 2) + 1
|
settings['threads'] = (multiprocessing.cpu_count() * 2) + 1
|
||||||
|
|
||||||
app = create_app(settings)
|
app = create_app(debug=settings.get('debug', False),
|
||||||
|
threads=settings['threads'])
|
||||||
standalone = StandaloneApplication(app, settings)
|
standalone = StandaloneApplication(app, settings)
|
||||||
return standalone
|
return standalone
|
||||||
|
@ -111,6 +111,7 @@ class TransactionListApi(Resource):
|
|||||||
|
|
||||||
return tx
|
return tx
|
||||||
|
|
||||||
|
|
||||||
transaction_api.add_resource(TransactionApi,
|
transaction_api.add_resource(TransactionApi,
|
||||||
'/transactions/<string:tx_id>',
|
'/transactions/<string:tx_id>',
|
||||||
strict_slashes=False)
|
strict_slashes=False)
|
||||||
|
@ -58,6 +58,9 @@ At a high level, one can communicate with a BigchainDB cluster (set of nodes) us
|
|||||||
<div class="buttondiv">
|
<div class="buttondiv">
|
||||||
<a class="button" href="http://docs.bigchaindb.com/projects/py-driver/en/latest/index.html">Python Driver Docs</a>
|
<a class="button" href="http://docs.bigchaindb.com/projects/py-driver/en/latest/index.html">Python Driver Docs</a>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="buttondiv">
|
||||||
|
<a class="button" href="https://docs.bigchaindb.com/projects/cli/en/latest/">Command Line Transaction Tool</a>
|
||||||
|
</div>
|
||||||
<div class="buttondiv">
|
<div class="buttondiv">
|
||||||
<a class="button" href="http://docs.bigchaindb.com/projects/server/en/latest/index.html">Server Docs</a>
|
<a class="button" href="http://docs.bigchaindb.com/projects/server/en/latest/index.html">Server Docs</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,21 +3,35 @@ The HTTP Client-Server API
|
|||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
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:
|
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:
|
||||||
|
|
||||||
`http://localhost:9984/api/v1/ <http://localhost:9984/api/v1/>`_
|
`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>`.
|
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>`.
|
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>`.
|
||||||
|
|
||||||
|
|
||||||
API Root
|
API Root
|
||||||
--------
|
--------
|
||||||
|
|
||||||
If you send an HTTP GET request to e.g. ``http://localhost:9984`` (with no ``/api/v1/`` on the end), then you should get an HTTP response with something like the following in the body:
|
If you send an HTTP GET request to e.g. ``http://localhost:9984`` (with no
|
||||||
|
``/api/v1/`` on the end), then you should get an HTTP response with something
|
||||||
|
like the following in the body:
|
||||||
|
|
||||||
.. code-block:: json
|
.. code-block:: json
|
||||||
|
|
||||||
@ -39,8 +53,13 @@ POST /transactions/
|
|||||||
.. http:post:: /transactions/
|
.. http:post:: /transactions/
|
||||||
|
|
||||||
Push a new transaction.
|
Push a new transaction.
|
||||||
|
|
||||||
Note: The posted transaction should be valid `transaction <https://bigchaindb.readthedocs.io/en/latest/data-models/transaction-model.html>`_. The steps to build a valid transaction are beyond the scope of this page. One would normally use a driver such as the `BigchainDB Python Driver <https://docs.bigchaindb.com/projects/py-driver/en/latest/index.html>`_ to build a valid transaction.
|
Note: The posted transaction should be valid `transaction
|
||||||
|
<https://bigchaindb.readthedocs.io/en/latest/data-models/transaction-model.html>`_.
|
||||||
|
The steps to build a valid transaction are beyond the scope of this page.
|
||||||
|
One would normally use a driver such as the `BigchainDB Python Driver
|
||||||
|
<https://docs.bigchaindb.com/projects/py-driver/en/latest/index.html>`_ to
|
||||||
|
build a valid transaction.
|
||||||
|
|
||||||
**Example request**:
|
**Example request**:
|
||||||
|
|
||||||
@ -158,9 +177,11 @@ GET /transactions/{tx_id}/status
|
|||||||
|
|
||||||
.. http:get:: /transactions/{tx_id}/status
|
.. http:get:: /transactions/{tx_id}/status
|
||||||
|
|
||||||
Get the status of the transaction with the ID ``tx_id``, if a transaction with that ``tx_id`` exists.
|
Get the status of the transaction with the ID ``tx_id``, if a transaction
|
||||||
|
with that ``tx_id`` exists.
|
||||||
|
|
||||||
The possible status values are ``backlog``, ``undecided``, ``valid`` or ``invalid``.
|
The possible status values are ``backlog``, ``undecided``, ``valid`` or
|
||||||
|
``invalid``.
|
||||||
|
|
||||||
:param tx_id: transaction ID
|
:param tx_id: transaction ID
|
||||||
:type tx_id: hex string
|
:type tx_id: hex string
|
||||||
@ -194,7 +215,8 @@ GET /transactions/{tx_id}
|
|||||||
|
|
||||||
Get the transaction with the ID ``tx_id``.
|
Get the transaction with the ID ``tx_id``.
|
||||||
|
|
||||||
This endpoint returns only a transaction from a ``VALID`` or ``UNDECIDED`` block on ``bigchain``, if exists.
|
This endpoint returns only a transaction from a ``VALID`` or ``UNDECIDED``
|
||||||
|
block on ``bigchain``, if exists.
|
||||||
|
|
||||||
:param tx_id: transaction ID
|
:param tx_id: transaction ID
|
||||||
:type tx_id: hex string
|
:type tx_id: hex string
|
||||||
@ -260,4 +282,46 @@ GET /transactions/{tx_id}
|
|||||||
}
|
}
|
||||||
|
|
||||||
:statuscode 200: A transaction with that ID was found.
|
:statuscode 200: A transaction with that ID was found.
|
||||||
:statuscode 404: A transaction with that ID was not found.
|
:statuscode 404: A transaction with that ID was not found.
|
||||||
|
|
||||||
|
|
||||||
|
GET /unspents/
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
.. http:get:: /unspents?owner_after={owner_after}
|
||||||
|
|
||||||
|
Get a list of links to transactions' conditions that have not been used in
|
||||||
|
a previous transaction and could hence be called unspent conditions/outputs
|
||||||
|
(or simply: unspents).
|
||||||
|
|
||||||
|
This endpoint will return a ``HTTP 400 Bad Request`` if the querystring
|
||||||
|
``owner_after`` happens to not be defined in the request.
|
||||||
|
|
||||||
|
Note that if unspents for a certain ``owner_after`` have not been found by
|
||||||
|
the server, this will result in the server returning a 200 OK HTTP status
|
||||||
|
code and an empty list in the response's body.
|
||||||
|
|
||||||
|
:param owner_after: A public key, able to validly spend an output of a transaction, assuming the user also has the corresponding private key.
|
||||||
|
:type owner_after: base58 encoded string
|
||||||
|
|
||||||
|
**Example request**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
GET /unspents?owner_after=1AAAbbb...ccc HTTP/1.1
|
||||||
|
Host: example.com
|
||||||
|
|
||||||
|
**Example response**:
|
||||||
|
|
||||||
|
.. sourcecode:: http
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
[
|
||||||
|
'../transactions/2d431073e1477f3073a4693ac7ff9be5634751de1b8abaa1f4e19548ef0b4b0e/conditions/0',
|
||||||
|
'../transactions/2d431073e1477f3073a4693ac7ff9be5634751de1b8abaa1f4e19548ef0b4b0e/conditions/1'
|
||||||
|
]
|
||||||
|
|
||||||
|
: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.
|
||||||
|
@ -1,10 +1,17 @@
|
|||||||
Drivers & Clients
|
Drivers & Clients
|
||||||
=================
|
=================
|
||||||
|
|
||||||
|
Currently, the only language-native driver is written in the Python language.
|
||||||
|
|
||||||
|
We also provide the Transaction CLI to be able to script the building of
|
||||||
|
transactions. You may be able to wrap this tool inside the language of
|
||||||
|
your choice, and then use the HTTP API directly to post transactions.
|
||||||
|
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
||||||
http-client-server-api
|
http-client-server-api
|
||||||
python-driver
|
The Python Driver <https://docs.bigchaindb.com/projects/py-driver/en/latest/index.html>
|
||||||
|
Transaction CLI <https://docs.bigchaindb.com/projects/cli/en/latest/>
|
||||||
example-apps
|
example-apps
|
||||||
|
|
@ -1,7 +0,0 @@
|
|||||||
# The Python Driver
|
|
||||||
|
|
||||||
The BigchainDB Python Driver is a Python wrapper around the [HTTP Client-Server API](http-client-server-api.html). A developer can use it to develop a Python app that communicates with one or more BigchainDB clusters.
|
|
||||||
|
|
||||||
The BigchainDB Python Driver documentation is at:
|
|
||||||
|
|
||||||
[http://docs.bigchaindb.com/projects/py-driver/en/latest/index.html](http://docs.bigchaindb.com/projects/py-driver/en/latest/index.html)
|
|
@ -1,5 +1,7 @@
|
|||||||
import pytest
|
import pytest
|
||||||
from ..db.conftest import inputs
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from ..db.conftest import inputs # noqa
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures('inputs')
|
@pytest.mark.usefixtures('inputs')
|
||||||
@ -9,7 +11,7 @@ def test_asset_transfer(b, user_pk, user_sk):
|
|||||||
tx_input = b.get_owned_ids(user_pk).pop()
|
tx_input = b.get_owned_ids(user_pk).pop()
|
||||||
tx_create = b.get_transaction(tx_input.txid)
|
tx_create = b.get_transaction(tx_input.txid)
|
||||||
|
|
||||||
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [user_pk],
|
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([user_pk], 1)],
|
||||||
tx_create.asset)
|
tx_create.asset)
|
||||||
tx_transfer_signed = tx_transfer.sign([user_sk])
|
tx_transfer_signed = tx_transfer.sign([user_sk])
|
||||||
|
|
||||||
@ -18,61 +20,40 @@ def test_asset_transfer(b, user_pk, user_sk):
|
|||||||
|
|
||||||
|
|
||||||
def test_validate_bad_asset_creation(b, user_pk):
|
def test_validate_bad_asset_creation(b, user_pk):
|
||||||
from bigchaindb.models import Transaction
|
from bigchaindb.models import Transaction, Asset
|
||||||
|
|
||||||
# `divisible` needs to be a boolean
|
# `divisible` needs to be a boolean
|
||||||
tx = Transaction.create([b.me], [user_pk])
|
tx = Transaction.create([b.me], [([user_pk], 1)])
|
||||||
tx.asset.divisible = 1
|
tx.asset.divisible = 1
|
||||||
tx_signed = tx.sign([b.me_private])
|
with patch.object(Asset, 'validate_asset', return_value=None):
|
||||||
|
tx_signed = tx.sign([b.me_private])
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
tx_signed.validate(b)
|
tx_signed.validate(b)
|
||||||
|
|
||||||
# `refillable` needs to be a boolean
|
# `refillable` needs to be a boolean
|
||||||
tx = Transaction.create([b.me], [user_pk])
|
tx = Transaction.create([b.me], [([user_pk], 1)])
|
||||||
tx.asset.refillable = 1
|
tx.asset.refillable = 1
|
||||||
tx_signed = tx.sign([b.me_private])
|
with patch.object(Asset, 'validate_asset', return_value=None):
|
||||||
|
tx_signed = tx.sign([b.me_private])
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
b.validate_transaction(tx_signed)
|
b.validate_transaction(tx_signed)
|
||||||
|
|
||||||
# `updatable` needs to be a boolean
|
# `updatable` needs to be a boolean
|
||||||
tx = Transaction.create([b.me], [user_pk])
|
tx = Transaction.create([b.me], [([user_pk], 1)])
|
||||||
tx.asset.updatable = 1
|
tx.asset.updatable = 1
|
||||||
tx_signed = tx.sign([b.me_private])
|
with patch.object(Asset, 'validate_asset', return_value=None):
|
||||||
|
tx_signed = tx.sign([b.me_private])
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
b.validate_transaction(tx_signed)
|
b.validate_transaction(tx_signed)
|
||||||
|
|
||||||
# `data` needs to be a dictionary
|
# `data` needs to be a dictionary
|
||||||
tx = Transaction.create([b.me], [user_pk])
|
tx = Transaction.create([b.me], [([user_pk], 1)])
|
||||||
tx.asset.data = 'a'
|
tx.asset.data = 'a'
|
||||||
tx_signed = tx.sign([b.me_private])
|
with patch.object(Asset, 'validate_asset', return_value=None):
|
||||||
|
tx_signed = tx.sign([b.me_private])
|
||||||
with pytest.raises(TypeError):
|
with pytest.raises(TypeError):
|
||||||
b.validate_transaction(tx_signed)
|
b.validate_transaction(tx_signed)
|
||||||
|
|
||||||
# TODO: Check where to test for the amount
|
|
||||||
"""
|
|
||||||
tx = b.create_transaction(b.me, user_pk, None, 'CREATE')
|
|
||||||
tx['transaction']['conditions'][0]['amount'] = 'a'
|
|
||||||
tx['id'] = get_hash_data(tx['transaction'])
|
|
||||||
tx_signed = b.sign_transaction(tx, b.me_private)
|
|
||||||
with pytest.raises(TypeError):
|
|
||||||
b.validate_transaction(tx_signed)
|
|
||||||
|
|
||||||
tx = b.create_transaction(b.me, user_pk, None, 'CREATE')
|
|
||||||
tx['transaction']['conditions'][0]['amount'] = 2
|
|
||||||
tx['transaction']['asset'].update({'divisible': False})
|
|
||||||
tx['id'] = get_hash_data(tx['transaction'])
|
|
||||||
tx_signed = b.sign_transaction(tx, b.me_private)
|
|
||||||
with pytest.raises(AmountError):
|
|
||||||
b.validate_transaction(tx_signed)
|
|
||||||
|
|
||||||
tx = b.create_transaction(b.me, user_pk, None, 'CREATE')
|
|
||||||
tx['transaction']['conditions'][0]['amount'] = 0
|
|
||||||
tx['id'] = get_hash_data(tx['transaction'])
|
|
||||||
tx_signed = b.sign_transaction(tx, b.me_private)
|
|
||||||
with pytest.raises(AmountError):
|
|
||||||
b.validate_transaction(tx_signed)
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures('inputs')
|
@pytest.mark.usefixtures('inputs')
|
||||||
def test_validate_transfer_asset_id_mismatch(b, user_pk, user_sk):
|
def test_validate_transfer_asset_id_mismatch(b, user_pk, user_sk):
|
||||||
@ -81,7 +62,7 @@ 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_owned_ids(user_pk).pop()
|
||||||
tx_create = b.get_transaction(tx_create.txid)
|
tx_create = b.get_transaction(tx_create.txid)
|
||||||
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [user_pk],
|
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([user_pk], 1)],
|
||||||
tx_create.asset)
|
tx_create.asset)
|
||||||
tx_transfer.asset.data_id = 'aaa'
|
tx_transfer.asset.data_id = 'aaa'
|
||||||
tx_transfer_signed = tx_transfer.sign([user_sk])
|
tx_transfer_signed = tx_transfer.sign([user_sk])
|
||||||
@ -92,7 +73,7 @@ def test_validate_transfer_asset_id_mismatch(b, user_pk, user_sk):
|
|||||||
def test_get_asset_id_create_transaction(b, user_pk):
|
def test_get_asset_id_create_transaction(b, user_pk):
|
||||||
from bigchaindb.models import Transaction, Asset
|
from bigchaindb.models import Transaction, Asset
|
||||||
|
|
||||||
tx_create = Transaction.create([b.me], [user_pk])
|
tx_create = Transaction.create([b.me], [([user_pk], 1)])
|
||||||
asset_id = Asset.get_asset_id(tx_create)
|
asset_id = Asset.get_asset_id(tx_create)
|
||||||
|
|
||||||
assert asset_id == tx_create.asset.data_id
|
assert asset_id == tx_create.asset.data_id
|
||||||
@ -105,7 +86,7 @@ def test_get_asset_id_transfer_transaction(b, user_pk, user_sk):
|
|||||||
tx_create = b.get_owned_ids(user_pk).pop()
|
tx_create = b.get_owned_ids(user_pk).pop()
|
||||||
tx_create = b.get_transaction(tx_create.txid)
|
tx_create = b.get_transaction(tx_create.txid)
|
||||||
# create a transfer transaction
|
# create a transfer transaction
|
||||||
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [user_pk],
|
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([user_pk], 1)],
|
||||||
tx_create.asset)
|
tx_create.asset)
|
||||||
tx_transfer_signed = tx_transfer.sign([user_sk])
|
tx_transfer_signed = tx_transfer.sign([user_sk])
|
||||||
# create a block
|
# create a block
|
||||||
@ -123,8 +104,8 @@ def test_asset_id_mismatch(b, user_pk):
|
|||||||
from bigchaindb.models import Transaction, Asset
|
from bigchaindb.models import Transaction, Asset
|
||||||
from bigchaindb.common.exceptions import AssetIdMismatch
|
from bigchaindb.common.exceptions import AssetIdMismatch
|
||||||
|
|
||||||
tx1 = Transaction.create([b.me], [user_pk])
|
tx1 = Transaction.create([b.me], [([user_pk], 1)])
|
||||||
tx2 = Transaction.create([b.me], [user_pk])
|
tx2 = Transaction.create([b.me], [([user_pk], 1)])
|
||||||
|
|
||||||
with pytest.raises(AssetIdMismatch):
|
with pytest.raises(AssetIdMismatch):
|
||||||
Asset.get_asset_id([tx1, tx2])
|
Asset.get_asset_id([tx1, tx2])
|
||||||
@ -144,7 +125,7 @@ def test_get_txs_by_asset_id(b, user_pk, user_sk):
|
|||||||
assert txs[0].asset.data_id == asset_id
|
assert txs[0].asset.data_id == asset_id
|
||||||
|
|
||||||
# create a transfer transaction
|
# create a transfer transaction
|
||||||
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [user_pk],
|
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([user_pk], 1)],
|
||||||
tx_create.asset)
|
tx_create.asset)
|
||||||
tx_transfer_signed = tx_transfer.sign([user_sk])
|
tx_transfer_signed = tx_transfer.sign([user_sk])
|
||||||
# create the block
|
# create the block
|
||||||
@ -161,3 +142,73 @@ def test_get_txs_by_asset_id(b, user_pk, user_sk):
|
|||||||
assert tx_transfer.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[0].asset.data_id
|
||||||
assert asset_id == txs[1].asset.data_id
|
assert asset_id == txs[1].asset.data_id
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures('inputs')
|
||||||
|
def test_get_asset_by_id(b, user_vk, user_sk):
|
||||||
|
from bigchaindb.models import Transaction
|
||||||
|
|
||||||
|
tx_create = b.get_owned_ids(user_vk).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_vk], 1)],
|
||||||
|
tx_create.asset)
|
||||||
|
tx_transfer_signed = tx_transfer.sign([user_sk])
|
||||||
|
# create the block
|
||||||
|
block = b.create_block([tx_transfer_signed])
|
||||||
|
b.write_block(block, durability='hard')
|
||||||
|
# vote the block valid
|
||||||
|
vote = b.vote(block.id, b.get_last_voted_block().id, True)
|
||||||
|
b.write_vote(vote)
|
||||||
|
|
||||||
|
txs = b.get_txs_by_asset_id(asset_id)
|
||||||
|
assert len(txs) == 2
|
||||||
|
|
||||||
|
asset = b.get_asset_by_id(asset_id)
|
||||||
|
assert asset == tx_create.asset
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_invalid_divisible_asset(b, user_vk, user_sk):
|
||||||
|
from bigchaindb.models import Transaction, Asset
|
||||||
|
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_vk], [([user_vk], 2)], asset=asset)
|
||||||
|
|
||||||
|
# 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_vk], [([user_vk], 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_vk], [([user_vk], 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_vk], [([user_vk], 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
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_valid_divisible_asset(b, user_vk, user_sk):
|
||||||
|
from bigchaindb.models import Transaction, Asset
|
||||||
|
|
||||||
|
asset = Asset(divisible=True)
|
||||||
|
tx = Transaction.create([user_vk], [([user_vk], 2)], asset=asset)
|
||||||
|
tx_signed = tx.sign([user_sk])
|
||||||
|
assert b.is_valid_transaction(tx_signed)
|
||||||
|
779
tests/assets/test_divisible_assets.py
Normal file
779
tests/assets/test_divisible_assets.py
Normal file
@ -0,0 +1,779 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from ..db.conftest import inputs # noqa
|
||||||
|
|
||||||
|
|
||||||
|
# CREATE divisible asset
|
||||||
|
# Single input
|
||||||
|
# Single owners_before
|
||||||
|
# Single output
|
||||||
|
# Single owners_after
|
||||||
|
def test_single_in_single_own_single_out_single_own_create(b, user_vk):
|
||||||
|
from bigchaindb.models import Transaction
|
||||||
|
from bigchaindb.common.transaction import Asset
|
||||||
|
|
||||||
|
asset = Asset(divisible=True)
|
||||||
|
tx = Transaction.create([b.me], [([user_vk], 100)], asset=asset)
|
||||||
|
tx_signed = tx.sign([b.me_private])
|
||||||
|
|
||||||
|
assert tx_signed.validate(b) == tx_signed
|
||||||
|
assert len(tx_signed.conditions) == 1
|
||||||
|
assert tx_signed.conditions[0].amount == 100
|
||||||
|
assert len(tx_signed.fulfillments) == 1
|
||||||
|
|
||||||
|
|
||||||
|
# CREATE divisible asset
|
||||||
|
# Single input
|
||||||
|
# Single owners_before
|
||||||
|
# Multiple outputs
|
||||||
|
# Single owners_after per output
|
||||||
|
def test_single_in_single_own_multiple_out_single_own_create(b, user_vk):
|
||||||
|
from bigchaindb.models import Transaction
|
||||||
|
from bigchaindb.common.transaction import Asset
|
||||||
|
|
||||||
|
asset = Asset(divisible=True)
|
||||||
|
tx = Transaction.create([b.me], [([user_vk], 50), ([user_vk], 50)],
|
||||||
|
asset=asset)
|
||||||
|
tx_signed = tx.sign([b.me_private])
|
||||||
|
|
||||||
|
assert tx_signed.validate(b) == tx_signed
|
||||||
|
assert len(tx_signed.conditions) == 2
|
||||||
|
assert tx_signed.conditions[0].amount == 50
|
||||||
|
assert tx_signed.conditions[1].amount == 50
|
||||||
|
assert len(tx_signed.fulfillments) == 1
|
||||||
|
|
||||||
|
|
||||||
|
# CREATE divisible asset
|
||||||
|
# Single input
|
||||||
|
# Single owners_before
|
||||||
|
# Single output
|
||||||
|
# Multiple owners_after
|
||||||
|
def test_single_in_single_own_single_out_multiple_own_create(b, user_vk):
|
||||||
|
from bigchaindb.models import Transaction
|
||||||
|
from bigchaindb.common.transaction import Asset
|
||||||
|
|
||||||
|
asset = Asset(divisible=True)
|
||||||
|
tx = Transaction.create([b.me], [([user_vk, user_vk], 100)], asset=asset)
|
||||||
|
tx_signed = tx.sign([b.me_private])
|
||||||
|
|
||||||
|
assert tx_signed.validate(b) == tx_signed
|
||||||
|
assert len(tx_signed.conditions) == 1
|
||||||
|
assert tx_signed.conditions[0].amount == 100
|
||||||
|
|
||||||
|
condition = tx_signed.conditions[0].to_dict()
|
||||||
|
assert 'subfulfillments' in condition['condition']['details']
|
||||||
|
assert len(condition['condition']['details']['subfulfillments']) == 2
|
||||||
|
|
||||||
|
assert len(tx_signed.fulfillments) == 1
|
||||||
|
|
||||||
|
|
||||||
|
# CREATE divisible asset
|
||||||
|
# Single input
|
||||||
|
# Single owners_before
|
||||||
|
# Multiple outputs
|
||||||
|
# Mix: one output with a single owners_after, one output with multiple
|
||||||
|
# owners_after
|
||||||
|
def test_single_in_single_own_multiple_out_mix_own_create(b, user_vk):
|
||||||
|
from bigchaindb.models import Transaction
|
||||||
|
from bigchaindb.common.transaction import Asset
|
||||||
|
|
||||||
|
asset = Asset(divisible=True)
|
||||||
|
tx = Transaction.create([b.me],
|
||||||
|
[([user_vk], 50), ([user_vk, user_vk], 50)],
|
||||||
|
asset=asset)
|
||||||
|
tx_signed = tx.sign([b.me_private])
|
||||||
|
|
||||||
|
assert tx_signed.validate(b) == tx_signed
|
||||||
|
assert len(tx_signed.conditions) == 2
|
||||||
|
assert tx_signed.conditions[0].amount == 50
|
||||||
|
assert tx_signed.conditions[1].amount == 50
|
||||||
|
|
||||||
|
condition_cid1 = tx_signed.conditions[1].to_dict()
|
||||||
|
assert 'subfulfillments' in condition_cid1['condition']['details']
|
||||||
|
assert len(condition_cid1['condition']['details']['subfulfillments']) == 2
|
||||||
|
|
||||||
|
assert len(tx_signed.fulfillments) == 1
|
||||||
|
|
||||||
|
|
||||||
|
# CREATE divisible asset
|
||||||
|
# Single input
|
||||||
|
# Multiple owners_before
|
||||||
|
# Output combinations already tested above
|
||||||
|
def test_single_in_multiple_own_single_out_single_own_create(b, user_vk,
|
||||||
|
user_sk):
|
||||||
|
from bigchaindb.models import Transaction
|
||||||
|
from bigchaindb.common.transaction import Asset
|
||||||
|
|
||||||
|
asset = Asset(divisible=True)
|
||||||
|
tx = Transaction.create([b.me, user_vk], [([user_vk], 100)], asset=asset)
|
||||||
|
tx_signed = tx.sign([b.me_private, user_sk])
|
||||||
|
assert tx_signed.validate(b) == tx_signed
|
||||||
|
assert len(tx_signed.conditions) == 1
|
||||||
|
assert tx_signed.conditions[0].amount == 100
|
||||||
|
assert len(tx_signed.fulfillments) == 1
|
||||||
|
|
||||||
|
ffill = tx_signed.fulfillments[0].fulfillment.to_dict()
|
||||||
|
assert 'subfulfillments' in ffill
|
||||||
|
assert len(ffill['subfulfillments']) == 2
|
||||||
|
|
||||||
|
|
||||||
|
# TRANSFER divisible asset
|
||||||
|
# Single input
|
||||||
|
# Single owners_before
|
||||||
|
# Single output
|
||||||
|
# Single owners_after
|
||||||
|
# TODO: I don't really need inputs. But I need the database to be setup or
|
||||||
|
# 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.usefixtures('inputs')
|
||||||
|
def test_single_in_single_own_single_out_single_own_transfer(b, user_vk,
|
||||||
|
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_vk], 100)], asset=asset)
|
||||||
|
tx_create_signed = tx_create.sign([b.me_private])
|
||||||
|
# create block
|
||||||
|
block = b.create_block([tx_create_signed])
|
||||||
|
assert block.validate(b) == block
|
||||||
|
b.write_block(block, durability='hard')
|
||||||
|
# vote
|
||||||
|
vote = b.vote(block.id, b.get_last_voted_block().id, True)
|
||||||
|
b.write_vote(vote)
|
||||||
|
|
||||||
|
# TRANSFER
|
||||||
|
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([b.me], 100)],
|
||||||
|
asset=tx_create.asset)
|
||||||
|
tx_transfer_signed = tx_transfer.sign([user_sk])
|
||||||
|
|
||||||
|
assert tx_transfer_signed.validate(b)
|
||||||
|
assert len(tx_transfer_signed.conditions) == 1
|
||||||
|
assert tx_transfer_signed.conditions[0].amount == 100
|
||||||
|
assert len(tx_transfer_signed.fulfillments) == 1
|
||||||
|
|
||||||
|
|
||||||
|
# TRANSFER divisible asset
|
||||||
|
# Single input
|
||||||
|
# Single owners_before
|
||||||
|
# Multiple output
|
||||||
|
# Single owners_after
|
||||||
|
@pytest.mark.usefixtures('inputs')
|
||||||
|
def test_single_in_single_own_multiple_out_single_own_transfer(b, user_vk,
|
||||||
|
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_vk], 100)], asset=asset)
|
||||||
|
tx_create_signed = tx_create.sign([b.me_private])
|
||||||
|
# create block
|
||||||
|
block = b.create_block([tx_create_signed])
|
||||||
|
assert block.validate(b) == block
|
||||||
|
b.write_block(block, durability='hard')
|
||||||
|
# vote
|
||||||
|
vote = b.vote(block.id, b.get_last_voted_block().id, True)
|
||||||
|
b.write_vote(vote)
|
||||||
|
|
||||||
|
# TRANSFER
|
||||||
|
tx_transfer = Transaction.transfer(tx_create.to_inputs(),
|
||||||
|
[([b.me], 50), ([b.me], 50)],
|
||||||
|
asset=tx_create.asset)
|
||||||
|
tx_transfer_signed = tx_transfer.sign([user_sk])
|
||||||
|
|
||||||
|
assert tx_transfer_signed.validate(b) == tx_transfer_signed
|
||||||
|
assert len(tx_transfer_signed.conditions) == 2
|
||||||
|
assert tx_transfer_signed.conditions[0].amount == 50
|
||||||
|
assert tx_transfer_signed.conditions[1].amount == 50
|
||||||
|
assert len(tx_transfer_signed.fulfillments) == 1
|
||||||
|
|
||||||
|
|
||||||
|
# TRANSFER divisible asset
|
||||||
|
# Single input
|
||||||
|
# Single owners_before
|
||||||
|
# Single output
|
||||||
|
# Multiple owners_after
|
||||||
|
@pytest.mark.usefixtures('inputs')
|
||||||
|
def test_single_in_single_own_single_out_multiple_own_transfer(b, user_vk,
|
||||||
|
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_vk], 100)], asset=asset)
|
||||||
|
tx_create_signed = tx_create.sign([b.me_private])
|
||||||
|
# create block
|
||||||
|
block = b.create_block([tx_create_signed])
|
||||||
|
assert block.validate(b) == block
|
||||||
|
b.write_block(block, durability='hard')
|
||||||
|
# vote
|
||||||
|
vote = b.vote(block.id, b.get_last_voted_block().id, True)
|
||||||
|
b.write_vote(vote)
|
||||||
|
|
||||||
|
# TRANSFER
|
||||||
|
tx_transfer = Transaction.transfer(tx_create.to_inputs(),
|
||||||
|
[([b.me, b.me], 100)],
|
||||||
|
asset=tx_create.asset)
|
||||||
|
tx_transfer_signed = tx_transfer.sign([user_sk])
|
||||||
|
|
||||||
|
assert tx_transfer_signed.validate(b) == tx_transfer_signed
|
||||||
|
assert len(tx_transfer_signed.conditions) == 1
|
||||||
|
assert tx_transfer_signed.conditions[0].amount == 100
|
||||||
|
|
||||||
|
condition = tx_transfer_signed.conditions[0].to_dict()
|
||||||
|
assert 'subfulfillments' in condition['condition']['details']
|
||||||
|
assert len(condition['condition']['details']['subfulfillments']) == 2
|
||||||
|
|
||||||
|
assert len(tx_transfer_signed.fulfillments) == 1
|
||||||
|
|
||||||
|
|
||||||
|
# TRANSFER divisible asset
|
||||||
|
# Single input
|
||||||
|
# Single owners_before
|
||||||
|
# Multiple outputs
|
||||||
|
# Mix: one output with a single owners_after, one output with multiple
|
||||||
|
# owners_after
|
||||||
|
@pytest.mark.usefixtures('inputs')
|
||||||
|
def test_single_in_single_own_multiple_out_mix_own_transfer(b, user_vk,
|
||||||
|
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_vk], 100)], asset=asset)
|
||||||
|
tx_create_signed = tx_create.sign([b.me_private])
|
||||||
|
# create block
|
||||||
|
block = b.create_block([tx_create_signed])
|
||||||
|
assert block.validate(b) == block
|
||||||
|
b.write_block(block, durability='hard')
|
||||||
|
# vote
|
||||||
|
vote = b.vote(block.id, b.get_last_voted_block().id, True)
|
||||||
|
b.write_vote(vote)
|
||||||
|
|
||||||
|
# TRANSFER
|
||||||
|
tx_transfer = Transaction.transfer(tx_create.to_inputs(),
|
||||||
|
[([b.me], 50), ([b.me, b.me], 50)],
|
||||||
|
asset=tx_create.asset)
|
||||||
|
tx_transfer_signed = tx_transfer.sign([user_sk])
|
||||||
|
|
||||||
|
assert tx_transfer_signed.validate(b) == tx_transfer_signed
|
||||||
|
assert len(tx_transfer_signed.conditions) == 2
|
||||||
|
assert tx_transfer_signed.conditions[0].amount == 50
|
||||||
|
assert tx_transfer_signed.conditions[1].amount == 50
|
||||||
|
|
||||||
|
condition_cid1 = tx_transfer_signed.conditions[1].to_dict()
|
||||||
|
assert 'subfulfillments' in condition_cid1['condition']['details']
|
||||||
|
assert len(condition_cid1['condition']['details']['subfulfillments']) == 2
|
||||||
|
|
||||||
|
assert len(tx_transfer_signed.fulfillments) == 1
|
||||||
|
|
||||||
|
|
||||||
|
# TRANSFER divisible asset
|
||||||
|
# Single input
|
||||||
|
# Multiple owners_before
|
||||||
|
# Single output
|
||||||
|
# Single owners_after
|
||||||
|
@pytest.mark.usefixtures('inputs')
|
||||||
|
def test_single_in_multiple_own_single_out_single_own_transfer(b, user_vk,
|
||||||
|
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_vk], 100)],
|
||||||
|
asset=asset)
|
||||||
|
tx_create_signed = tx_create.sign([b.me_private])
|
||||||
|
# create block
|
||||||
|
block = b.create_block([tx_create_signed])
|
||||||
|
assert block.validate(b) == block
|
||||||
|
b.write_block(block, durability='hard')
|
||||||
|
# vote
|
||||||
|
vote = b.vote(block.id, b.get_last_voted_block().id, True)
|
||||||
|
b.write_vote(vote)
|
||||||
|
|
||||||
|
# TRANSFER
|
||||||
|
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([b.me], 100)],
|
||||||
|
asset=tx_create.asset)
|
||||||
|
tx_transfer_signed = tx_transfer.sign([b.me_private, user_sk])
|
||||||
|
|
||||||
|
assert tx_transfer_signed.validate(b) == tx_transfer_signed
|
||||||
|
assert len(tx_transfer_signed.conditions) == 1
|
||||||
|
assert tx_transfer_signed.conditions[0].amount == 100
|
||||||
|
assert len(tx_transfer_signed.fulfillments) == 1
|
||||||
|
|
||||||
|
ffill = tx_transfer_signed.fulfillments[0].fulfillment.to_dict()
|
||||||
|
assert 'subfulfillments' in ffill
|
||||||
|
assert len(ffill['subfulfillments']) == 2
|
||||||
|
|
||||||
|
|
||||||
|
# TRANSFER divisible asset
|
||||||
|
# Multiple inputs
|
||||||
|
# Single owners_before per input
|
||||||
|
# Single output
|
||||||
|
# Single owners_after
|
||||||
|
@pytest.mark.usefixtures('inputs')
|
||||||
|
def test_multiple_in_single_own_single_out_single_own_transfer(b, user_vk,
|
||||||
|
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_vk], 50), ([user_vk], 50)],
|
||||||
|
asset=asset)
|
||||||
|
tx_create_signed = tx_create.sign([b.me_private])
|
||||||
|
# create block
|
||||||
|
block = b.create_block([tx_create_signed])
|
||||||
|
assert block.validate(b) == block
|
||||||
|
b.write_block(block, durability='hard')
|
||||||
|
# vote
|
||||||
|
vote = b.vote(block.id, b.get_last_voted_block().id, True)
|
||||||
|
b.write_vote(vote)
|
||||||
|
|
||||||
|
# TRANSFER
|
||||||
|
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([b.me], 100)],
|
||||||
|
asset=tx_create.asset)
|
||||||
|
tx_transfer_signed = tx_transfer.sign([user_sk])
|
||||||
|
|
||||||
|
assert tx_transfer_signed.validate(b)
|
||||||
|
assert len(tx_transfer_signed.conditions) == 1
|
||||||
|
assert tx_transfer_signed.conditions[0].amount == 100
|
||||||
|
assert len(tx_transfer_signed.fulfillments) == 2
|
||||||
|
|
||||||
|
|
||||||
|
# TRANSFER divisible asset
|
||||||
|
# Multiple inputs
|
||||||
|
# Multiple owners_before per input
|
||||||
|
# Single output
|
||||||
|
# Single owners_after
|
||||||
|
@pytest.mark.usefixtures('inputs')
|
||||||
|
def test_multiple_in_multiple_own_single_out_single_own_transfer(b, user_vk,
|
||||||
|
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_vk, b.me], 50),
|
||||||
|
([user_vk, b.me], 50)],
|
||||||
|
asset=asset)
|
||||||
|
tx_create_signed = tx_create.sign([b.me_private])
|
||||||
|
# create block
|
||||||
|
block = b.create_block([tx_create_signed])
|
||||||
|
assert block.validate(b) == block
|
||||||
|
b.write_block(block, durability='hard')
|
||||||
|
# vote
|
||||||
|
vote = b.vote(block.id, b.get_last_voted_block().id, True)
|
||||||
|
b.write_vote(vote)
|
||||||
|
|
||||||
|
# TRANSFER
|
||||||
|
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([b.me], 100)],
|
||||||
|
asset=tx_create.asset)
|
||||||
|
tx_transfer_signed = tx_transfer.sign([b.me_private, user_sk])
|
||||||
|
|
||||||
|
assert tx_transfer_signed.validate(b)
|
||||||
|
assert len(tx_transfer_signed.conditions) == 1
|
||||||
|
assert tx_transfer_signed.conditions[0].amount == 100
|
||||||
|
assert len(tx_transfer_signed.fulfillments) == 2
|
||||||
|
|
||||||
|
ffill_fid0 = tx_transfer_signed.fulfillments[0].fulfillment.to_dict()
|
||||||
|
ffill_fid1 = tx_transfer_signed.fulfillments[1].fulfillment.to_dict()
|
||||||
|
assert 'subfulfillments' in ffill_fid0
|
||||||
|
assert 'subfulfillments' in ffill_fid1
|
||||||
|
assert len(ffill_fid0['subfulfillments']) == 2
|
||||||
|
assert len(ffill_fid1['subfulfillments']) == 2
|
||||||
|
|
||||||
|
|
||||||
|
# TRANSFER divisible asset
|
||||||
|
# Multiple inputs
|
||||||
|
# Mix: one input with a single owners_before, one input with multiple
|
||||||
|
# owners_before
|
||||||
|
# Single output
|
||||||
|
# Single owners_after
|
||||||
|
@pytest.mark.usefixtures('inputs')
|
||||||
|
def test_muiltiple_in_mix_own_multiple_out_single_own_transfer(b, user_vk,
|
||||||
|
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_vk], 50),
|
||||||
|
([user_vk, b.me], 50)],
|
||||||
|
asset=asset)
|
||||||
|
tx_create_signed = tx_create.sign([b.me_private])
|
||||||
|
# create block
|
||||||
|
block = b.create_block([tx_create_signed])
|
||||||
|
assert block.validate(b) == block
|
||||||
|
b.write_block(block, durability='hard')
|
||||||
|
# vote
|
||||||
|
vote = b.vote(block.id, b.get_last_voted_block().id, True)
|
||||||
|
b.write_vote(vote)
|
||||||
|
|
||||||
|
# TRANSFER
|
||||||
|
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([b.me], 100)],
|
||||||
|
asset=tx_create.asset)
|
||||||
|
tx_transfer_signed = tx_transfer.sign([b.me_private, user_sk])
|
||||||
|
|
||||||
|
assert tx_transfer_signed.validate(b) == tx_transfer_signed
|
||||||
|
assert len(tx_transfer_signed.conditions) == 1
|
||||||
|
assert tx_transfer_signed.conditions[0].amount == 100
|
||||||
|
assert len(tx_transfer_signed.fulfillments) == 2
|
||||||
|
|
||||||
|
ffill_fid0 = tx_transfer_signed.fulfillments[0].fulfillment.to_dict()
|
||||||
|
ffill_fid1 = tx_transfer_signed.fulfillments[1].fulfillment.to_dict()
|
||||||
|
assert 'subfulfillments' not in ffill_fid0
|
||||||
|
assert 'subfulfillments' in ffill_fid1
|
||||||
|
assert len(ffill_fid1['subfulfillments']) == 2
|
||||||
|
|
||||||
|
|
||||||
|
# TRANSFER divisible asset
|
||||||
|
# Multiple inputs
|
||||||
|
# Mix: one input with a single owners_before, one input with multiple
|
||||||
|
# owners_before
|
||||||
|
# Multiple outputs
|
||||||
|
# Mix: one output with a single owners_after, one output with multiple
|
||||||
|
# owners_after
|
||||||
|
@pytest.mark.usefixtures('inputs')
|
||||||
|
def test_muiltiple_in_mix_own_multiple_out_mix_own_transfer(b, user_vk,
|
||||||
|
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_vk], 50),
|
||||||
|
([user_vk, b.me], 50)],
|
||||||
|
asset=asset)
|
||||||
|
tx_create_signed = tx_create.sign([b.me_private])
|
||||||
|
# create block
|
||||||
|
block = b.create_block([tx_create_signed])
|
||||||
|
assert block.validate(b) == block
|
||||||
|
b.write_block(block, durability='hard')
|
||||||
|
# vote
|
||||||
|
vote = b.vote(block.id, b.get_last_voted_block().id, True)
|
||||||
|
b.write_vote(vote)
|
||||||
|
|
||||||
|
# TRANSFER
|
||||||
|
tx_transfer = Transaction.transfer(tx_create.to_inputs(),
|
||||||
|
[([b.me], 50), ([b.me, user_vk], 50)],
|
||||||
|
asset=tx_create.asset)
|
||||||
|
tx_transfer_signed = tx_transfer.sign([b.me_private, user_sk])
|
||||||
|
|
||||||
|
assert tx_transfer_signed.validate(b) == tx_transfer_signed
|
||||||
|
assert len(tx_transfer_signed.conditions) == 2
|
||||||
|
assert tx_transfer_signed.conditions[0].amount == 50
|
||||||
|
assert tx_transfer_signed.conditions[1].amount == 50
|
||||||
|
assert len(tx_transfer_signed.fulfillments) == 2
|
||||||
|
|
||||||
|
cond_cid0 = tx_transfer_signed.conditions[0].to_dict()
|
||||||
|
cond_cid1 = tx_transfer_signed.conditions[1].to_dict()
|
||||||
|
assert 'subfulfillments' not in cond_cid0['condition']['details']
|
||||||
|
assert 'subfulfillments' in cond_cid1['condition']['details']
|
||||||
|
assert len(cond_cid1['condition']['details']['subfulfillments']) == 2
|
||||||
|
|
||||||
|
ffill_fid0 = tx_transfer_signed.fulfillments[0].fulfillment.to_dict()
|
||||||
|
ffill_fid1 = tx_transfer_signed.fulfillments[1].fulfillment.to_dict()
|
||||||
|
assert 'subfulfillments' not in ffill_fid0
|
||||||
|
assert 'subfulfillments' in ffill_fid1
|
||||||
|
assert len(ffill_fid1['subfulfillments']) == 2
|
||||||
|
|
||||||
|
|
||||||
|
# TRANSFER divisible asset
|
||||||
|
# Multiple inputs from different transactions
|
||||||
|
# Single owners_before
|
||||||
|
# Single output
|
||||||
|
# Single owners_after
|
||||||
|
@pytest.mark.usefixtures('inputs')
|
||||||
|
def test_multiple_in_different_transactions(b, user_vk, 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_vk`
|
||||||
|
asset = Asset(divisible=True)
|
||||||
|
tx_create = Transaction.create([b.me],
|
||||||
|
[([user_vk], 50),
|
||||||
|
([b.me], 50)],
|
||||||
|
asset=asset)
|
||||||
|
tx_create_signed = tx_create.sign([b.me_private])
|
||||||
|
# create block
|
||||||
|
block = b.create_block([tx_create_signed])
|
||||||
|
assert block.validate(b) == block
|
||||||
|
b.write_block(block, durability='hard')
|
||||||
|
# vote
|
||||||
|
vote = b.vote(block.id, b.get_last_voted_block().id, True)
|
||||||
|
b.write_vote(vote)
|
||||||
|
|
||||||
|
# TRANSFER divisible asset
|
||||||
|
# `b` transfers its 50 shares to `user_vk`
|
||||||
|
# after this transaction `user_vk` will have a total of 100 shares
|
||||||
|
# split across two different transactions
|
||||||
|
tx_transfer1 = Transaction.transfer(tx_create.to_inputs([1]),
|
||||||
|
[([user_vk], 50)],
|
||||||
|
asset=tx_create.asset)
|
||||||
|
tx_transfer1_signed = tx_transfer1.sign([b.me_private])
|
||||||
|
# create block
|
||||||
|
block = b.create_block([tx_transfer1_signed])
|
||||||
|
assert block.validate(b) == block
|
||||||
|
b.write_block(block, durability='hard')
|
||||||
|
# vote
|
||||||
|
vote = b.vote(block.id, b.get_last_voted_block().id, True)
|
||||||
|
b.write_vote(vote)
|
||||||
|
|
||||||
|
# TRANSFER
|
||||||
|
# `user_vk` combines two different transaction with 50 shares each and
|
||||||
|
# transfers a total of 100 shares back to `b`
|
||||||
|
tx_transfer2 = Transaction.transfer(tx_create.to_inputs([0]) +
|
||||||
|
tx_transfer1.to_inputs([0]),
|
||||||
|
[([b.me], 100)],
|
||||||
|
asset=tx_create.asset)
|
||||||
|
tx_transfer2_signed = tx_transfer2.sign([user_sk])
|
||||||
|
|
||||||
|
assert tx_transfer2_signed.validate(b) == tx_transfer2_signed
|
||||||
|
assert len(tx_transfer2_signed.conditions) == 1
|
||||||
|
assert tx_transfer2_signed.conditions[0].amount == 100
|
||||||
|
assert len(tx_transfer2_signed.fulfillments) == 2
|
||||||
|
|
||||||
|
fid0_input = tx_transfer2_signed.fulfillments[0].to_dict()['input']['txid']
|
||||||
|
fid1_input = tx_transfer2_signed.fulfillments[1].to_dict()['input']['txid']
|
||||||
|
assert fid0_input == tx_create.id
|
||||||
|
assert fid1_input == tx_transfer1.id
|
||||||
|
|
||||||
|
|
||||||
|
# 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.usefixtures('inputs')
|
||||||
|
def test_amount_error_transfer(b, user_vk, 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_vk], 100)], asset=asset)
|
||||||
|
tx_create_signed = tx_create.sign([b.me_private])
|
||||||
|
# create block
|
||||||
|
block = b.create_block([tx_create_signed])
|
||||||
|
assert block.validate(b) == block
|
||||||
|
b.write_block(block, durability='hard')
|
||||||
|
# vote
|
||||||
|
vote = b.vote(block.id, b.get_last_voted_block().id, True)
|
||||||
|
b.write_vote(vote)
|
||||||
|
|
||||||
|
# TRANSFER
|
||||||
|
# output amount less than input amount
|
||||||
|
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([b.me], 50)],
|
||||||
|
asset=tx_create.asset)
|
||||||
|
tx_transfer_signed = tx_transfer.sign([user_sk])
|
||||||
|
with pytest.raises(AmountError):
|
||||||
|
tx_transfer_signed.validate(b)
|
||||||
|
|
||||||
|
# TRANSFER
|
||||||
|
# output amount greater than input amount
|
||||||
|
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([b.me], 101)],
|
||||||
|
asset=tx_create.asset)
|
||||||
|
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.usefixtures('inputs')
|
||||||
|
def test_threshold_same_public_key(b, user_vk, user_sk):
|
||||||
|
# If we try to fulfill a threshold condition where each subcondition has
|
||||||
|
# the same key get_subcondition_from_vk will always return the first
|
||||||
|
# subcondition. This means that only the 1st subfulfillment will be
|
||||||
|
# generated
|
||||||
|
# Creating threshold conditions with the same key does not make sense but
|
||||||
|
# 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_vk, user_vk], 100)],
|
||||||
|
asset=asset)
|
||||||
|
tx_create_signed = tx_create.sign([b.me_private])
|
||||||
|
# create block
|
||||||
|
block = b.create_block([tx_create_signed])
|
||||||
|
assert block.validate(b) == block
|
||||||
|
b.write_block(block, durability='hard')
|
||||||
|
# vote
|
||||||
|
vote = b.vote(block.id, b.get_last_voted_block().id, True)
|
||||||
|
b.write_vote(vote)
|
||||||
|
|
||||||
|
# TRANSFER
|
||||||
|
tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([b.me], 100)],
|
||||||
|
asset=tx_create.asset)
|
||||||
|
tx_transfer_signed = tx_transfer.sign([user_sk, user_sk])
|
||||||
|
|
||||||
|
assert tx_transfer_signed.validate(b) == tx_transfer_signed
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures('inputs')
|
||||||
|
def test_sum_amount(b, user_vk, 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_vk], 1),
|
||||||
|
([user_vk], 1),
|
||||||
|
([user_vk], 1)],
|
||||||
|
asset=asset)
|
||||||
|
tx_create_signed = tx_create.sign([b.me_private])
|
||||||
|
# create block
|
||||||
|
block = b.create_block([tx_create_signed])
|
||||||
|
assert block.validate(b) == block
|
||||||
|
b.write_block(block, durability='hard')
|
||||||
|
# vote
|
||||||
|
vote = b.vote(block.id, b.get_last_voted_block().id, True)
|
||||||
|
b.write_vote(vote)
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
tx_transfer_signed = tx_transfer.sign([user_sk])
|
||||||
|
|
||||||
|
assert tx_transfer_signed.validate(b) == tx_transfer_signed
|
||||||
|
assert len(tx_transfer_signed.conditions) == 1
|
||||||
|
assert tx_transfer_signed.conditions[0].amount == 3
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures('inputs')
|
||||||
|
def test_divide(b, user_vk, 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_vk], 3)],
|
||||||
|
asset=asset)
|
||||||
|
tx_create_signed = tx_create.sign([b.me_private])
|
||||||
|
# create block
|
||||||
|
block = b.create_block([tx_create_signed])
|
||||||
|
assert block.validate(b) == block
|
||||||
|
b.write_block(block, durability='hard')
|
||||||
|
# vote
|
||||||
|
vote = b.vote(block.id, b.get_last_voted_block().id, True)
|
||||||
|
b.write_vote(vote)
|
||||||
|
|
||||||
|
# create a transfer transaction with 3 outputs and check if the amount
|
||||||
|
# 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)
|
||||||
|
tx_transfer_signed = tx_transfer.sign([user_sk])
|
||||||
|
|
||||||
|
assert tx_transfer_signed.validate(b) == tx_transfer_signed
|
||||||
|
assert len(tx_transfer_signed.conditions) == 3
|
||||||
|
for condition in tx_transfer_signed.conditions:
|
||||||
|
assert condition.amount == 1
|
||||||
|
|
||||||
|
|
||||||
|
# Check that negative inputs are caught when creating a TRANSFER transaction
|
||||||
|
@pytest.mark.usefixtures('inputs')
|
||||||
|
def test_non_positive_amounts_on_transfer(b, user_vk):
|
||||||
|
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_vk], 3)],
|
||||||
|
asset=asset)
|
||||||
|
tx_create_signed = tx_create.sign([b.me_private])
|
||||||
|
# create block
|
||||||
|
block = b.create_block([tx_create_signed])
|
||||||
|
assert block.validate(b) == block
|
||||||
|
b.write_block(block, durability='hard')
|
||||||
|
# vote
|
||||||
|
vote = b.vote(block.id, b.get_last_voted_block().id, True)
|
||||||
|
b.write_vote(vote)
|
||||||
|
|
||||||
|
with pytest.raises(AmountError):
|
||||||
|
Transaction.transfer(tx_create.to_inputs(),
|
||||||
|
[([b.me], 4), ([b.me], -1)],
|
||||||
|
asset=tx_create.asset)
|
||||||
|
|
||||||
|
|
||||||
|
# Check that negative inputs are caught when validating a TRANSFER transaction
|
||||||
|
@pytest.mark.usefixtures('inputs')
|
||||||
|
def test_non_positive_amounts_on_transfer_validate(b, user_vk, 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_vk], 3)],
|
||||||
|
asset=asset)
|
||||||
|
tx_create_signed = tx_create.sign([b.me_private])
|
||||||
|
# create block
|
||||||
|
block = b.create_block([tx_create_signed])
|
||||||
|
assert block.validate(b) == block
|
||||||
|
b.write_block(block, durability='hard')
|
||||||
|
# vote
|
||||||
|
vote = b.vote(block.id, b.get_last_voted_block().id, True)
|
||||||
|
b.write_vote(vote)
|
||||||
|
|
||||||
|
# create a transfer transaction with 3 outputs and check if the amount
|
||||||
|
# of each output is 1
|
||||||
|
tx_transfer = Transaction.transfer(tx_create.to_inputs(),
|
||||||
|
[([b.me], 4), ([b.me], 1)],
|
||||||
|
asset=tx_create.asset)
|
||||||
|
tx_transfer.conditions[1].amount = -1
|
||||||
|
tx_transfer_signed = tx_transfer.sign([user_sk])
|
||||||
|
|
||||||
|
with pytest.raises(AmountError):
|
||||||
|
tx_transfer_signed.validate(b)
|
||||||
|
|
||||||
|
|
||||||
|
# Check that negative inputs are caught when creating a CREATE transaction
|
||||||
|
@pytest.mark.usefixtures('inputs')
|
||||||
|
def test_non_positive_amounts_on_create(b, user_vk):
|
||||||
|
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_vk], -3)],
|
||||||
|
asset=asset)
|
||||||
|
|
||||||
|
|
||||||
|
# Check that negative inputs are caught when validating a CREATE transaction
|
||||||
|
@pytest.mark.usefixtures('inputs')
|
||||||
|
def test_non_positive_amounts_on_create_validate(b, user_vk):
|
||||||
|
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_vk], 3)],
|
||||||
|
asset=asset)
|
||||||
|
tx_create.conditions[0].amount = -3
|
||||||
|
with patch.object(Asset, 'validate_asset', return_value=None):
|
||||||
|
tx_create_signed = tx_create.sign([b.me_private])
|
||||||
|
|
||||||
|
with pytest.raises(AmountError):
|
||||||
|
tx_create_signed.validate(b)
|
@ -22,6 +22,7 @@ def test_asset_creation_with_data(data):
|
|||||||
def test_asset_invalid_asset_initialization():
|
def test_asset_invalid_asset_initialization():
|
||||||
from bigchaindb.common.transaction import Asset
|
from bigchaindb.common.transaction import Asset
|
||||||
|
|
||||||
|
# check types
|
||||||
with raises(TypeError):
|
with raises(TypeError):
|
||||||
Asset(data='some wrong type')
|
Asset(data='some wrong type')
|
||||||
with raises(TypeError):
|
with raises(TypeError):
|
||||||
@ -31,6 +32,12 @@ def test_asset_invalid_asset_initialization():
|
|||||||
with raises(TypeError):
|
with raises(TypeError):
|
||||||
Asset(updatable=1)
|
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):
|
def test_invalid_asset_comparison(data, data_id):
|
||||||
from bigchaindb.common.transaction import Asset
|
from bigchaindb.common.transaction import Asset
|
||||||
@ -69,12 +76,17 @@ def test_asset_deserialization(data, data_id):
|
|||||||
|
|
||||||
def test_validate_asset():
|
def test_validate_asset():
|
||||||
from bigchaindb.common.transaction import 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):
|
with raises(TypeError):
|
||||||
Asset(divisible=1)
|
asset.validate_asset(amount='a')
|
||||||
with raises(TypeError):
|
|
||||||
Asset(refillable=1)
|
|
||||||
with raises(TypeError):
|
|
||||||
Asset(updatable=1)
|
|
||||||
with raises(TypeError):
|
|
||||||
Asset(data='we need more lemon pledge')
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from pytest import raises, mark
|
from pytest import raises
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
|
||||||
def test_fulfillment_serialization(ffill_uri, user_pub):
|
def test_fulfillment_serialization(ffill_uri, user_pub):
|
||||||
@ -166,29 +167,7 @@ def test_generate_conditions_split_half_recursive(user_pub, user2_pub,
|
|||||||
expected_threshold.add_subfulfillment(expected_simple3)
|
expected_threshold.add_subfulfillment(expected_simple3)
|
||||||
expected.add_subfulfillment(expected_threshold)
|
expected.add_subfulfillment(expected_threshold)
|
||||||
|
|
||||||
cond = Condition.generate([user_pub, [user2_pub, expected_simple3]])
|
cond = Condition.generate([user_pub, [user2_pub, expected_simple3]], 1)
|
||||||
assert cond.fulfillment.to_dict() == expected.to_dict()
|
|
||||||
|
|
||||||
|
|
||||||
def test_generate_conditions_split_half_recursive_custom_threshold(user_pub,
|
|
||||||
user2_pub,
|
|
||||||
user3_pub):
|
|
||||||
from bigchaindb.common.transaction import Condition
|
|
||||||
from cryptoconditions import Ed25519Fulfillment, ThresholdSha256Fulfillment
|
|
||||||
|
|
||||||
expected_simple1 = Ed25519Fulfillment(public_key=user_pub)
|
|
||||||
expected_simple2 = Ed25519Fulfillment(public_key=user2_pub)
|
|
||||||
expected_simple3 = Ed25519Fulfillment(public_key=user3_pub)
|
|
||||||
|
|
||||||
expected = ThresholdSha256Fulfillment(threshold=1)
|
|
||||||
expected.add_subfulfillment(expected_simple1)
|
|
||||||
expected_threshold = ThresholdSha256Fulfillment(threshold=1)
|
|
||||||
expected_threshold.add_subfulfillment(expected_simple2)
|
|
||||||
expected_threshold.add_subfulfillment(expected_simple3)
|
|
||||||
expected.add_subfulfillment(expected_threshold)
|
|
||||||
|
|
||||||
cond = Condition.generate(([user_pub, ([user2_pub, expected_simple3], 1)],
|
|
||||||
1))
|
|
||||||
assert cond.fulfillment.to_dict() == expected.to_dict()
|
assert cond.fulfillment.to_dict() == expected.to_dict()
|
||||||
|
|
||||||
|
|
||||||
@ -208,7 +187,7 @@ def test_generate_conditions_split_half_single_owner(user_pub, user2_pub,
|
|||||||
expected.add_subfulfillment(expected_threshold)
|
expected.add_subfulfillment(expected_threshold)
|
||||||
expected.add_subfulfillment(expected_simple1)
|
expected.add_subfulfillment(expected_simple1)
|
||||||
|
|
||||||
cond = Condition.generate([[expected_simple2, user3_pub], user_pub])
|
cond = Condition.generate([[expected_simple2, user3_pub], user_pub], 1)
|
||||||
assert cond.fulfillment.to_dict() == expected.to_dict()
|
assert cond.fulfillment.to_dict() == expected.to_dict()
|
||||||
|
|
||||||
|
|
||||||
@ -225,7 +204,7 @@ def test_generate_conditions_flat_ownage(user_pub, user2_pub, user3_pub):
|
|||||||
expected.add_subfulfillment(expected_simple2)
|
expected.add_subfulfillment(expected_simple2)
|
||||||
expected.add_subfulfillment(expected_simple3)
|
expected.add_subfulfillment(expected_simple3)
|
||||||
|
|
||||||
cond = Condition.generate([user_pub, user2_pub, expected_simple3])
|
cond = Condition.generate([user_pub, user2_pub, expected_simple3], 1)
|
||||||
assert cond.fulfillment.to_dict() == expected.to_dict()
|
assert cond.fulfillment.to_dict() == expected.to_dict()
|
||||||
|
|
||||||
|
|
||||||
@ -234,7 +213,7 @@ def test_generate_conditions_single_owner(user_pub):
|
|||||||
from cryptoconditions import Ed25519Fulfillment
|
from cryptoconditions import Ed25519Fulfillment
|
||||||
|
|
||||||
expected = Ed25519Fulfillment(public_key=user_pub)
|
expected = Ed25519Fulfillment(public_key=user_pub)
|
||||||
cond = Condition.generate([user_pub])
|
cond = Condition.generate([user_pub], 1)
|
||||||
|
|
||||||
assert cond.fulfillment.to_dict() == expected.to_dict()
|
assert cond.fulfillment.to_dict() == expected.to_dict()
|
||||||
|
|
||||||
@ -244,48 +223,23 @@ def test_generate_conditions_single_owner_with_condition(user_pub):
|
|||||||
from cryptoconditions import Ed25519Fulfillment
|
from cryptoconditions import Ed25519Fulfillment
|
||||||
|
|
||||||
expected = Ed25519Fulfillment(public_key=user_pub)
|
expected = Ed25519Fulfillment(public_key=user_pub)
|
||||||
cond = Condition.generate([expected])
|
cond = Condition.generate([expected], 1)
|
||||||
|
|
||||||
assert cond.fulfillment.to_dict() == expected.to_dict()
|
assert cond.fulfillment.to_dict() == expected.to_dict()
|
||||||
|
|
||||||
|
|
||||||
# TODO FOR CC: see skip reason
|
|
||||||
@mark.skip(reason='threshold(hashlock).to_dict() exposes secret')
|
|
||||||
def test_generate_threshold_condition_with_hashlock(user_pub, user2_pub,
|
|
||||||
user3_pub):
|
|
||||||
from bigchaindb.common.transaction import Condition
|
|
||||||
from cryptoconditions import (PreimageSha256Fulfillment,
|
|
||||||
Ed25519Fulfillment,
|
|
||||||
ThresholdSha256Fulfillment)
|
|
||||||
|
|
||||||
secret = b'much secret, wow'
|
|
||||||
hashlock = PreimageSha256Fulfillment(preimage=secret)
|
|
||||||
|
|
||||||
expected_simple1 = Ed25519Fulfillment(public_key=user_pub)
|
|
||||||
expected_simple3 = Ed25519Fulfillment(public_key=user3_pub)
|
|
||||||
|
|
||||||
expected = ThresholdSha256Fulfillment(threshold=2)
|
|
||||||
expected_sub = ThresholdSha256Fulfillment(threshold=2)
|
|
||||||
expected_sub.add_subfulfillment(expected_simple1)
|
|
||||||
expected_sub.add_subfulfillment(hashlock)
|
|
||||||
expected.add_subfulfillment(expected_simple3)
|
|
||||||
|
|
||||||
cond = Condition.generate([[user_pub, hashlock], expected_simple3])
|
|
||||||
assert cond.fulfillment.to_dict() == expected.to_dict()
|
|
||||||
|
|
||||||
|
|
||||||
def test_generate_conditions_invalid_parameters(user_pub, user2_pub,
|
def test_generate_conditions_invalid_parameters(user_pub, user2_pub,
|
||||||
user3_pub):
|
user3_pub):
|
||||||
from bigchaindb.common.transaction import Condition
|
from bigchaindb.common.transaction import Condition
|
||||||
|
|
||||||
with raises(ValueError):
|
with raises(ValueError):
|
||||||
Condition.generate([])
|
Condition.generate([], 1)
|
||||||
with raises(TypeError):
|
with raises(TypeError):
|
||||||
Condition.generate('not a list')
|
Condition.generate('not a list', 1)
|
||||||
with raises(ValueError):
|
with raises(ValueError):
|
||||||
Condition.generate([[user_pub, [user2_pub, [user3_pub]]]])
|
Condition.generate([[user_pub, [user2_pub, [user3_pub]]]], 1)
|
||||||
with raises(ValueError):
|
with raises(ValueError):
|
||||||
Condition.generate([[user_pub]])
|
Condition.generate([[user_pub]], 1)
|
||||||
|
|
||||||
|
|
||||||
def test_invalid_transaction_initialization():
|
def test_invalid_transaction_initialization():
|
||||||
@ -321,7 +275,8 @@ def test_invalid_transaction_initialization():
|
|||||||
def test_create_default_asset_on_tx_initialization():
|
def test_create_default_asset_on_tx_initialization():
|
||||||
from bigchaindb.common.transaction import Transaction, Asset
|
from bigchaindb.common.transaction import Transaction, Asset
|
||||||
|
|
||||||
tx = Transaction(Transaction.CREATE, None)
|
with patch.object(Asset, 'validate_asset', return_value=None):
|
||||||
|
tx = Transaction(Transaction.CREATE, None)
|
||||||
expected = Asset()
|
expected = Asset()
|
||||||
asset = tx.asset
|
asset = tx.asset
|
||||||
|
|
||||||
@ -510,10 +465,72 @@ def test_cast_transaction_link_to_boolean():
|
|||||||
assert bool(TransactionLink(False, False)) is True
|
assert bool(TransactionLink(False, False)) is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_asset_link_serialization():
|
||||||
|
from bigchaindb.common.transaction import AssetLink
|
||||||
|
|
||||||
|
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_fulfillment_to_tx(user_ffill):
|
def test_add_fulfillment_to_tx(user_ffill):
|
||||||
from bigchaindb.common.transaction import Transaction, Asset
|
from bigchaindb.common.transaction import Transaction, Asset
|
||||||
|
|
||||||
tx = Transaction(Transaction.CREATE, Asset(), [], [])
|
with patch.object(Asset, 'validate_asset', return_value=None):
|
||||||
|
tx = Transaction(Transaction.CREATE, Asset(), [], [])
|
||||||
tx.add_fulfillment(user_ffill)
|
tx.add_fulfillment(user_ffill)
|
||||||
|
|
||||||
assert len(tx.fulfillments) == 1
|
assert len(tx.fulfillments) == 1
|
||||||
@ -522,7 +539,8 @@ def test_add_fulfillment_to_tx(user_ffill):
|
|||||||
def test_add_fulfillment_to_tx_with_invalid_parameters():
|
def test_add_fulfillment_to_tx_with_invalid_parameters():
|
||||||
from bigchaindb.common.transaction import Transaction, Asset
|
from bigchaindb.common.transaction import Transaction, Asset
|
||||||
|
|
||||||
tx = Transaction(Transaction.CREATE, Asset())
|
with patch.object(Asset, 'validate_asset', return_value=None):
|
||||||
|
tx = Transaction(Transaction.CREATE, Asset())
|
||||||
with raises(TypeError):
|
with raises(TypeError):
|
||||||
tx.add_fulfillment('somewronginput')
|
tx.add_fulfillment('somewronginput')
|
||||||
|
|
||||||
@ -530,7 +548,8 @@ def test_add_fulfillment_to_tx_with_invalid_parameters():
|
|||||||
def test_add_condition_to_tx(user_cond):
|
def test_add_condition_to_tx(user_cond):
|
||||||
from bigchaindb.common.transaction import Transaction, Asset
|
from bigchaindb.common.transaction import Transaction, Asset
|
||||||
|
|
||||||
tx = Transaction(Transaction.CREATE, Asset())
|
with patch.object(Asset, 'validate_asset', return_value=None):
|
||||||
|
tx = Transaction(Transaction.CREATE, Asset())
|
||||||
tx.add_condition(user_cond)
|
tx.add_condition(user_cond)
|
||||||
|
|
||||||
assert len(tx.conditions) == 1
|
assert len(tx.conditions) == 1
|
||||||
@ -539,7 +558,8 @@ def test_add_condition_to_tx(user_cond):
|
|||||||
def test_add_condition_to_tx_with_invalid_parameters():
|
def test_add_condition_to_tx_with_invalid_parameters():
|
||||||
from bigchaindb.common.transaction import Transaction, Asset
|
from bigchaindb.common.transaction import Transaction, Asset
|
||||||
|
|
||||||
tx = Transaction(Transaction.CREATE, Asset(), [], [])
|
with patch.object(Asset, 'validate_asset', return_value=None):
|
||||||
|
tx = Transaction(Transaction.CREATE, Asset(), [], [])
|
||||||
with raises(TypeError):
|
with raises(TypeError):
|
||||||
tx.add_condition('somewronginput')
|
tx.add_condition('somewronginput')
|
||||||
|
|
||||||
@ -614,16 +634,14 @@ def test_validate_multiple_fulfillments(user_ffill, user_cond, user_priv):
|
|||||||
from bigchaindb.common.crypto import PrivateKey
|
from bigchaindb.common.crypto import PrivateKey
|
||||||
from bigchaindb.common.transaction import Transaction, Asset
|
from bigchaindb.common.transaction import Transaction, Asset
|
||||||
|
|
||||||
tx = Transaction(Transaction.CREATE, Asset(),
|
tx = Transaction(Transaction.CREATE, Asset(divisible=True),
|
||||||
[user_ffill, deepcopy(user_ffill)],
|
[user_ffill, deepcopy(user_ffill)],
|
||||||
[user_ffill, deepcopy(user_cond)])
|
[user_cond, deepcopy(user_cond)])
|
||||||
|
|
||||||
expected_first = deepcopy(tx)
|
expected_first = deepcopy(tx)
|
||||||
expected_second = deepcopy(tx)
|
expected_second = deepcopy(tx)
|
||||||
expected_first.fulfillments = [expected_first.fulfillments[0]]
|
expected_first.fulfillments = [expected_first.fulfillments[0]]
|
||||||
expected_first.conditions = [expected_first.conditions[0]]
|
|
||||||
expected_second.fulfillments = [expected_second.fulfillments[1]]
|
expected_second.fulfillments = [expected_second.fulfillments[1]]
|
||||||
expected_second.conditions = [expected_second.conditions[1]]
|
|
||||||
|
|
||||||
expected_first_bytes = str(expected_first).encode()
|
expected_first_bytes = str(expected_first).encode()
|
||||||
expected_first.fulfillments[0].fulfillment.sign(expected_first_bytes,
|
expected_first.fulfillments[0].fulfillment.sign(expected_first_bytes,
|
||||||
@ -674,7 +692,7 @@ def test_multiple_fulfillment_validation_of_transfer_tx(user_ffill, user_cond,
|
|||||||
Fulfillment, Condition, Asset)
|
Fulfillment, Condition, Asset)
|
||||||
from cryptoconditions import Ed25519Fulfillment
|
from cryptoconditions import Ed25519Fulfillment
|
||||||
|
|
||||||
tx = Transaction(Transaction.CREATE, Asset(),
|
tx = Transaction(Transaction.CREATE, Asset(divisible=True),
|
||||||
[user_ffill, deepcopy(user_ffill)],
|
[user_ffill, deepcopy(user_ffill)],
|
||||||
[user_cond, deepcopy(user_cond)])
|
[user_cond, deepcopy(user_cond)])
|
||||||
tx.sign([user_priv])
|
tx.sign([user_priv])
|
||||||
@ -715,10 +733,6 @@ def test_validate_fulfillments_of_transfer_tx_with_invalid_params(transfer_tx,
|
|||||||
with raises(TypeError):
|
with raises(TypeError):
|
||||||
transfer_tx.operation = "Operation that doesn't exist"
|
transfer_tx.operation = "Operation that doesn't exist"
|
||||||
transfer_tx.fulfillments_valid([utx.conditions[0]])
|
transfer_tx.fulfillments_valid([utx.conditions[0]])
|
||||||
with raises(ValueError):
|
|
||||||
tx = utx.sign([user_priv])
|
|
||||||
tx.conditions = []
|
|
||||||
tx.fulfillments_valid()
|
|
||||||
|
|
||||||
|
|
||||||
def test_create_create_transaction_single_io(user_cond, user_pub, data,
|
def test_create_create_transaction_single_io(user_cond, user_pub, data,
|
||||||
@ -754,7 +768,8 @@ def test_create_create_transaction_single_io(user_cond, user_pub, data,
|
|||||||
}
|
}
|
||||||
|
|
||||||
asset = Asset(data, data_id)
|
asset = Asset(data, data_id)
|
||||||
tx = Transaction.create([user_pub], [user_pub], data, asset).to_dict()
|
tx = Transaction.create([user_pub], [([user_pub], 1)],
|
||||||
|
data, asset).to_dict()
|
||||||
tx.pop('id')
|
tx.pop('id')
|
||||||
tx['transaction']['metadata'].pop('id')
|
tx['transaction']['metadata'].pop('id')
|
||||||
tx['transaction'].pop('timestamp')
|
tx['transaction'].pop('timestamp')
|
||||||
@ -766,17 +781,20 @@ def test_create_create_transaction_single_io(user_cond, user_pub, data,
|
|||||||
def test_validate_single_io_create_transaction(user_pub, user_priv, data):
|
def test_validate_single_io_create_transaction(user_pub, user_priv, data):
|
||||||
from bigchaindb.common.transaction import Transaction, Asset
|
from bigchaindb.common.transaction import Transaction, Asset
|
||||||
|
|
||||||
tx = Transaction.create([user_pub], [user_pub], data, Asset())
|
tx = Transaction.create([user_pub], [([user_pub], 1)], data, Asset())
|
||||||
tx = tx.sign([user_priv])
|
tx = tx.sign([user_priv])
|
||||||
assert tx.fulfillments_valid() is True
|
assert tx.fulfillments_valid() is True
|
||||||
|
|
||||||
|
|
||||||
@mark.skip(reason='Multiple inputs and outputs in CREATE not supported')
|
|
||||||
# TODO: Add digital assets
|
|
||||||
def test_create_create_transaction_multiple_io(user_cond, user2_cond, user_pub,
|
def test_create_create_transaction_multiple_io(user_cond, user2_cond, user_pub,
|
||||||
user2_pub):
|
user2_pub):
|
||||||
from bigchaindb.common.transaction import Transaction
|
from bigchaindb.common.transaction import Transaction, Asset, Fulfillment
|
||||||
|
|
||||||
|
# a fulfillment for a create transaction with multiple `owners_before`
|
||||||
|
# is a fulfillment for an implicit threshold condition with
|
||||||
|
# weight = len(owners_before)
|
||||||
|
ffill = Fulfillment.generate([user_pub, user2_pub]).to_dict()
|
||||||
|
ffill.update({'fid': 0})
|
||||||
expected = {
|
expected = {
|
||||||
'transaction': {
|
'transaction': {
|
||||||
'conditions': [user_cond.to_dict(0), user2_cond.to_dict(1)],
|
'conditions': [user_cond.to_dict(0), user2_cond.to_dict(1)],
|
||||||
@ -785,45 +803,32 @@ def test_create_create_transaction_multiple_io(user_cond, user2_cond, user_pub,
|
|||||||
'message': 'hello'
|
'message': 'hello'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'fulfillments': [
|
'fulfillments': [ffill],
|
||||||
{
|
|
||||||
'owners_before': [
|
|
||||||
user_pub,
|
|
||||||
],
|
|
||||||
'fid': 0,
|
|
||||||
'fulfillment': None,
|
|
||||||
'input': None
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'owners_before': [
|
|
||||||
user2_pub,
|
|
||||||
],
|
|
||||||
'fid': 1,
|
|
||||||
'fulfillment': None,
|
|
||||||
'input': None
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'operation': 'CREATE',
|
'operation': 'CREATE',
|
||||||
},
|
},
|
||||||
'version': 1
|
'version': 1
|
||||||
}
|
}
|
||||||
tx = Transaction.create([user_pub, user2_pub], [user_pub, user2_pub],
|
asset = Asset(divisible=True)
|
||||||
{'message': 'hello'}).to_dict()
|
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('id')
|
||||||
tx['transaction']['metadata'].pop('id')
|
tx['transaction']['metadata'].pop('id')
|
||||||
tx['transaction'].pop('timestamp')
|
tx['transaction'].pop('timestamp')
|
||||||
|
tx['transaction'].pop('asset')
|
||||||
|
|
||||||
assert tx == expected
|
assert tx == expected
|
||||||
|
|
||||||
|
|
||||||
@mark.skip(reason='Multiple inputs and outputs in CREATE not supported')
|
|
||||||
# TODO: Add digital assets
|
|
||||||
def test_validate_multiple_io_create_transaction(user_pub, user_priv,
|
def test_validate_multiple_io_create_transaction(user_pub, user_priv,
|
||||||
user2_pub, user2_priv):
|
user2_pub, user2_priv):
|
||||||
from bigchaindb.common.transaction import Transaction
|
from bigchaindb.common.transaction import Transaction, Asset
|
||||||
|
|
||||||
tx = Transaction.create([user_pub, user2_pub], [user_pub, user2_pub],
|
tx = Transaction.create([user_pub, user2_pub],
|
||||||
{'message': 'hello'})
|
[([user_pub], 1), ([user2_pub], 1)],
|
||||||
|
metadata={'message': 'hello'},
|
||||||
|
asset=Asset(divisible=True))
|
||||||
tx = tx.sign([user_priv, user2_priv])
|
tx = tx.sign([user_priv, user2_priv])
|
||||||
assert tx.fulfillments_valid() is True
|
assert tx.fulfillments_valid() is True
|
||||||
|
|
||||||
@ -862,7 +867,8 @@ def test_create_create_transaction_threshold(user_pub, user2_pub, user3_pub,
|
|||||||
'version': 1
|
'version': 1
|
||||||
}
|
}
|
||||||
asset = Asset(data, data_id)
|
asset = Asset(data, data_id)
|
||||||
tx = Transaction.create([user_pub], [user_pub, user2_pub], data, asset)
|
tx = Transaction.create([user_pub], [([user_pub, user2_pub], 1)],
|
||||||
|
data, asset)
|
||||||
tx_dict = tx.to_dict()
|
tx_dict = tx.to_dict()
|
||||||
tx_dict.pop('id')
|
tx_dict.pop('id')
|
||||||
tx_dict['transaction']['metadata'].pop('id')
|
tx_dict['transaction']['metadata'].pop('id')
|
||||||
@ -876,80 +882,27 @@ def test_validate_threshold_create_transaction(user_pub, user_priv, user2_pub,
|
|||||||
data):
|
data):
|
||||||
from bigchaindb.common.transaction import Transaction, Asset
|
from bigchaindb.common.transaction import Transaction, Asset
|
||||||
|
|
||||||
tx = Transaction.create([user_pub], [user_pub, user2_pub], data, Asset())
|
tx = Transaction.create([user_pub], [([user_pub, user2_pub], 1)],
|
||||||
|
data, Asset())
|
||||||
tx = tx.sign([user_priv])
|
tx = tx.sign([user_priv])
|
||||||
assert tx.fulfillments_valid() is True
|
assert tx.fulfillments_valid() is True
|
||||||
|
|
||||||
|
|
||||||
def test_create_create_transaction_hashlock(user_pub, data, data_id):
|
def test_create_create_transaction_with_invalid_parameters(user_pub):
|
||||||
from cryptoconditions import PreimageSha256Fulfillment
|
|
||||||
from bigchaindb.common.transaction import Transaction, Condition, Asset
|
|
||||||
|
|
||||||
secret = b'much secret, wow'
|
|
||||||
hashlock = PreimageSha256Fulfillment(preimage=secret).condition_uri
|
|
||||||
cond = Condition(hashlock)
|
|
||||||
|
|
||||||
expected = {
|
|
||||||
'transaction': {
|
|
||||||
'conditions': [cond.to_dict(0)],
|
|
||||||
'metadata': {
|
|
||||||
'data': data,
|
|
||||||
},
|
|
||||||
'asset': {
|
|
||||||
'id': data_id,
|
|
||||||
'divisible': False,
|
|
||||||
'updatable': False,
|
|
||||||
'refillable': False,
|
|
||||||
'data': data,
|
|
||||||
},
|
|
||||||
'fulfillments': [
|
|
||||||
{
|
|
||||||
'owners_before': [
|
|
||||||
user_pub,
|
|
||||||
],
|
|
||||||
'fid': 0,
|
|
||||||
'fulfillment': None,
|
|
||||||
'input': None
|
|
||||||
},
|
|
||||||
],
|
|
||||||
'operation': 'CREATE',
|
|
||||||
},
|
|
||||||
'version': 1
|
|
||||||
}
|
|
||||||
|
|
||||||
asset = Asset(data, data_id)
|
|
||||||
tx = Transaction.create([user_pub], [], data, asset, secret).to_dict()
|
|
||||||
tx.pop('id')
|
|
||||||
tx['transaction']['metadata'].pop('id')
|
|
||||||
tx['transaction'].pop('timestamp')
|
|
||||||
tx['transaction']['fulfillments'][0]['fulfillment'] = None
|
|
||||||
|
|
||||||
assert tx == expected
|
|
||||||
|
|
||||||
|
|
||||||
def test_validate_hashlock_create_transaction(user_pub, user_priv, data):
|
|
||||||
from bigchaindb.common.transaction import Transaction, Asset
|
|
||||||
|
|
||||||
tx = Transaction.create([user_pub], [], data, Asset(), b'much secret, wow')
|
|
||||||
tx = tx.sign([user_priv])
|
|
||||||
assert tx.fulfillments_valid() is True
|
|
||||||
|
|
||||||
|
|
||||||
def test_create_create_transaction_with_invalid_parameters():
|
|
||||||
from bigchaindb.common.transaction import Transaction
|
from bigchaindb.common.transaction import Transaction
|
||||||
|
|
||||||
with raises(TypeError):
|
with raises(TypeError):
|
||||||
Transaction.create('not a list')
|
Transaction.create('not a list')
|
||||||
with raises(TypeError):
|
with raises(TypeError):
|
||||||
Transaction.create([], 'not a list')
|
Transaction.create([], 'not a list')
|
||||||
with raises(NotImplementedError):
|
|
||||||
Transaction.create(['a', 'b'], ['c', 'd'])
|
|
||||||
with raises(NotImplementedError):
|
|
||||||
Transaction.create(['a'], [], time_expire=123)
|
|
||||||
with raises(ValueError):
|
with raises(ValueError):
|
||||||
Transaction.create(['a'], [], secret=None)
|
Transaction.create([], [user_pub])
|
||||||
with raises(ValueError):
|
with raises(ValueError):
|
||||||
Transaction.create([], [], secret='wow, much secret')
|
Transaction.create([user_pub], [])
|
||||||
|
with raises(ValueError):
|
||||||
|
Transaction.create([user_pub], [user_pub])
|
||||||
|
with raises(ValueError):
|
||||||
|
Transaction.create([user_pub], [([user_pub],)])
|
||||||
|
|
||||||
|
|
||||||
def test_conditions_to_inputs(tx):
|
def test_conditions_to_inputs(tx):
|
||||||
@ -995,7 +948,7 @@ def test_create_transfer_transaction_single_io(tx, user_pub, user2_pub,
|
|||||||
}
|
}
|
||||||
inputs = tx.to_inputs([0])
|
inputs = tx.to_inputs([0])
|
||||||
asset = Asset(None, data_id)
|
asset = Asset(None, data_id)
|
||||||
transfer_tx = Transaction.transfer(inputs, [user2_pub], asset=asset)
|
transfer_tx = Transaction.transfer(inputs, [([user2_pub], 1)], asset=asset)
|
||||||
transfer_tx = transfer_tx.sign([user_priv])
|
transfer_tx = transfer_tx.sign([user_priv])
|
||||||
transfer_tx = transfer_tx.to_dict()
|
transfer_tx = transfer_tx.to_dict()
|
||||||
transfer_tx_body = transfer_tx['transaction']
|
transfer_tx_body = transfer_tx['transaction']
|
||||||
@ -1014,16 +967,15 @@ def test_create_transfer_transaction_single_io(tx, user_pub, user2_pub,
|
|||||||
assert transfer_tx.fulfillments_valid([tx.conditions[0]]) is True
|
assert transfer_tx.fulfillments_valid([tx.conditions[0]]) is True
|
||||||
|
|
||||||
|
|
||||||
@mark.skip(reason='FIXME: When divisible assets land')
|
|
||||||
def test_create_transfer_transaction_multiple_io(user_pub, user_priv,
|
def test_create_transfer_transaction_multiple_io(user_pub, user_priv,
|
||||||
user2_pub, user2_priv,
|
user2_pub, user2_priv,
|
||||||
user3_pub, user2_cond):
|
user3_pub, user2_cond):
|
||||||
from bigchaindb.common.transaction import Transaction
|
from bigchaindb.common.transaction import Transaction, Asset
|
||||||
|
|
||||||
tx1 = Transaction.create([user_pub], [user_pub], {'message': 'hello'})
|
asset = Asset(divisible=True)
|
||||||
tx1 = tx1.sign([user_priv])
|
tx = Transaction.create([user_pub], [([user_pub], 1), ([user2_pub], 1)],
|
||||||
tx2 = Transaction.create([user2_pub], [user2_pub], {'message': 'hello'})
|
asset=asset, metadata={'message': 'hello'})
|
||||||
tx2 = tx2.sign([user2_priv])
|
tx = tx.sign([user_priv])
|
||||||
|
|
||||||
expected = {
|
expected = {
|
||||||
'transaction': {
|
'transaction': {
|
||||||
@ -1037,7 +989,7 @@ def test_create_transfer_transaction_multiple_io(user_pub, user_priv,
|
|||||||
'fid': 0,
|
'fid': 0,
|
||||||
'fulfillment': None,
|
'fulfillment': None,
|
||||||
'input': {
|
'input': {
|
||||||
'txid': tx1.id,
|
'txid': tx.id,
|
||||||
'cid': 0
|
'cid': 0
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
@ -1047,8 +999,8 @@ def test_create_transfer_transaction_multiple_io(user_pub, user_priv,
|
|||||||
'fid': 1,
|
'fid': 1,
|
||||||
'fulfillment': None,
|
'fulfillment': None,
|
||||||
'input': {
|
'input': {
|
||||||
'txid': tx2.id,
|
'txid': tx.id,
|
||||||
'cid': 0
|
'cid': 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -1056,30 +1008,28 @@ def test_create_transfer_transaction_multiple_io(user_pub, user_priv,
|
|||||||
},
|
},
|
||||||
'version': 1
|
'version': 1
|
||||||
}
|
}
|
||||||
tx1_inputs = tx1.to_inputs()
|
|
||||||
tx2_inputs = tx2.to_inputs()
|
|
||||||
tx_inputs = tx1_inputs + tx2_inputs
|
|
||||||
|
|
||||||
transfer_tx = Transaction.transfer(tx_inputs, [[user2_pub], [user2_pub]])
|
transfer_tx = Transaction.transfer(tx.to_inputs(),
|
||||||
|
[([user2_pub], 1), ([user2_pub], 1)],
|
||||||
|
asset=tx.asset)
|
||||||
transfer_tx = transfer_tx.sign([user_priv, user2_priv])
|
transfer_tx = transfer_tx.sign([user_priv, user2_priv])
|
||||||
transfer_tx = transfer_tx
|
|
||||||
|
|
||||||
assert len(transfer_tx.fulfillments) == 2
|
assert len(transfer_tx.fulfillments) == 2
|
||||||
assert len(transfer_tx.conditions) == 2
|
assert len(transfer_tx.conditions) == 2
|
||||||
|
|
||||||
combined_conditions = tx1.conditions + tx2.conditions
|
assert transfer_tx.fulfillments_valid(tx.conditions) is True
|
||||||
assert transfer_tx.fulfillments_valid(combined_conditions) is True
|
|
||||||
|
|
||||||
transfer_tx = transfer_tx.to_dict()
|
transfer_tx = transfer_tx.to_dict()
|
||||||
transfer_tx['transaction']['fulfillments'][0]['fulfillment'] = None
|
transfer_tx['transaction']['fulfillments'][0]['fulfillment'] = None
|
||||||
transfer_tx['transaction']['fulfillments'][1]['fulfillment'] = None
|
transfer_tx['transaction']['fulfillments'][1]['fulfillment'] = None
|
||||||
transfer_tx['transaction'].pop('timestamp')
|
transfer_tx['transaction'].pop('timestamp')
|
||||||
transfer_tx.pop('id')
|
transfer_tx.pop('id')
|
||||||
|
transfer_tx['transaction'].pop('asset')
|
||||||
|
|
||||||
assert expected == transfer_tx
|
assert expected == transfer_tx
|
||||||
|
|
||||||
|
|
||||||
def test_create_transfer_with_invalid_parameters():
|
def test_create_transfer_with_invalid_parameters(user_pub):
|
||||||
from bigchaindb.common.transaction import Transaction, Asset
|
from bigchaindb.common.transaction import Transaction, Asset
|
||||||
|
|
||||||
with raises(TypeError):
|
with raises(TypeError):
|
||||||
@ -1090,17 +1040,25 @@ def test_create_transfer_with_invalid_parameters():
|
|||||||
Transaction.transfer(['fulfillment'], {}, Asset())
|
Transaction.transfer(['fulfillment'], {}, Asset())
|
||||||
with raises(ValueError):
|
with raises(ValueError):
|
||||||
Transaction.transfer(['fulfillment'], [], Asset())
|
Transaction.transfer(['fulfillment'], [], Asset())
|
||||||
|
with raises(ValueError):
|
||||||
|
Transaction.transfer(['fulfillment'], [user_pub], Asset())
|
||||||
|
with raises(ValueError):
|
||||||
|
Transaction.transfer(['fulfillment'], [([user_pub],)], Asset())
|
||||||
|
|
||||||
|
|
||||||
def test_cant_add_empty_condition():
|
def test_cant_add_empty_condition():
|
||||||
from bigchaindb.common.transaction import Transaction
|
from bigchaindb.common.transaction import Transaction, Asset
|
||||||
tx = Transaction(Transaction.CREATE, None)
|
|
||||||
|
with patch.object(Asset, 'validate_asset', return_value=None):
|
||||||
|
tx = Transaction(Transaction.CREATE, None)
|
||||||
with raises(TypeError):
|
with raises(TypeError):
|
||||||
tx.add_condition(None)
|
tx.add_condition(None)
|
||||||
|
|
||||||
|
|
||||||
def test_cant_add_empty_fulfillment():
|
def test_cant_add_empty_fulfillment():
|
||||||
from bigchaindb.common.transaction import Transaction
|
from bigchaindb.common.transaction import Transaction, Asset
|
||||||
tx = Transaction(Transaction.CREATE, None)
|
|
||||||
|
with patch.object(Asset, 'validate_asset', return_value=None):
|
||||||
|
tx = Transaction(Transaction.CREATE, None)
|
||||||
with raises(TypeError):
|
with raises(TypeError):
|
||||||
tx.add_fulfillment(None)
|
tx.add_fulfillment(None)
|
||||||
|
@ -72,7 +72,7 @@ def b(request, node_config):
|
|||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def create_tx(b, user_pk):
|
def create_tx(b, user_pk):
|
||||||
from bigchaindb.models import Transaction
|
from bigchaindb.models import Transaction
|
||||||
return Transaction.create([b.me], [user_pk])
|
return Transaction.create([b.me], [([user_pk], 1)])
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@ -84,5 +84,5 @@ def signed_create_tx(b, create_tx):
|
|||||||
def signed_transfer_tx(signed_create_tx, user_pk, user_sk):
|
def signed_transfer_tx(signed_create_tx, user_pk, user_sk):
|
||||||
from bigchaindb.models import Transaction
|
from bigchaindb.models import Transaction
|
||||||
inputs = signed_create_tx.to_inputs()
|
inputs = signed_create_tx.to_inputs()
|
||||||
tx = Transaction.transfer(inputs, [user_pk], signed_create_tx.asset)
|
tx = Transaction.transfer(inputs, [([user_pk], 1)], signed_create_tx.asset)
|
||||||
return tx.sign([user_sk])
|
return tx.sign([user_sk])
|
||||||
|
@ -84,7 +84,7 @@ def inputs(user_pk):
|
|||||||
prev_block_id = g.id
|
prev_block_id = g.id
|
||||||
for block in range(4):
|
for block in range(4):
|
||||||
transactions = [
|
transactions = [
|
||||||
Transaction.create([b.me], [user_pk]).sign([b.me_private])
|
Transaction.create([b.me], [([user_pk], 1)]).sign([b.me_private])
|
||||||
for i in range(10)
|
for i in range(10)
|
||||||
]
|
]
|
||||||
block = b.create_block(transactions)
|
block = b.create_block(transactions)
|
||||||
|
@ -15,7 +15,7 @@ def dummy_tx():
|
|||||||
import bigchaindb
|
import bigchaindb
|
||||||
from bigchaindb.models import Transaction
|
from bigchaindb.models import Transaction
|
||||||
b = bigchaindb.Bigchain()
|
b = bigchaindb.Bigchain()
|
||||||
tx = Transaction.create([b.me], [b.me])
|
tx = Transaction.create([b.me], [([b.me], 1)])
|
||||||
tx = tx.sign([b.me_private])
|
tx = tx.sign([b.me_private])
|
||||||
return tx
|
return tx
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ class TestBigchainApi(object):
|
|||||||
|
|
||||||
b.create_genesis_block()
|
b.create_genesis_block()
|
||||||
|
|
||||||
tx = Transaction.create([b.me], [b.me])
|
tx = Transaction.create([b.me], [([b.me], 1)])
|
||||||
tx = tx.sign([b.me_private])
|
tx = tx.sign([b.me_private])
|
||||||
monkeypatch.setattr('time.time', lambda: 1)
|
monkeypatch.setattr('time.time', lambda: 1)
|
||||||
block1 = b.create_block([tx])
|
block1 = b.create_block([tx])
|
||||||
@ -60,7 +60,7 @@ class TestBigchainApi(object):
|
|||||||
|
|
||||||
b.create_genesis_block()
|
b.create_genesis_block()
|
||||||
|
|
||||||
tx = Transaction.create([b.me], [b.me])
|
tx = Transaction.create([b.me], [([b.me], 1)])
|
||||||
tx = tx.sign([b.me_private])
|
tx = tx.sign([b.me_private])
|
||||||
block1 = b.create_block([tx])
|
block1 = b.create_block([tx])
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ class TestBigchainApi(object):
|
|||||||
|
|
||||||
b.create_genesis_block()
|
b.create_genesis_block()
|
||||||
|
|
||||||
tx = Transaction.create([b.me], [b.me])
|
tx = Transaction.create([b.me], [([b.me], 1)])
|
||||||
tx = tx.sign([b.me_private])
|
tx = tx.sign([b.me_private])
|
||||||
|
|
||||||
monkeypatch.setattr('time.time', lambda: 1)
|
monkeypatch.setattr('time.time', lambda: 1)
|
||||||
@ -99,7 +99,7 @@ class TestBigchainApi(object):
|
|||||||
|
|
||||||
b.create_genesis_block()
|
b.create_genesis_block()
|
||||||
|
|
||||||
tx = Transaction.create([b.me], [b.me])
|
tx = Transaction.create([b.me], [([b.me], 1)])
|
||||||
tx = tx.sign([b.me_private])
|
tx = tx.sign([b.me_private])
|
||||||
|
|
||||||
monkeypatch.setattr('time.time', lambda: 1)
|
monkeypatch.setattr('time.time', lambda: 1)
|
||||||
@ -107,13 +107,15 @@ class TestBigchainApi(object):
|
|||||||
b.write_block(block1, durability='hard')
|
b.write_block(block1, durability='hard')
|
||||||
|
|
||||||
monkeypatch.setattr('time.time', lambda: 2)
|
monkeypatch.setattr('time.time', lambda: 2)
|
||||||
transfer_tx = Transaction.transfer(tx.to_inputs(), [b.me], tx.asset)
|
transfer_tx = Transaction.transfer(tx.to_inputs(), [([b.me], 1)],
|
||||||
|
tx.asset)
|
||||||
transfer_tx = transfer_tx.sign([b.me_private])
|
transfer_tx = transfer_tx.sign([b.me_private])
|
||||||
block2 = b.create_block([transfer_tx])
|
block2 = b.create_block([transfer_tx])
|
||||||
b.write_block(block2, durability='hard')
|
b.write_block(block2, durability='hard')
|
||||||
|
|
||||||
monkeypatch.setattr('time.time', lambda: 3)
|
monkeypatch.setattr('time.time', lambda: 3)
|
||||||
transfer_tx2 = Transaction.transfer(tx.to_inputs(), [b.me], tx.asset)
|
transfer_tx2 = Transaction.transfer(tx.to_inputs(), [([b.me], 1)],
|
||||||
|
tx.asset)
|
||||||
transfer_tx2 = transfer_tx2.sign([b.me_private])
|
transfer_tx2 = transfer_tx2.sign([b.me_private])
|
||||||
block3 = b.create_block([transfer_tx2])
|
block3 = b.create_block([transfer_tx2])
|
||||||
b.write_block(block3, durability='hard')
|
b.write_block(block3, durability='hard')
|
||||||
@ -133,7 +135,7 @@ class TestBigchainApi(object):
|
|||||||
|
|
||||||
b.create_genesis_block()
|
b.create_genesis_block()
|
||||||
|
|
||||||
tx = Transaction.create([b.me], [b.me])
|
tx = Transaction.create([b.me], [([b.me], 1)])
|
||||||
tx = tx.sign([b.me_private])
|
tx = tx.sign([b.me_private])
|
||||||
|
|
||||||
monkeypatch.setattr('time.time', lambda: 1)
|
monkeypatch.setattr('time.time', lambda: 1)
|
||||||
@ -159,13 +161,13 @@ class TestBigchainApi(object):
|
|||||||
b.create_genesis_block()
|
b.create_genesis_block()
|
||||||
|
|
||||||
monkeypatch.setattr('time.time', lambda: 1)
|
monkeypatch.setattr('time.time', lambda: 1)
|
||||||
tx1 = Transaction.create([b.me], [b.me])
|
tx1 = Transaction.create([b.me], [([b.me], 1)])
|
||||||
tx1 = tx1.sign([b.me_private])
|
tx1 = tx1.sign([b.me_private])
|
||||||
block1 = b.create_block([tx1])
|
block1 = b.create_block([tx1])
|
||||||
b.write_block(block1, durability='hard')
|
b.write_block(block1, durability='hard')
|
||||||
|
|
||||||
monkeypatch.setattr('time.time', lambda: 2)
|
monkeypatch.setattr('time.time', lambda: 2)
|
||||||
tx2 = Transaction.create([b.me], [b.me])
|
tx2 = Transaction.create([b.me], [([b.me], 1)])
|
||||||
tx2 = tx2.sign([b.me_private])
|
tx2 = tx2.sign([b.me_private])
|
||||||
block2 = b.create_block([tx2])
|
block2 = b.create_block([tx2])
|
||||||
b.write_block(block2, durability='hard')
|
b.write_block(block2, durability='hard')
|
||||||
@ -185,7 +187,7 @@ class TestBigchainApi(object):
|
|||||||
from bigchaindb.models import Transaction
|
from bigchaindb.models import Transaction
|
||||||
|
|
||||||
metadata = {'msg': 'Hello BigchainDB!'}
|
metadata = {'msg': 'Hello BigchainDB!'}
|
||||||
tx = Transaction.create([b.me], [user_pk], metadata=metadata)
|
tx = Transaction.create([b.me], [([user_pk], 1)], metadata=metadata)
|
||||||
|
|
||||||
block = b.create_block([tx])
|
block = b.create_block([tx])
|
||||||
b.write_block(block, durability='hard')
|
b.write_block(block, durability='hard')
|
||||||
@ -205,7 +207,7 @@ class TestBigchainApi(object):
|
|||||||
input_tx = b.get_owned_ids(user_pk).pop()
|
input_tx = b.get_owned_ids(user_pk).pop()
|
||||||
input_tx = b.get_transaction(input_tx.txid)
|
input_tx = b.get_transaction(input_tx.txid)
|
||||||
inputs = input_tx.to_inputs()
|
inputs = input_tx.to_inputs()
|
||||||
tx = Transaction.transfer(inputs, [user_pk], input_tx.asset)
|
tx = Transaction.transfer(inputs, [([user_pk], 1)], input_tx.asset)
|
||||||
tx = tx.sign([user_sk])
|
tx = tx.sign([user_sk])
|
||||||
response = b.write_transaction(tx)
|
response = b.write_transaction(tx)
|
||||||
|
|
||||||
@ -223,7 +225,7 @@ class TestBigchainApi(object):
|
|||||||
input_tx = b.get_owned_ids(user_pk).pop()
|
input_tx = b.get_owned_ids(user_pk).pop()
|
||||||
input_tx = b.get_transaction(input_tx.txid)
|
input_tx = b.get_transaction(input_tx.txid)
|
||||||
inputs = input_tx.to_inputs()
|
inputs = input_tx.to_inputs()
|
||||||
tx = Transaction.transfer(inputs, [user_pk], input_tx.asset)
|
tx = Transaction.transfer(inputs, [([user_pk], 1)], input_tx.asset)
|
||||||
tx = tx.sign([user_sk])
|
tx = tx.sign([user_sk])
|
||||||
b.write_transaction(tx)
|
b.write_transaction(tx)
|
||||||
|
|
||||||
@ -243,7 +245,7 @@ class TestBigchainApi(object):
|
|||||||
input_tx = b.get_owned_ids(user_pk).pop()
|
input_tx = b.get_owned_ids(user_pk).pop()
|
||||||
input_tx = b.get_transaction(input_tx.txid)
|
input_tx = b.get_transaction(input_tx.txid)
|
||||||
inputs = input_tx.to_inputs()
|
inputs = input_tx.to_inputs()
|
||||||
tx = Transaction.transfer(inputs, [user_pk], input_tx.asset)
|
tx = Transaction.transfer(inputs, [([user_pk], 1)], input_tx.asset)
|
||||||
tx = tx.sign([user_sk])
|
tx = tx.sign([user_sk])
|
||||||
# There's no need to b.write_transaction(tx) to the backlog
|
# There's no need to b.write_transaction(tx) to the backlog
|
||||||
|
|
||||||
@ -267,7 +269,7 @@ class TestBigchainApi(object):
|
|||||||
input_tx = b.get_owned_ids(user_pk).pop()
|
input_tx = b.get_owned_ids(user_pk).pop()
|
||||||
input_tx = b.get_transaction(input_tx.txid)
|
input_tx = b.get_transaction(input_tx.txid)
|
||||||
inputs = input_tx.to_inputs()
|
inputs = input_tx.to_inputs()
|
||||||
tx = Transaction.transfer(inputs, [user_pk], input_tx.asset)
|
tx = Transaction.transfer(inputs, [([user_pk], 1)], input_tx.asset)
|
||||||
tx = tx.sign([user_sk])
|
tx = tx.sign([user_sk])
|
||||||
|
|
||||||
# Make sure there's a copy of tx in the backlog
|
# Make sure there's a copy of tx in the backlog
|
||||||
@ -369,6 +371,15 @@ class TestBigchainApi(object):
|
|||||||
|
|
||||||
assert excinfo.value.args[0] == 'Empty block creation is not allowed'
|
assert excinfo.value.args[0] == 'Empty block creation is not allowed'
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures('inputs')
|
||||||
|
def test_get_block_by_id(self, b):
|
||||||
|
new_block = dummy_block()
|
||||||
|
b.write_block(new_block, durability='hard')
|
||||||
|
|
||||||
|
assert b.get_block(new_block.id) == new_block.to_dict()
|
||||||
|
block, status = b.get_block(new_block.id, include_status=True)
|
||||||
|
assert status == b.BLOCK_UNDECIDED
|
||||||
|
|
||||||
def test_get_last_voted_block_returns_genesis_if_no_votes_has_been_casted(self, b):
|
def test_get_last_voted_block_returns_genesis_if_no_votes_has_been_casted(self, b):
|
||||||
import rethinkdb as r
|
import rethinkdb as r
|
||||||
from bigchaindb import util
|
from bigchaindb import util
|
||||||
@ -528,7 +539,7 @@ class TestBigchainApi(object):
|
|||||||
input_tx = b.get_owned_ids(user_pk).pop()
|
input_tx = b.get_owned_ids(user_pk).pop()
|
||||||
input_tx = b.get_transaction(input_tx.txid)
|
input_tx = b.get_transaction(input_tx.txid)
|
||||||
inputs = input_tx.to_inputs()
|
inputs = input_tx.to_inputs()
|
||||||
tx = Transaction.transfer(inputs, [user_pk], input_tx.asset)
|
tx = Transaction.transfer(inputs, [([user_pk], 1)], input_tx.asset)
|
||||||
tx = tx.sign([user_sk])
|
tx = tx.sign([user_sk])
|
||||||
b.write_transaction(tx)
|
b.write_transaction(tx)
|
||||||
|
|
||||||
@ -554,7 +565,7 @@ class TestBigchainApi(object):
|
|||||||
input_tx = b.get_owned_ids(user_pk).pop()
|
input_tx = b.get_owned_ids(user_pk).pop()
|
||||||
input_tx = b.get_transaction(input_tx.txid)
|
input_tx = b.get_transaction(input_tx.txid)
|
||||||
inputs = input_tx.to_inputs()
|
inputs = input_tx.to_inputs()
|
||||||
tx = Transaction.transfer(inputs, [user_pk], input_tx.asset)
|
tx = Transaction.transfer(inputs, [([user_pk], 1)], input_tx.asset)
|
||||||
tx = tx.sign([user_sk])
|
tx = tx.sign([user_sk])
|
||||||
b.write_transaction(tx)
|
b.write_transaction(tx)
|
||||||
|
|
||||||
@ -578,11 +589,21 @@ class TestBigchainApi(object):
|
|||||||
fulfillment = Fulfillment(Ed25519Fulfillment(public_key=user_pk),
|
fulfillment = Fulfillment(Ed25519Fulfillment(public_key=user_pk),
|
||||||
[user_pk],
|
[user_pk],
|
||||||
TransactionLink('somethingsomething', 0))
|
TransactionLink('somethingsomething', 0))
|
||||||
tx = Transaction.transfer([fulfillment], [user_pk], Asset())
|
tx = Transaction.transfer([fulfillment], [([user_pk], 1)], Asset())
|
||||||
|
|
||||||
with pytest.raises(TransactionDoesNotExist) as excinfo:
|
with pytest.raises(TransactionDoesNotExist):
|
||||||
tx.validate(Bigchain())
|
tx.validate(Bigchain())
|
||||||
|
|
||||||
|
def test_count_backlog(self, b, user_vk):
|
||||||
|
from bigchaindb.models import Transaction
|
||||||
|
|
||||||
|
for _ in range(4):
|
||||||
|
tx = Transaction.create([b.me],
|
||||||
|
[([user_vk], 1)]).sign([b.me_private])
|
||||||
|
b.write_transaction(tx)
|
||||||
|
|
||||||
|
assert b.backend.count_backlog() == 4
|
||||||
|
|
||||||
|
|
||||||
class TestTransactionValidation(object):
|
class TestTransactionValidation(object):
|
||||||
def test_create_operation_with_inputs(self, b, user_pk, create_tx):
|
def test_create_operation_with_inputs(self, b, user_pk, create_tx):
|
||||||
@ -620,7 +641,7 @@ class TestTransactionValidation(object):
|
|||||||
input_tx = b.get_owned_ids(user_pk).pop()
|
input_tx = b.get_owned_ids(user_pk).pop()
|
||||||
input_transaction = b.get_transaction(input_tx.txid)
|
input_transaction = b.get_transaction(input_tx.txid)
|
||||||
sk, pk = generate_key_pair()
|
sk, pk = generate_key_pair()
|
||||||
tx = Transaction.create([pk], [user_pk])
|
tx = Transaction.create([pk], [([user_pk], 1)])
|
||||||
tx.operation = 'TRANSFER'
|
tx.operation = 'TRANSFER'
|
||||||
tx.asset = input_transaction.asset
|
tx.asset = input_transaction.asset
|
||||||
tx.fulfillments[0].tx_input = input_tx
|
tx.fulfillments[0].tx_input = input_tx
|
||||||
@ -664,7 +685,8 @@ class TestTransactionValidation(object):
|
|||||||
input_tx = b.get_owned_ids(user_pk).pop()
|
input_tx = b.get_owned_ids(user_pk).pop()
|
||||||
input_tx = b.get_transaction(input_tx.txid)
|
input_tx = b.get_transaction(input_tx.txid)
|
||||||
inputs = input_tx.to_inputs()
|
inputs = input_tx.to_inputs()
|
||||||
transfer_tx = Transaction.transfer(inputs, [user_pk], input_tx.asset)
|
transfer_tx = Transaction.transfer(inputs, [([user_pk], 1)],
|
||||||
|
input_tx.asset)
|
||||||
transfer_tx = transfer_tx.sign([user_sk])
|
transfer_tx = transfer_tx.sign([user_sk])
|
||||||
|
|
||||||
assert transfer_tx == b.validate_transaction(transfer_tx)
|
assert transfer_tx == b.validate_transaction(transfer_tx)
|
||||||
@ -679,16 +701,17 @@ class TestTransactionValidation(object):
|
|||||||
assert transfer_tx == b.validate_transaction(transfer_tx)
|
assert transfer_tx == b.validate_transaction(transfer_tx)
|
||||||
|
|
||||||
@pytest.mark.usefixtures('inputs')
|
@pytest.mark.usefixtures('inputs')
|
||||||
def test_fulfillment_not_in_valid_block(self, b, user_pk, user_sk):
|
def test_transaction_not_in_valid_block(self, b, user_pk, user_sk):
|
||||||
from bigchaindb.models import Transaction
|
from bigchaindb.models import Transaction
|
||||||
from bigchaindb.common.exceptions import FulfillmentNotInValidBlock
|
from bigchaindb.common.exceptions import TransactionNotInValidBlock
|
||||||
|
|
||||||
input_tx = b.get_owned_ids(user_pk).pop()
|
input_tx = b.get_owned_ids(user_pk).pop()
|
||||||
input_tx = b.get_transaction(input_tx.txid)
|
input_tx = b.get_transaction(input_tx.txid)
|
||||||
inputs = input_tx.to_inputs()
|
inputs = input_tx.to_inputs()
|
||||||
|
|
||||||
# create a transaction that's valid but not in a voted valid block
|
# create a transaction that's valid but not in a voted valid block
|
||||||
transfer_tx = Transaction.transfer(inputs, [user_pk], input_tx.asset)
|
transfer_tx = Transaction.transfer(inputs, [([user_pk], 1)],
|
||||||
|
input_tx.asset)
|
||||||
transfer_tx = transfer_tx.sign([user_sk])
|
transfer_tx = transfer_tx.sign([user_sk])
|
||||||
|
|
||||||
assert transfer_tx == b.validate_transaction(transfer_tx)
|
assert transfer_tx == b.validate_transaction(transfer_tx)
|
||||||
@ -698,11 +721,12 @@ class TestTransactionValidation(object):
|
|||||||
b.write_block(block, durability='hard')
|
b.write_block(block, durability='hard')
|
||||||
|
|
||||||
# create transaction with the undecided input
|
# create transaction with the undecided input
|
||||||
tx_invalid = Transaction.transfer(transfer_tx.to_inputs(), [user_pk],
|
tx_invalid = Transaction.transfer(transfer_tx.to_inputs(),
|
||||||
transfer_tx.asset)
|
[([user_pk], 1)],
|
||||||
|
transfer_tx.asset)
|
||||||
tx_invalid = tx_invalid.sign([user_sk])
|
tx_invalid = tx_invalid.sign([user_sk])
|
||||||
|
|
||||||
with pytest.raises(FulfillmentNotInValidBlock):
|
with pytest.raises(TransactionNotInValidBlock):
|
||||||
b.validate_transaction(tx_invalid)
|
b.validate_transaction(tx_invalid)
|
||||||
|
|
||||||
|
|
||||||
@ -797,7 +821,7 @@ class TestMultipleInputs(object):
|
|||||||
tx_link = b.get_owned_ids(user_pk).pop()
|
tx_link = b.get_owned_ids(user_pk).pop()
|
||||||
input_tx = b.get_transaction(tx_link.txid)
|
input_tx = b.get_transaction(tx_link.txid)
|
||||||
inputs = input_tx.to_inputs()
|
inputs = input_tx.to_inputs()
|
||||||
tx = Transaction.transfer(inputs, [user2_pk], input_tx.asset)
|
tx = Transaction.transfer(inputs, [([user2_pk], 1)], input_tx.asset)
|
||||||
tx = tx.sign([user_sk])
|
tx = tx.sign([user_sk])
|
||||||
|
|
||||||
# validate transaction
|
# validate transaction
|
||||||
@ -805,69 +829,6 @@ class TestMultipleInputs(object):
|
|||||||
assert len(tx.fulfillments) == 1
|
assert len(tx.fulfillments) == 1
|
||||||
assert len(tx.conditions) == 1
|
assert len(tx.conditions) == 1
|
||||||
|
|
||||||
@pytest.mark.skipif(reason=('Multiple inputs are only allowed for the '
|
|
||||||
'same asset. Remove this after implementing ',
|
|
||||||
'multiple assets'))
|
|
||||||
@pytest.mark.usefixtures('inputs')
|
|
||||||
def test_transfer_single_owners_multiple_inputs(self, b, user_sk, user_pk):
|
|
||||||
from bigchaindb.common import crypto
|
|
||||||
from bigchaindb.models import Transaction
|
|
||||||
|
|
||||||
user2_sk, user2_pk = crypto.generate_key_pair()
|
|
||||||
|
|
||||||
# get inputs
|
|
||||||
owned_inputs = b.get_owned_ids(user_pk)
|
|
||||||
input_txs = [b.get_transaction(tx_link.txid) for tx_link
|
|
||||||
in owned_inputs]
|
|
||||||
inputs = sum([input_tx.to_inputs() for input_tx in input_txs], [])
|
|
||||||
tx = Transaction.transfer(inputs, len(inputs) * [[user_pk]])
|
|
||||||
tx = tx.sign([user_sk])
|
|
||||||
assert b.validate_transaction(tx) == tx
|
|
||||||
assert len(tx.fulfillments) == len(inputs)
|
|
||||||
assert len(tx.conditions) == len(inputs)
|
|
||||||
|
|
||||||
@pytest.mark.skipif(reason=('Multiple inputs are only allowed for the '
|
|
||||||
'same asset. Remove this after implementing ',
|
|
||||||
'multiple assets'))
|
|
||||||
@pytest.mark.usefixtures('inputs')
|
|
||||||
def test_transfer_single_owners_single_input_from_multiple_outputs(self, b,
|
|
||||||
user_sk,
|
|
||||||
user_pk):
|
|
||||||
from bigchaindb.common import crypto
|
|
||||||
from bigchaindb.models import Transaction
|
|
||||||
|
|
||||||
user2_sk, user2_pk = crypto.generate_key_pair()
|
|
||||||
|
|
||||||
# get inputs
|
|
||||||
owned_inputs = b.get_owned_ids(user_pk)
|
|
||||||
input_txs = [b.get_transaction(tx_link.txid) for tx_link
|
|
||||||
in owned_inputs]
|
|
||||||
inputs = sum([input_tx.to_inputs() for input_tx in input_txs], [])
|
|
||||||
tx = Transaction.transfer(inputs, len(inputs) * [[user2_pk]])
|
|
||||||
tx = tx.sign([user_sk])
|
|
||||||
|
|
||||||
# create block with the transaction
|
|
||||||
block = b.create_block([tx])
|
|
||||||
b.write_block(block, durability='hard')
|
|
||||||
|
|
||||||
# vote block valid
|
|
||||||
vote = b.vote(block.id, b.get_last_voted_block().id, True)
|
|
||||||
b.write_vote(vote)
|
|
||||||
|
|
||||||
# get inputs from user2
|
|
||||||
owned_inputs = b.get_owned_ids(user2_pk)
|
|
||||||
assert len(owned_inputs) == len(inputs)
|
|
||||||
|
|
||||||
# create a transaction with a single input from a multiple output transaction
|
|
||||||
tx_link = owned_inputs.pop()
|
|
||||||
inputs = b.get_transaction(tx_link.txid).to_inputs([0])
|
|
||||||
tx = Transaction.transfer(inputs, [user_pk])
|
|
||||||
tx = tx.sign([user2_sk])
|
|
||||||
|
|
||||||
assert b.is_valid_transaction(tx) == tx
|
|
||||||
assert len(tx.fulfillments) == 1
|
|
||||||
assert len(tx.conditions) == 1
|
|
||||||
|
|
||||||
def test_single_owner_before_multiple_owners_after_single_input(self, b,
|
def test_single_owner_before_multiple_owners_after_single_input(self, b,
|
||||||
user_sk,
|
user_sk,
|
||||||
user_pk,
|
user_pk,
|
||||||
@ -881,47 +842,14 @@ class TestMultipleInputs(object):
|
|||||||
owned_inputs = b.get_owned_ids(user_pk)
|
owned_inputs = b.get_owned_ids(user_pk)
|
||||||
tx_link = owned_inputs.pop()
|
tx_link = owned_inputs.pop()
|
||||||
input_tx = b.get_transaction(tx_link.txid)
|
input_tx = b.get_transaction(tx_link.txid)
|
||||||
tx = Transaction.transfer(input_tx.to_inputs(), [[user2_pk, user3_pk]], input_tx.asset)
|
tx = Transaction.transfer(input_tx.to_inputs(),
|
||||||
|
[([user2_pk, user3_pk], 1)], input_tx.asset)
|
||||||
tx = tx.sign([user_sk])
|
tx = tx.sign([user_sk])
|
||||||
|
|
||||||
assert b.is_valid_transaction(tx) == tx
|
assert b.is_valid_transaction(tx) == tx
|
||||||
assert len(tx.fulfillments) == 1
|
assert len(tx.fulfillments) == 1
|
||||||
assert len(tx.conditions) == 1
|
assert len(tx.conditions) == 1
|
||||||
|
|
||||||
@pytest.mark.skipif(reason=('Multiple inputs are only allowed for the '
|
|
||||||
'same asset. Remove this after implementing ',
|
|
||||||
'multiple assets'))
|
|
||||||
@pytest.mark.usefixtures('inputs')
|
|
||||||
def test_single_owner_before_multiple_owners_after_multiple_inputs(self, b,
|
|
||||||
user_sk,
|
|
||||||
user_pk):
|
|
||||||
from bigchaindb.common import crypto
|
|
||||||
from bigchaindb.models import Transaction
|
|
||||||
|
|
||||||
user2_sk, user2_pk = crypto.generate_key_pair()
|
|
||||||
user3_sk, user3_pk = crypto.generate_key_pair()
|
|
||||||
|
|
||||||
owned_inputs = b.get_owned_ids(user_pk)
|
|
||||||
input_txs = [b.get_transaction(tx_link.txid) for tx_link
|
|
||||||
in owned_inputs]
|
|
||||||
inputs = sum([input_tx.to_inputs() for input_tx in input_txs], [])
|
|
||||||
|
|
||||||
tx = Transaction.transfer(inputs, len(inputs) * [[user2_pk, user3_pk]])
|
|
||||||
tx = tx.sign([user_sk])
|
|
||||||
|
|
||||||
# create block with the transaction
|
|
||||||
block = b.create_block([tx])
|
|
||||||
b.write_block(block, durability='hard')
|
|
||||||
|
|
||||||
# vote block valid
|
|
||||||
vote = b.vote(block.id, b.get_last_voted_block().id, True)
|
|
||||||
b.write_vote(vote)
|
|
||||||
|
|
||||||
# validate transaction
|
|
||||||
assert b.is_valid_transaction(tx) == tx
|
|
||||||
assert len(tx.fulfillments) == len(inputs)
|
|
||||||
assert len(tx.conditions) == len(inputs)
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures('inputs')
|
@pytest.mark.usefixtures('inputs')
|
||||||
def test_multiple_owners_before_single_owner_after_single_input(self, b,
|
def test_multiple_owners_before_single_owner_after_single_input(self, b,
|
||||||
user_sk,
|
user_sk,
|
||||||
@ -932,7 +860,7 @@ class TestMultipleInputs(object):
|
|||||||
user2_sk, user2_pk = crypto.generate_key_pair()
|
user2_sk, user2_pk = crypto.generate_key_pair()
|
||||||
user3_sk, user3_pk = crypto.generate_key_pair()
|
user3_sk, user3_pk = crypto.generate_key_pair()
|
||||||
|
|
||||||
tx = Transaction.create([b.me], [user_pk, user2_pk])
|
tx = Transaction.create([b.me], [([user_pk, user2_pk], 1)])
|
||||||
tx = tx.sign([b.me_private])
|
tx = tx.sign([b.me_private])
|
||||||
block = b.create_block([tx])
|
block = b.create_block([tx])
|
||||||
b.write_block(block, durability='hard')
|
b.write_block(block, durability='hard')
|
||||||
@ -945,7 +873,8 @@ class TestMultipleInputs(object):
|
|||||||
input_tx = b.get_transaction(owned_input.txid)
|
input_tx = b.get_transaction(owned_input.txid)
|
||||||
inputs = input_tx.to_inputs()
|
inputs = input_tx.to_inputs()
|
||||||
|
|
||||||
transfer_tx = Transaction.transfer(inputs, [user3_pk], input_tx.asset)
|
transfer_tx = Transaction.transfer(inputs, [([user3_pk], 1)],
|
||||||
|
input_tx.asset)
|
||||||
transfer_tx = transfer_tx.sign([user_sk, user2_sk])
|
transfer_tx = transfer_tx.sign([user_sk, user2_sk])
|
||||||
|
|
||||||
# validate transaction
|
# validate transaction
|
||||||
@ -953,29 +882,6 @@ class TestMultipleInputs(object):
|
|||||||
assert len(transfer_tx.fulfillments) == 1
|
assert len(transfer_tx.fulfillments) == 1
|
||||||
assert len(transfer_tx.conditions) == 1
|
assert len(transfer_tx.conditions) == 1
|
||||||
|
|
||||||
@pytest.mark.skipif(reason=('Multiple inputs are only allowed for the '
|
|
||||||
'same asset. Remove this after implementing ',
|
|
||||||
'multiple assets'))
|
|
||||||
@pytest.mark.usefixtures('inputs_shared')
|
|
||||||
def test_multiple_owners_before_single_owner_after_multiple_inputs(self, b,
|
|
||||||
user_sk, user_pk, user2_pk, user2_sk):
|
|
||||||
from bigchaindb.common import crypto
|
|
||||||
from bigchaindb.models import Transaction
|
|
||||||
|
|
||||||
# create a new users
|
|
||||||
user3_sk, user3_pk = crypto.generate_key_pair()
|
|
||||||
|
|
||||||
tx_links = b.get_owned_ids(user_pk)
|
|
||||||
inputs = sum([b.get_transaction(tx_link.txid).to_inputs() for tx_link
|
|
||||||
in tx_links], [])
|
|
||||||
|
|
||||||
tx = Transaction.transfer(inputs, len(inputs) * [[user3_pk]])
|
|
||||||
tx = tx.sign([user_sk, user2_sk])
|
|
||||||
|
|
||||||
assert b.is_valid_transaction(tx) == tx
|
|
||||||
assert len(tx.fulfillments) == len(inputs)
|
|
||||||
assert len(tx.conditions) == len(inputs)
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures('inputs')
|
@pytest.mark.usefixtures('inputs')
|
||||||
def test_multiple_owners_before_multiple_owners_after_single_input(self, b,
|
def test_multiple_owners_before_multiple_owners_after_single_input(self, b,
|
||||||
user_sk,
|
user_sk,
|
||||||
@ -987,7 +893,7 @@ class TestMultipleInputs(object):
|
|||||||
user3_sk, user3_pk = crypto.generate_key_pair()
|
user3_sk, user3_pk = crypto.generate_key_pair()
|
||||||
user4_sk, user4_pk = crypto.generate_key_pair()
|
user4_sk, user4_pk = crypto.generate_key_pair()
|
||||||
|
|
||||||
tx = Transaction.create([b.me], [user_pk, user2_pk])
|
tx = Transaction.create([b.me], [([user_pk, user2_pk], 1)])
|
||||||
tx = tx.sign([b.me_private])
|
tx = tx.sign([b.me_private])
|
||||||
block = b.create_block([tx])
|
block = b.create_block([tx])
|
||||||
b.write_block(block, durability='hard')
|
b.write_block(block, durability='hard')
|
||||||
@ -1000,38 +906,14 @@ class TestMultipleInputs(object):
|
|||||||
tx_link = b.get_owned_ids(user_pk).pop()
|
tx_link = b.get_owned_ids(user_pk).pop()
|
||||||
tx_input = b.get_transaction(tx_link.txid)
|
tx_input = b.get_transaction(tx_link.txid)
|
||||||
|
|
||||||
tx = Transaction.transfer(tx_input.to_inputs(), [[user3_pk, user4_pk]], tx_input.asset)
|
tx = Transaction.transfer(tx_input.to_inputs(),
|
||||||
|
[([user3_pk, user4_pk], 1)], tx_input.asset)
|
||||||
tx = tx.sign([user_sk, user2_sk])
|
tx = tx.sign([user_sk, user2_sk])
|
||||||
|
|
||||||
assert b.is_valid_transaction(tx) == tx
|
assert b.is_valid_transaction(tx) == tx
|
||||||
assert len(tx.fulfillments) == 1
|
assert len(tx.fulfillments) == 1
|
||||||
assert len(tx.conditions) == 1
|
assert len(tx.conditions) == 1
|
||||||
|
|
||||||
@pytest.mark.skipif(reason=('Multiple inputs are only allowed for the '
|
|
||||||
'same asset. Remove this after implementing ',
|
|
||||||
'multiple assets'))
|
|
||||||
@pytest.mark.usefixtures('inputs_shared')
|
|
||||||
def test_multiple_owners_before_multiple_owners_after_multiple_inputs(self, b,
|
|
||||||
user_sk, user_pk,
|
|
||||||
user2_sk, user2_pk):
|
|
||||||
from bigchaindb.common import crypto
|
|
||||||
from bigchaindb.models import Transaction
|
|
||||||
|
|
||||||
# create a new users
|
|
||||||
user3_sk, user3_pk = crypto.generate_key_pair()
|
|
||||||
user4_sk, user4_pk = crypto.generate_key_pair()
|
|
||||||
|
|
||||||
tx_links = b.get_owned_ids(user_pk)
|
|
||||||
inputs = sum([b.get_transaction(tx_link.txid).to_inputs() for tx_link
|
|
||||||
in tx_links], [])
|
|
||||||
|
|
||||||
tx = Transaction.transfer(inputs, len(inputs) * [[user3_pk, user4_pk]])
|
|
||||||
tx = tx.sign([user_sk, user2_sk])
|
|
||||||
|
|
||||||
assert b.is_valid_transaction(tx) == tx
|
|
||||||
assert len(tx.fulfillments) == len(inputs)
|
|
||||||
assert len(tx.conditions) == len(inputs)
|
|
||||||
|
|
||||||
def test_get_owned_ids_single_tx_single_output(self, b, user_sk, user_pk):
|
def test_get_owned_ids_single_tx_single_output(self, b, user_sk, user_pk):
|
||||||
from bigchaindb.common import crypto
|
from bigchaindb.common import crypto
|
||||||
from bigchaindb.common.transaction import TransactionLink
|
from bigchaindb.common.transaction import TransactionLink
|
||||||
@ -1039,7 +921,7 @@ class TestMultipleInputs(object):
|
|||||||
|
|
||||||
user2_sk, user2_pk = crypto.generate_key_pair()
|
user2_sk, user2_pk = crypto.generate_key_pair()
|
||||||
|
|
||||||
tx = Transaction.create([b.me], [user_pk])
|
tx = Transaction.create([b.me], [([user_pk], 1)])
|
||||||
tx = tx.sign([b.me_private])
|
tx = tx.sign([b.me_private])
|
||||||
block = b.create_block([tx])
|
block = b.create_block([tx])
|
||||||
b.write_block(block, durability='hard')
|
b.write_block(block, durability='hard')
|
||||||
@ -1049,7 +931,7 @@ class TestMultipleInputs(object):
|
|||||||
assert owned_inputs_user1 == [TransactionLink(tx.id, 0)]
|
assert owned_inputs_user1 == [TransactionLink(tx.id, 0)]
|
||||||
assert owned_inputs_user2 == []
|
assert owned_inputs_user2 == []
|
||||||
|
|
||||||
tx = Transaction.transfer(tx.to_inputs(), [user2_pk], tx.asset)
|
tx = Transaction.transfer(tx.to_inputs(), [([user2_pk], 1)], tx.asset)
|
||||||
tx = tx.sign([user_sk])
|
tx = tx.sign([user_sk])
|
||||||
block = b.create_block([tx])
|
block = b.create_block([tx])
|
||||||
b.write_block(block, durability='hard')
|
b.write_block(block, durability='hard')
|
||||||
@ -1069,7 +951,7 @@ class TestMultipleInputs(object):
|
|||||||
genesis = b.create_genesis_block()
|
genesis = b.create_genesis_block()
|
||||||
user2_sk, user2_pk = crypto.generate_key_pair()
|
user2_sk, user2_pk = crypto.generate_key_pair()
|
||||||
|
|
||||||
tx = Transaction.create([b.me], [user_pk])
|
tx = Transaction.create([b.me], [([user_pk], 1)])
|
||||||
tx = tx.sign([b.me_private])
|
tx = tx.sign([b.me_private])
|
||||||
block = b.create_block([tx])
|
block = b.create_block([tx])
|
||||||
b.write_block(block, durability='hard')
|
b.write_block(block, durability='hard')
|
||||||
@ -1085,7 +967,8 @@ class TestMultipleInputs(object):
|
|||||||
|
|
||||||
# NOTE: The transaction itself is valid, still will mark the block
|
# NOTE: The transaction itself is valid, still will mark the block
|
||||||
# as invalid to mock the behavior.
|
# as invalid to mock the behavior.
|
||||||
tx_invalid = Transaction.transfer(tx.to_inputs(), [user2_pk], tx.asset)
|
tx_invalid = Transaction.transfer(tx.to_inputs(), [([user2_pk], 1)],
|
||||||
|
tx.asset)
|
||||||
tx_invalid = tx_invalid.sign([user_sk])
|
tx_invalid = tx_invalid.sign([user_sk])
|
||||||
block = b.create_block([tx_invalid])
|
block = b.create_block([tx_invalid])
|
||||||
b.write_block(block, durability='hard')
|
b.write_block(block, durability='hard')
|
||||||
@ -1101,47 +984,45 @@ class TestMultipleInputs(object):
|
|||||||
assert owned_inputs_user1 == [TransactionLink(tx.id, 0)]
|
assert owned_inputs_user1 == [TransactionLink(tx.id, 0)]
|
||||||
assert owned_inputs_user2 == []
|
assert owned_inputs_user2 == []
|
||||||
|
|
||||||
@pytest.mark.skipif(reason=('Multiple inputs are only allowed for the '
|
|
||||||
'same asset. Remove this after implementing ',
|
|
||||||
'multiple assets'))
|
|
||||||
def test_get_owned_ids_single_tx_multiple_outputs(self, b, user_sk,
|
def test_get_owned_ids_single_tx_multiple_outputs(self, b, user_sk,
|
||||||
user_pk):
|
user_pk):
|
||||||
import random
|
|
||||||
from bigchaindb.common import crypto
|
from bigchaindb.common import crypto
|
||||||
from bigchaindb.common.transaction import TransactionLink
|
from bigchaindb.common.transaction import TransactionLink, Asset
|
||||||
from bigchaindb.models import Transaction
|
from bigchaindb.models import Transaction
|
||||||
|
|
||||||
user2_sk, user2_pk = crypto.generate_key_pair()
|
user2_sk, user2_pk = crypto.generate_key_pair()
|
||||||
|
|
||||||
transactions = []
|
# create divisible asset
|
||||||
for i in range(2):
|
asset = Asset(divisible=True)
|
||||||
payload = {'somedata': random.randint(0, 255)}
|
tx_create = Transaction.create([b.me],
|
||||||
tx = Transaction.create([b.me], [user_pk], payload)
|
[([user_pk], 1), ([user_pk], 1)],
|
||||||
tx = tx.sign([b.me_private])
|
asset=asset)
|
||||||
transactions.append(tx)
|
tx_create_signed = tx_create.sign([b.me_private])
|
||||||
block = b.create_block(transactions)
|
block = b.create_block([tx_create_signed])
|
||||||
b.write_block(block, durability='hard')
|
b.write_block(block, durability='hard')
|
||||||
|
|
||||||
# get input
|
# get input
|
||||||
owned_inputs_user1 = b.get_owned_ids(user_pk)
|
owned_inputs_user1 = b.get_owned_ids(user_pk)
|
||||||
owned_inputs_user2 = b.get_owned_ids(user2_pk)
|
owned_inputs_user2 = b.get_owned_ids(user2_pk)
|
||||||
|
|
||||||
expected_owned_inputs_user1 = [TransactionLink(tx.id, 0) for tx
|
expected_owned_inputs_user1 = [TransactionLink(tx_create.id, 0),
|
||||||
in transactions]
|
TransactionLink(tx_create.id, 1)]
|
||||||
assert owned_inputs_user1 == expected_owned_inputs_user1
|
assert owned_inputs_user1 == expected_owned_inputs_user1
|
||||||
assert owned_inputs_user2 == []
|
assert owned_inputs_user2 == []
|
||||||
|
|
||||||
inputs = sum([tx.to_inputs() for tx in transactions], [])
|
# transfer divisible asset divided in two outputs
|
||||||
tx = Transaction.transfer(inputs, len(inputs) * [[user2_pk]])
|
tx_transfer = Transaction.transfer(tx_create.to_inputs(),
|
||||||
tx = tx.sign([user_sk])
|
[([user2_pk], 1), ([user2_pk], 1)],
|
||||||
block = b.create_block([tx])
|
asset=tx_create.asset)
|
||||||
|
tx_transfer_signed = tx_transfer.sign([user_sk])
|
||||||
|
block = b.create_block([tx_transfer_signed])
|
||||||
b.write_block(block, durability='hard')
|
b.write_block(block, durability='hard')
|
||||||
|
|
||||||
owned_inputs_user1 = b.get_owned_ids(user_pk)
|
owned_inputs_user1 = b.get_owned_ids(user_pk)
|
||||||
owned_inputs_user2 = b.get_owned_ids(user2_pk)
|
owned_inputs_user2 = b.get_owned_ids(user2_pk)
|
||||||
assert owned_inputs_user1 == []
|
assert owned_inputs_user1 == []
|
||||||
assert owned_inputs_user2 == [TransactionLink(tx.id, 0),
|
assert owned_inputs_user2 == [TransactionLink(tx_transfer.id, 0),
|
||||||
TransactionLink(tx.id, 1)]
|
TransactionLink(tx_transfer.id, 1)]
|
||||||
|
|
||||||
def test_get_owned_ids_multiple_owners(self, b, user_sk, user_pk):
|
def test_get_owned_ids_multiple_owners(self, b, user_sk, user_pk):
|
||||||
from bigchaindb.common import crypto
|
from bigchaindb.common import crypto
|
||||||
@ -1151,7 +1032,7 @@ class TestMultipleInputs(object):
|
|||||||
user2_sk, user2_pk = crypto.generate_key_pair()
|
user2_sk, user2_pk = crypto.generate_key_pair()
|
||||||
user3_sk, user3_pk = crypto.generate_key_pair()
|
user3_sk, user3_pk = crypto.generate_key_pair()
|
||||||
|
|
||||||
tx = Transaction.create([b.me], [user_pk, user2_pk])
|
tx = Transaction.create([b.me], [([user_pk, user2_pk], 1)])
|
||||||
tx = tx.sign([b.me_private])
|
tx = tx.sign([b.me_private])
|
||||||
block = b.create_block([tx])
|
block = b.create_block([tx])
|
||||||
b.write_block(block, durability='hard')
|
b.write_block(block, durability='hard')
|
||||||
@ -1163,7 +1044,7 @@ class TestMultipleInputs(object):
|
|||||||
assert owned_inputs_user1 == owned_inputs_user2
|
assert owned_inputs_user1 == owned_inputs_user2
|
||||||
assert owned_inputs_user1 == expected_owned_inputs_user1
|
assert owned_inputs_user1 == expected_owned_inputs_user1
|
||||||
|
|
||||||
tx = Transaction.transfer(tx.to_inputs(), [user3_pk], tx.asset)
|
tx = Transaction.transfer(tx.to_inputs(), [([user3_pk], 1)], tx.asset)
|
||||||
tx = tx.sign([user_sk, user2_sk])
|
tx = tx.sign([user_sk, user2_sk])
|
||||||
block = b.create_block([tx])
|
block = b.create_block([tx])
|
||||||
b.write_block(block, durability='hard')
|
b.write_block(block, durability='hard')
|
||||||
@ -1179,7 +1060,7 @@ class TestMultipleInputs(object):
|
|||||||
|
|
||||||
user2_sk, user2_pk = crypto.generate_key_pair()
|
user2_sk, user2_pk = crypto.generate_key_pair()
|
||||||
|
|
||||||
tx = Transaction.create([b.me], [user_pk])
|
tx = Transaction.create([b.me], [([user_pk], 1)])
|
||||||
tx = tx.sign([b.me_private])
|
tx = tx.sign([b.me_private])
|
||||||
block = b.create_block([tx])
|
block = b.create_block([tx])
|
||||||
b.write_block(block, durability='hard')
|
b.write_block(block, durability='hard')
|
||||||
@ -1193,7 +1074,7 @@ class TestMultipleInputs(object):
|
|||||||
assert spent_inputs_user1 is None
|
assert spent_inputs_user1 is None
|
||||||
|
|
||||||
# create a transaction and block
|
# create a transaction and block
|
||||||
tx = Transaction.transfer(tx.to_inputs(), [user2_pk], tx.asset)
|
tx = Transaction.transfer(tx.to_inputs(), [([user2_pk], 1)], tx.asset)
|
||||||
tx = tx.sign([user_sk])
|
tx = tx.sign([user_sk])
|
||||||
block = b.create_block([tx])
|
block = b.create_block([tx])
|
||||||
b.write_block(block, durability='hard')
|
b.write_block(block, durability='hard')
|
||||||
@ -1210,7 +1091,7 @@ class TestMultipleInputs(object):
|
|||||||
# create a new users
|
# create a new users
|
||||||
user2_sk, user2_pk = crypto.generate_key_pair()
|
user2_sk, user2_pk = crypto.generate_key_pair()
|
||||||
|
|
||||||
tx = Transaction.create([b.me], [user_pk])
|
tx = Transaction.create([b.me], [([user_pk], 1)])
|
||||||
tx = tx.sign([b.me_private])
|
tx = tx.sign([b.me_private])
|
||||||
block = b.create_block([tx])
|
block = b.create_block([tx])
|
||||||
b.write_block(block, durability='hard')
|
b.write_block(block, durability='hard')
|
||||||
@ -1228,7 +1109,7 @@ class TestMultipleInputs(object):
|
|||||||
assert spent_inputs_user1 is None
|
assert spent_inputs_user1 is None
|
||||||
|
|
||||||
# create a transaction and block
|
# create a transaction and block
|
||||||
tx = Transaction.transfer(tx.to_inputs(), [user2_pk], tx.asset)
|
tx = Transaction.transfer(tx.to_inputs(), [([user2_pk], 1)], tx.asset)
|
||||||
tx = tx.sign([user_sk])
|
tx = tx.sign([user_sk])
|
||||||
block = b.create_block([tx])
|
block = b.create_block([tx])
|
||||||
b.write_block(block, durability='hard')
|
b.write_block(block, durability='hard')
|
||||||
@ -1243,24 +1124,23 @@ class TestMultipleInputs(object):
|
|||||||
# Now there should be no spents (the block is invalid)
|
# Now there should be no spents (the block is invalid)
|
||||||
assert spent_inputs_user1 is None
|
assert spent_inputs_user1 is None
|
||||||
|
|
||||||
@pytest.mark.skipif(reason=('Multiple inputs are only allowed for the '
|
|
||||||
'same asset. Remove this after implementing ',
|
|
||||||
'multiple assets'))
|
|
||||||
def test_get_spent_single_tx_multiple_outputs(self, b, user_sk, user_pk):
|
def test_get_spent_single_tx_multiple_outputs(self, b, user_sk, user_pk):
|
||||||
import random
|
|
||||||
from bigchaindb.common import crypto
|
from bigchaindb.common import crypto
|
||||||
from bigchaindb.models import Transaction
|
from bigchaindb.models import Transaction
|
||||||
|
from bigchaindb.common.transaction import Asset
|
||||||
|
|
||||||
# create a new users
|
# create a new users
|
||||||
user2_sk, user2_pk = crypto.generate_key_pair()
|
user2_sk, user2_pk = crypto.generate_key_pair()
|
||||||
|
|
||||||
transactions = []
|
# create a divisible asset with 3 outputs
|
||||||
for i in range(3):
|
asset = Asset(divisible=True)
|
||||||
payload = {'somedata': random.randint(0, 255)}
|
tx_create = Transaction.create([b.me],
|
||||||
tx = Transaction.create([b.me], [user_pk], payload)
|
[([user_pk], 1),
|
||||||
tx = tx.sign([b.me_private])
|
([user_pk], 1),
|
||||||
transactions.append(tx)
|
([user_pk], 1)],
|
||||||
block = b.create_block(transactions)
|
asset=asset)
|
||||||
|
tx_create_signed = tx_create.sign([b.me_private])
|
||||||
|
block = b.create_block([tx_create_signed])
|
||||||
b.write_block(block, durability='hard')
|
b.write_block(block, durability='hard')
|
||||||
|
|
||||||
owned_inputs_user1 = b.get_owned_ids(user_pk)
|
owned_inputs_user1 = b.get_owned_ids(user_pk)
|
||||||
@ -1269,22 +1149,22 @@ class TestMultipleInputs(object):
|
|||||||
for input_tx in owned_inputs_user1:
|
for input_tx in owned_inputs_user1:
|
||||||
assert b.get_spent(input_tx.txid, input_tx.cid) is None
|
assert b.get_spent(input_tx.txid, input_tx.cid) is None
|
||||||
|
|
||||||
# select inputs to use
|
# transfer the first 2 inputs
|
||||||
inputs = sum([tx.to_inputs() for tx in transactions[:2]], [])
|
tx_transfer = Transaction.transfer(tx_create.to_inputs()[:2],
|
||||||
|
[([user2_pk], 1), ([user2_pk], 1)],
|
||||||
# create a transaction and block
|
asset=tx_create.asset)
|
||||||
tx = Transaction.transfer(inputs, len(inputs) * [[user2_pk]])
|
tx_transfer_signed = tx_transfer.sign([user_sk])
|
||||||
tx = tx.sign([user_sk])
|
block = b.create_block([tx_transfer_signed])
|
||||||
block = b.create_block([tx])
|
|
||||||
b.write_block(block, durability='hard')
|
b.write_block(block, durability='hard')
|
||||||
|
|
||||||
# check that used inputs are marked as spent
|
# check that used inputs are marked as spent
|
||||||
for ffill in inputs:
|
for ffill in tx_create.to_inputs()[:2]:
|
||||||
assert b.get_spent(ffill.tx_input.txid, ffill.tx_input.cid) == tx
|
spent_tx = b.get_spent(ffill.tx_input.txid, ffill.tx_input.cid)
|
||||||
|
assert spent_tx == tx_transfer_signed
|
||||||
|
|
||||||
# check if remaining transaction that was unspent is also perceived
|
# check if remaining transaction that was unspent is also perceived
|
||||||
# spendable by BigchainDB
|
# spendable by BigchainDB
|
||||||
assert b.get_spent(transactions[2].id, 0) is None
|
assert b.get_spent(tx_create.to_inputs()[2].tx_input.txid, 2) is None
|
||||||
|
|
||||||
def test_get_spent_multiple_owners(self, b, user_sk, user_pk):
|
def test_get_spent_multiple_owners(self, b, user_sk, user_pk):
|
||||||
import random
|
import random
|
||||||
@ -1297,7 +1177,8 @@ class TestMultipleInputs(object):
|
|||||||
transactions = []
|
transactions = []
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
payload = {'somedata': random.randint(0, 255)}
|
payload = {'somedata': random.randint(0, 255)}
|
||||||
tx = Transaction.create([b.me], [user_pk, user2_pk], payload)
|
tx = Transaction.create([b.me], [([user_pk, user2_pk], 1)],
|
||||||
|
payload)
|
||||||
tx = tx.sign([b.me_private])
|
tx = tx.sign([b.me_private])
|
||||||
transactions.append(tx)
|
transactions.append(tx)
|
||||||
block = b.create_block(transactions)
|
block = b.create_block(transactions)
|
||||||
@ -1310,7 +1191,8 @@ class TestMultipleInputs(object):
|
|||||||
assert b.get_spent(input_tx.txid, input_tx.cid) is None
|
assert b.get_spent(input_tx.txid, input_tx.cid) is None
|
||||||
|
|
||||||
# create a transaction
|
# create a transaction
|
||||||
tx = Transaction.transfer(transactions[0].to_inputs(), [user3_pk], transactions[0].asset)
|
tx = Transaction.transfer(transactions[0].to_inputs(),
|
||||||
|
[([user3_pk], 1)], transactions[0].asset)
|
||||||
tx = tx.sign([user_sk, user2_sk])
|
tx = tx.sign([user_sk, user2_sk])
|
||||||
block = b.create_block([tx])
|
block = b.create_block([tx])
|
||||||
b.write_block(block, durability='hard')
|
b.write_block(block, durability='hard')
|
||||||
|
@ -45,7 +45,7 @@ def test_create_block(b, user_pk):
|
|||||||
block_maker = BlockPipeline()
|
block_maker = BlockPipeline()
|
||||||
|
|
||||||
for i in range(100):
|
for i in range(100):
|
||||||
tx = Transaction.create([b.me], [user_pk])
|
tx = Transaction.create([b.me], [([user_pk], 1)])
|
||||||
tx = tx.sign([b.me_private])
|
tx = tx.sign([b.me_private])
|
||||||
block_maker.create(tx)
|
block_maker.create(tx)
|
||||||
|
|
||||||
@ -63,7 +63,7 @@ def test_write_block(b, user_pk):
|
|||||||
|
|
||||||
txs = []
|
txs = []
|
||||||
for i in range(100):
|
for i in range(100):
|
||||||
tx = Transaction.create([b.me], [user_pk])
|
tx = Transaction.create([b.me], [([user_pk], 1)])
|
||||||
tx = tx.sign([b.me_private])
|
tx = tx.sign([b.me_private])
|
||||||
txs.append(tx)
|
txs.append(tx)
|
||||||
|
|
||||||
@ -82,7 +82,7 @@ def test_duplicate_transaction(b, user_pk):
|
|||||||
|
|
||||||
txs = []
|
txs = []
|
||||||
for i in range(10):
|
for i in range(10):
|
||||||
tx = Transaction.create([b.me], [user_pk])
|
tx = Transaction.create([b.me], [([user_pk], 1)])
|
||||||
tx = tx.sign([b.me_private])
|
tx = tx.sign([b.me_private])
|
||||||
txs.append(tx)
|
txs.append(tx)
|
||||||
|
|
||||||
@ -109,7 +109,7 @@ def test_delete_tx(b, user_pk):
|
|||||||
from bigchaindb.pipelines.block import BlockPipeline
|
from bigchaindb.pipelines.block import BlockPipeline
|
||||||
block_maker = BlockPipeline()
|
block_maker = BlockPipeline()
|
||||||
for i in range(100):
|
for i in range(100):
|
||||||
tx = Transaction.create([b.me], [user_pk])
|
tx = Transaction.create([b.me], [([user_pk], 1)])
|
||||||
tx = tx.sign([b.me_private])
|
tx = tx.sign([b.me_private])
|
||||||
block_maker.create(tx)
|
block_maker.create(tx)
|
||||||
# make sure the tx appears in the backlog
|
# make sure the tx appears in the backlog
|
||||||
@ -138,7 +138,8 @@ def test_prefeed(b, user_pk):
|
|||||||
from bigchaindb.pipelines.block import initial
|
from bigchaindb.pipelines.block import initial
|
||||||
|
|
||||||
for i in range(100):
|
for i in range(100):
|
||||||
tx = Transaction.create([b.me], [user_pk], {'msg': random.random()})
|
tx = Transaction.create([b.me], [([user_pk], 1)],
|
||||||
|
{'msg': random.random()})
|
||||||
tx = tx.sign([b.me_private])
|
tx = tx.sign([b.me_private])
|
||||||
b.write_transaction(tx)
|
b.write_transaction(tx)
|
||||||
|
|
||||||
@ -167,7 +168,8 @@ def test_full_pipeline(b, user_pk):
|
|||||||
|
|
||||||
count_assigned_to_me = 0
|
count_assigned_to_me = 0
|
||||||
for i in range(100):
|
for i in range(100):
|
||||||
tx = Transaction.create([b.me], [user_pk], {'msg': random.random()})
|
tx = Transaction.create([b.me], [([user_pk], 1)],
|
||||||
|
{'msg': random.random()})
|
||||||
tx = tx.sign([b.me_private]).to_dict()
|
tx = tx.sign([b.me_private]).to_dict()
|
||||||
assignee = random.choice([b.me, 'aaa', 'bbb', 'ccc'])
|
assignee = random.choice([b.me, 'aaa', 'bbb', 'ccc'])
|
||||||
tx['assignee'] = assignee
|
tx['assignee'] = assignee
|
||||||
|
@ -15,7 +15,7 @@ def test_check_for_quorum_invalid(b, user_pk):
|
|||||||
e = election.Election()
|
e = election.Election()
|
||||||
|
|
||||||
# create blocks with transactions
|
# create blocks with transactions
|
||||||
tx1 = Transaction.create([b.me], [user_pk])
|
tx1 = Transaction.create([b.me], [([user_pk], 1)])
|
||||||
test_block = b.create_block([tx1])
|
test_block = b.create_block([tx1])
|
||||||
|
|
||||||
# simulate a federation with four voters
|
# simulate a federation with four voters
|
||||||
@ -44,7 +44,7 @@ def test_check_for_quorum_invalid_prev_node(b, user_pk):
|
|||||||
e = election.Election()
|
e = election.Election()
|
||||||
|
|
||||||
# create blocks with transactions
|
# create blocks with transactions
|
||||||
tx1 = Transaction.create([b.me], [user_pk])
|
tx1 = Transaction.create([b.me], [([user_pk], 1)])
|
||||||
test_block = b.create_block([tx1])
|
test_block = b.create_block([tx1])
|
||||||
|
|
||||||
# simulate a federation with four voters
|
# simulate a federation with four voters
|
||||||
@ -74,7 +74,7 @@ def test_check_for_quorum_valid(b, user_pk):
|
|||||||
e = election.Election()
|
e = election.Election()
|
||||||
|
|
||||||
# create blocks with transactions
|
# create blocks with transactions
|
||||||
tx1 = Transaction.create([b.me], [user_pk])
|
tx1 = Transaction.create([b.me], [([user_pk], 1)])
|
||||||
test_block = b.create_block([tx1])
|
test_block = b.create_block([tx1])
|
||||||
|
|
||||||
# simulate a federation with four voters
|
# simulate a federation with four voters
|
||||||
@ -103,7 +103,7 @@ def test_check_requeue_transaction(b, user_pk):
|
|||||||
e = election.Election()
|
e = election.Election()
|
||||||
|
|
||||||
# create blocks with transactions
|
# create blocks with transactions
|
||||||
tx1 = Transaction.create([b.me], [user_pk])
|
tx1 = Transaction.create([b.me], [([user_pk], 1)])
|
||||||
test_block = b.create_block([tx1])
|
test_block = b.create_block([tx1])
|
||||||
|
|
||||||
e.requeue_transactions(test_block)
|
e.requeue_transactions(test_block)
|
||||||
@ -131,7 +131,8 @@ def test_full_pipeline(b, user_pk):
|
|||||||
# write two blocks
|
# write two blocks
|
||||||
txs = []
|
txs = []
|
||||||
for i in range(100):
|
for i in range(100):
|
||||||
tx = Transaction.create([b.me], [user_pk], {'msg': random.random()})
|
tx = Transaction.create([b.me], [([user_pk], 1)],
|
||||||
|
{'msg': random.random()})
|
||||||
tx = tx.sign([b.me_private])
|
tx = tx.sign([b.me_private])
|
||||||
txs.append(tx)
|
txs.append(tx)
|
||||||
|
|
||||||
@ -140,7 +141,8 @@ def test_full_pipeline(b, user_pk):
|
|||||||
|
|
||||||
txs = []
|
txs = []
|
||||||
for i in range(100):
|
for i in range(100):
|
||||||
tx = Transaction.create([b.me], [user_pk], {'msg': random.random()})
|
tx = Transaction.create([b.me], [([user_pk], 1)],
|
||||||
|
{'msg': random.random()})
|
||||||
tx = tx.sign([b.me_private])
|
tx = tx.sign([b.me_private])
|
||||||
txs.append(tx)
|
txs.append(tx)
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ import os
|
|||||||
|
|
||||||
def test_get_stale(b, user_pk):
|
def test_get_stale(b, user_pk):
|
||||||
from bigchaindb.models import Transaction
|
from bigchaindb.models import Transaction
|
||||||
tx = Transaction.create([b.me], [user_pk])
|
tx = Transaction.create([b.me], [([user_pk], 1)])
|
||||||
tx = tx.sign([b.me_private])
|
tx = tx.sign([b.me_private])
|
||||||
b.write_transaction(tx, durability='hard')
|
b.write_transaction(tx, durability='hard')
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ def test_get_stale(b, user_pk):
|
|||||||
def test_reassign_transactions(b, user_pk):
|
def test_reassign_transactions(b, user_pk):
|
||||||
from bigchaindb.models import Transaction
|
from bigchaindb.models import Transaction
|
||||||
# test with single node
|
# test with single node
|
||||||
tx = Transaction.create([b.me], [user_pk])
|
tx = Transaction.create([b.me], [([user_pk], 1)])
|
||||||
tx = tx.sign([b.me_private])
|
tx = tx.sign([b.me_private])
|
||||||
b.write_transaction(tx, durability='hard')
|
b.write_transaction(tx, durability='hard')
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ def test_reassign_transactions(b, user_pk):
|
|||||||
stm.reassign_transactions(tx.to_dict())
|
stm.reassign_transactions(tx.to_dict())
|
||||||
|
|
||||||
# test with federation
|
# test with federation
|
||||||
tx = Transaction.create([b.me], [user_pk])
|
tx = Transaction.create([b.me], [([user_pk], 1)])
|
||||||
tx = tx.sign([b.me_private])
|
tx = tx.sign([b.me_private])
|
||||||
b.write_transaction(tx, durability='hard')
|
b.write_transaction(tx, durability='hard')
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ def test_reassign_transactions(b, user_pk):
|
|||||||
assert reassigned_tx['assignee'] != tx['assignee']
|
assert reassigned_tx['assignee'] != tx['assignee']
|
||||||
|
|
||||||
# test with node not in federation
|
# test with node not in federation
|
||||||
tx = Transaction.create([b.me], [user_pk])
|
tx = Transaction.create([b.me], [([user_pk], 1)])
|
||||||
tx = tx.sign([b.me_private]).to_dict()
|
tx = tx.sign([b.me_private]).to_dict()
|
||||||
tx.update({'assignee': 'lol'})
|
tx.update({'assignee': 'lol'})
|
||||||
tx.update({'assignment_timestamp': time.time()})
|
tx.update({'assignment_timestamp': time.time()})
|
||||||
@ -85,7 +85,7 @@ def test_full_pipeline(monkeypatch, user_pk):
|
|||||||
monkeypatch.setattr('time.time', lambda: 1)
|
monkeypatch.setattr('time.time', lambda: 1)
|
||||||
|
|
||||||
for i in range(100):
|
for i in range(100):
|
||||||
tx = Transaction.create([b.me], [user_pk])
|
tx = Transaction.create([b.me], [([user_pk], 1)])
|
||||||
tx = tx.sign([b.me_private])
|
tx = tx.sign([b.me_private])
|
||||||
original_txc.append(tx.to_dict())
|
original_txc.append(tx.to_dict())
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ from multipipes import Pipe, Pipeline
|
|||||||
|
|
||||||
def dummy_tx(b):
|
def dummy_tx(b):
|
||||||
from bigchaindb.models import Transaction
|
from bigchaindb.models import Transaction
|
||||||
tx = Transaction.create([b.me], [b.me])
|
tx = Transaction.create([b.me], [([b.me], 1)])
|
||||||
tx = tx.sign([b.me_private])
|
tx = tx.sign([b.me_private])
|
||||||
return tx
|
return tx
|
||||||
|
|
||||||
@ -130,7 +130,7 @@ def test_vote_validate_transaction(b):
|
|||||||
assert validation == (True, 123, 1)
|
assert validation == (True, 123, 1)
|
||||||
|
|
||||||
# NOTE: Submit unsigned transaction to `validate_tx` yields `False`.
|
# NOTE: Submit unsigned transaction to `validate_tx` yields `False`.
|
||||||
tx = Transaction.create([b.me], [b.me])
|
tx = Transaction.create([b.me], [([b.me], 1)])
|
||||||
validation = vote_obj.validate_tx(tx, 456, 10)
|
validation = vote_obj.validate_tx(tx, 456, 10)
|
||||||
assert validation == (False, 456, 10)
|
assert validation == (False, 456, 10)
|
||||||
|
|
||||||
@ -224,7 +224,7 @@ def test_valid_block_voting_with_create_transaction(b, monkeypatch):
|
|||||||
|
|
||||||
# create a `CREATE` transaction
|
# create a `CREATE` transaction
|
||||||
test_user_priv, test_user_pub = crypto.generate_key_pair()
|
test_user_priv, test_user_pub = crypto.generate_key_pair()
|
||||||
tx = Transaction.create([b.me], [test_user_pub])
|
tx = Transaction.create([b.me], [([test_user_pub], 1)])
|
||||||
tx = tx.sign([b.me_private])
|
tx = tx.sign([b.me_private])
|
||||||
|
|
||||||
monkeypatch.setattr('time.time', lambda: 1)
|
monkeypatch.setattr('time.time', lambda: 1)
|
||||||
@ -265,7 +265,7 @@ def test_valid_block_voting_with_transfer_transactions(monkeypatch, b):
|
|||||||
|
|
||||||
# create a `CREATE` transaction
|
# create a `CREATE` transaction
|
||||||
test_user_priv, test_user_pub = crypto.generate_key_pair()
|
test_user_priv, test_user_pub = crypto.generate_key_pair()
|
||||||
tx = Transaction.create([b.me], [test_user_pub])
|
tx = Transaction.create([b.me], [([test_user_pub], 1)])
|
||||||
tx = tx.sign([b.me_private])
|
tx = tx.sign([b.me_private])
|
||||||
|
|
||||||
monkeypatch.setattr('time.time', lambda: 1)
|
monkeypatch.setattr('time.time', lambda: 1)
|
||||||
@ -274,7 +274,8 @@ def test_valid_block_voting_with_transfer_transactions(monkeypatch, b):
|
|||||||
|
|
||||||
# create a `TRANSFER` transaction
|
# create a `TRANSFER` transaction
|
||||||
test_user2_priv, test_user2_pub = crypto.generate_key_pair()
|
test_user2_priv, test_user2_pub = crypto.generate_key_pair()
|
||||||
tx2 = Transaction.transfer(tx.to_inputs(), [test_user2_pub], tx.asset)
|
tx2 = Transaction.transfer(tx.to_inputs(), [([test_user2_pub], 1)],
|
||||||
|
tx.asset)
|
||||||
tx2 = tx2.sign([test_user_priv])
|
tx2 = tx2.sign([test_user_priv])
|
||||||
|
|
||||||
monkeypatch.setattr('time.time', lambda: 2)
|
monkeypatch.setattr('time.time', lambda: 2)
|
||||||
@ -338,7 +339,7 @@ def test_unsigned_tx_in_block_voting(monkeypatch, b, user_pk):
|
|||||||
vote_pipeline.setup(indata=inpipe, outdata=outpipe)
|
vote_pipeline.setup(indata=inpipe, outdata=outpipe)
|
||||||
|
|
||||||
# NOTE: `tx` is invalid, because it wasn't signed.
|
# NOTE: `tx` is invalid, because it wasn't signed.
|
||||||
tx = Transaction.create([b.me], [b.me])
|
tx = Transaction.create([b.me], [([b.me], 1)])
|
||||||
block = b.create_block([tx])
|
block = b.create_block([tx])
|
||||||
|
|
||||||
inpipe.put(block.to_dict())
|
inpipe.put(block.to_dict())
|
||||||
@ -375,7 +376,7 @@ def test_invalid_id_tx_in_block_voting(monkeypatch, b, user_pk):
|
|||||||
vote_pipeline.setup(indata=inpipe, outdata=outpipe)
|
vote_pipeline.setup(indata=inpipe, outdata=outpipe)
|
||||||
|
|
||||||
# NOTE: `tx` is invalid, because its id is not corresponding to its content
|
# NOTE: `tx` is invalid, because its id is not corresponding to its content
|
||||||
tx = Transaction.create([b.me], [b.me])
|
tx = Transaction.create([b.me], [([b.me], 1)])
|
||||||
tx = tx.sign([b.me_private])
|
tx = tx.sign([b.me_private])
|
||||||
block = b.create_block([tx]).to_dict()
|
block = b.create_block([tx]).to_dict()
|
||||||
block['block']['transactions'][0]['id'] = 'an invalid tx id'
|
block['block']['transactions'][0]['id'] = 'an invalid tx id'
|
||||||
@ -414,7 +415,7 @@ def test_invalid_content_in_tx_in_block_voting(monkeypatch, b, user_pk):
|
|||||||
vote_pipeline.setup(indata=inpipe, outdata=outpipe)
|
vote_pipeline.setup(indata=inpipe, outdata=outpipe)
|
||||||
|
|
||||||
# NOTE: `tx` is invalid, because its content is not corresponding to its id
|
# NOTE: `tx` is invalid, because its content is not corresponding to its id
|
||||||
tx = Transaction.create([b.me], [b.me])
|
tx = Transaction.create([b.me], [([b.me], 1)])
|
||||||
tx = tx.sign([b.me_private])
|
tx = tx.sign([b.me_private])
|
||||||
block = b.create_block([tx]).to_dict()
|
block = b.create_block([tx]).to_dict()
|
||||||
block['block']['transactions'][0]['id'] = 'an invalid tx id'
|
block['block']['transactions'][0]['id'] = 'an invalid tx id'
|
||||||
|
@ -5,7 +5,7 @@ class TestTransactionModel(object):
|
|||||||
def test_validating_an_invalid_transaction(self, b):
|
def test_validating_an_invalid_transaction(self, b):
|
||||||
from bigchaindb.models import Transaction
|
from bigchaindb.models import Transaction
|
||||||
|
|
||||||
tx = Transaction.create([b.me], [b.me])
|
tx = Transaction.create([b.me], [([b.me], 1)])
|
||||||
tx.operation = 'something invalid'
|
tx.operation = 'something invalid'
|
||||||
|
|
||||||
with raises(TypeError):
|
with raises(TypeError):
|
||||||
@ -41,7 +41,7 @@ class TestBlockModel(object):
|
|||||||
from bigchaindb.common.util import gen_timestamp, serialize
|
from bigchaindb.common.util import gen_timestamp, serialize
|
||||||
from bigchaindb.models import Block, Transaction
|
from bigchaindb.models import Block, Transaction
|
||||||
|
|
||||||
transactions = [Transaction.create([b.me], [b.me])]
|
transactions = [Transaction.create([b.me], [([b.me], 1)])]
|
||||||
timestamp = gen_timestamp()
|
timestamp = gen_timestamp()
|
||||||
voters = ['Qaaa', 'Qbbb']
|
voters = ['Qaaa', 'Qbbb']
|
||||||
expected_block = {
|
expected_block = {
|
||||||
@ -73,7 +73,7 @@ class TestBlockModel(object):
|
|||||||
from bigchaindb.common.util import gen_timestamp, serialize
|
from bigchaindb.common.util import gen_timestamp, serialize
|
||||||
from bigchaindb.models import Block, Transaction
|
from bigchaindb.models import Block, Transaction
|
||||||
|
|
||||||
transactions = [Transaction.create([b.me], [b.me])]
|
transactions = [Transaction.create([b.me], [([b.me], 1)])]
|
||||||
timestamp = gen_timestamp()
|
timestamp = gen_timestamp()
|
||||||
voters = ['Qaaa', 'Qbbb']
|
voters = ['Qaaa', 'Qbbb']
|
||||||
expected = Block(transactions, b.me, timestamp, voters)
|
expected = Block(transactions, b.me, timestamp, voters)
|
||||||
@ -113,7 +113,7 @@ class TestBlockModel(object):
|
|||||||
from bigchaindb.common.util import gen_timestamp, serialize
|
from bigchaindb.common.util import gen_timestamp, serialize
|
||||||
from bigchaindb.models import Block, Transaction
|
from bigchaindb.models import Block, Transaction
|
||||||
|
|
||||||
transactions = [Transaction.create([b.me], [b.me])]
|
transactions = [Transaction.create([b.me], [([b.me], 1)])]
|
||||||
timestamp = gen_timestamp()
|
timestamp = gen_timestamp()
|
||||||
voters = ['Qaaa', 'Qbbb']
|
voters = ['Qaaa', 'Qbbb']
|
||||||
|
|
||||||
@ -136,7 +136,7 @@ class TestBlockModel(object):
|
|||||||
def test_compare_blocks(self, b):
|
def test_compare_blocks(self, b):
|
||||||
from bigchaindb.models import Block, Transaction
|
from bigchaindb.models import Block, Transaction
|
||||||
|
|
||||||
transactions = [Transaction.create([b.me], [b.me])]
|
transactions = [Transaction.create([b.me], [([b.me], 1)])]
|
||||||
|
|
||||||
assert Block() != 'invalid comparison'
|
assert Block() != 'invalid comparison'
|
||||||
assert Block(transactions) == Block(transactions)
|
assert Block(transactions) == Block(transactions)
|
||||||
@ -146,7 +146,7 @@ class TestBlockModel(object):
|
|||||||
from bigchaindb.common.util import gen_timestamp, serialize
|
from bigchaindb.common.util import gen_timestamp, serialize
|
||||||
from bigchaindb.models import Block, Transaction
|
from bigchaindb.models import Block, Transaction
|
||||||
|
|
||||||
transactions = [Transaction.create([b.me], [b.me])]
|
transactions = [Transaction.create([b.me], [([b.me], 1)])]
|
||||||
timestamp = gen_timestamp()
|
timestamp = gen_timestamp()
|
||||||
voters = ['Qaaa', 'Qbbb']
|
voters = ['Qaaa', 'Qbbb']
|
||||||
expected_block = {
|
expected_block = {
|
||||||
@ -168,7 +168,7 @@ class TestBlockModel(object):
|
|||||||
from unittest.mock import Mock
|
from unittest.mock import Mock
|
||||||
from bigchaindb.models import Transaction
|
from bigchaindb.models import Transaction
|
||||||
|
|
||||||
tx = Transaction.create([b.me], [b.me])
|
tx = Transaction.create([b.me], [([b.me], 1)])
|
||||||
block = b.create_block([tx])
|
block = b.create_block([tx])
|
||||||
|
|
||||||
has_previous_vote = Mock()
|
has_previous_vote = Mock()
|
||||||
|
@ -25,7 +25,7 @@ def app(request, node_config):
|
|||||||
restore_config(request, node_config)
|
restore_config(request, node_config)
|
||||||
|
|
||||||
from bigchaindb.web import server
|
from bigchaindb.web import server
|
||||||
app = server.create_app({'debug': True})
|
app = server.create_app(debug=True)
|
||||||
return app
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
5
tests/web/test_info.py
Normal file
5
tests/web/test_info.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
def test_api_endpoint_shows_basic_info(client):
|
||||||
|
from bigchaindb import version
|
||||||
|
res = client.get('/')
|
||||||
|
assert res.json['software'] == 'BigchainDB'
|
||||||
|
assert res.json['version'] == version.__version__
|
@ -25,18 +25,11 @@ def test_get_transaction_returns_404_if_not_found(client):
|
|||||||
assert res.status_code == 404
|
assert res.status_code == 404
|
||||||
|
|
||||||
|
|
||||||
def test_api_endpoint_shows_basic_info(client):
|
|
||||||
from bigchaindb import version
|
|
||||||
res = client.get('/')
|
|
||||||
assert res.json['software'] == 'BigchainDB'
|
|
||||||
assert res.json['version'] == version.__version__
|
|
||||||
|
|
||||||
|
|
||||||
def test_post_create_transaction_endpoint(b, client):
|
def test_post_create_transaction_endpoint(b, client):
|
||||||
from bigchaindb.models import Transaction
|
from bigchaindb.models import Transaction
|
||||||
user_priv, user_pub = crypto.generate_key_pair()
|
user_priv, user_pub = crypto.generate_key_pair()
|
||||||
|
|
||||||
tx = Transaction.create([user_pub], [user_pub])
|
tx = Transaction.create([user_pub], [([user_pub], 1)])
|
||||||
tx = tx.sign([user_priv])
|
tx = tx.sign([user_priv])
|
||||||
|
|
||||||
res = client.post(TX_ENDPOINT, data=json.dumps(tx.to_dict()))
|
res = client.post(TX_ENDPOINT, data=json.dumps(tx.to_dict()))
|
||||||
@ -48,7 +41,7 @@ def test_post_create_transaction_with_invalid_id(b, client):
|
|||||||
from bigchaindb.models import Transaction
|
from bigchaindb.models import Transaction
|
||||||
user_priv, user_pub = crypto.generate_key_pair()
|
user_priv, user_pub = crypto.generate_key_pair()
|
||||||
|
|
||||||
tx = Transaction.create([user_pub], [user_pub])
|
tx = Transaction.create([user_pub], [([user_pub], 1)])
|
||||||
tx = tx.sign([user_priv]).to_dict()
|
tx = tx.sign([user_priv]).to_dict()
|
||||||
tx['id'] = 'invalid id'
|
tx['id'] = 'invalid id'
|
||||||
|
|
||||||
@ -60,7 +53,7 @@ def test_post_create_transaction_with_invalid_signature(b, client):
|
|||||||
from bigchaindb.models import Transaction
|
from bigchaindb.models import Transaction
|
||||||
user_priv, user_pub = crypto.generate_key_pair()
|
user_priv, user_pub = crypto.generate_key_pair()
|
||||||
|
|
||||||
tx = Transaction.create([user_pub], [user_pub])
|
tx = Transaction.create([user_pub], [([user_pub], 1)])
|
||||||
tx = tx.sign([user_priv]).to_dict()
|
tx = tx.sign([user_priv]).to_dict()
|
||||||
tx['transaction']['fulfillments'][0]['fulfillment'] = 'invalid signature'
|
tx['transaction']['fulfillments'][0]['fulfillment'] = 'invalid signature'
|
||||||
|
|
||||||
@ -77,7 +70,8 @@ def test_post_transfer_transaction_endpoint(b, client, user_pk, user_sk):
|
|||||||
|
|
||||||
input_valid = b.get_owned_ids(user_pk).pop()
|
input_valid = b.get_owned_ids(user_pk).pop()
|
||||||
create_tx = b.get_transaction(input_valid.txid)
|
create_tx = b.get_transaction(input_valid.txid)
|
||||||
transfer_tx = Transaction.transfer(create_tx.to_inputs(), [user_pub], create_tx.asset)
|
transfer_tx = Transaction.transfer(create_tx.to_inputs(),
|
||||||
|
[([user_pub], 1)], create_tx.asset)
|
||||||
transfer_tx = transfer_tx.sign([user_sk])
|
transfer_tx = transfer_tx.sign([user_sk])
|
||||||
|
|
||||||
res = client.post(TX_ENDPOINT, data=json.dumps(transfer_tx.to_dict()))
|
res = client.post(TX_ENDPOINT, data=json.dumps(transfer_tx.to_dict()))
|
||||||
@ -94,7 +88,8 @@ def test_post_invalid_transfer_transaction_returns_400(b, client, user_pk, user_
|
|||||||
|
|
||||||
input_valid = b.get_owned_ids(user_pk).pop()
|
input_valid = b.get_owned_ids(user_pk).pop()
|
||||||
create_tx = b.get_transaction(input_valid.txid)
|
create_tx = b.get_transaction(input_valid.txid)
|
||||||
transfer_tx = Transaction.transfer(create_tx.to_inputs(), [user_pub], create_tx.asset)
|
transfer_tx = Transaction.transfer(create_tx.to_inputs(),
|
||||||
|
[([user_pub], 1)], create_tx.asset)
|
||||||
|
|
||||||
res = client.post(TX_ENDPOINT, data=json.dumps(transfer_tx.to_dict()))
|
res = client.post(TX_ENDPOINT, data=json.dumps(transfer_tx.to_dict()))
|
||||||
assert res.status_code == 400
|
assert res.status_code == 400
|
Loading…
x
Reference in New Issue
Block a user