mirror of
https://github.com/bigchaindb/bigchaindb.git
synced 2024-10-13 13:34:05 +00:00
Integrate cryptoconditions version 02
This commit is contained in:
parent
a175f371b0
commit
02fe712c34
@ -7,6 +7,6 @@ pip install --upgrade pip
|
|||||||
if [[ -n ${TOXENV} ]]; then
|
if [[ -n ${TOXENV} ]]; then
|
||||||
pip install --upgrade tox
|
pip install --upgrade tox
|
||||||
else
|
else
|
||||||
pip install -e .[test]
|
pip install .[test]
|
||||||
pip install --upgrade codecov
|
pip install --upgrade codecov
|
||||||
fi
|
fi
|
||||||
|
@ -154,7 +154,7 @@ definitions:
|
|||||||
additionalProperties: true
|
additionalProperties: true
|
||||||
uri:
|
uri:
|
||||||
type: string
|
type: string
|
||||||
pattern: "^cc:([1-9a-f][0-9a-f]{0,3}|0):[1-9a-f][0-9a-f]{0,15}:[a-zA-Z0-9_-]{0,86}:([1-9][0-9]{0,17}|0)$"
|
pattern: "^ni:///sha-256;([a-zA-Z0-9_-]{0,86})?(.+)$"
|
||||||
public_keys:
|
public_keys:
|
||||||
"$ref": "#/definitions/public_keys"
|
"$ref": "#/definitions/public_keys"
|
||||||
description: |
|
description: |
|
||||||
@ -195,7 +195,7 @@ definitions:
|
|||||||
that satisfies the condition of a previous output to prove that the
|
that satisfies the condition of a previous output to prove that the
|
||||||
creator(s) of this transaction have control over the listed asset.
|
creator(s) of this transaction have control over the listed asset.
|
||||||
- type: string
|
- type: string
|
||||||
pattern: "^cf:([1-9a-f][0-9a-f]{0,3}|0):[a-zA-Z0-9_-]*$"
|
pattern: "^[a-zA-Z0-9_-]*$"
|
||||||
fulfills:
|
fulfills:
|
||||||
anyOf:
|
anyOf:
|
||||||
- type: 'object'
|
- type: 'object'
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
|
|
||||||
from cryptoconditions import (Fulfillment, ThresholdSha256Fulfillment,
|
import base58
|
||||||
Ed25519Fulfillment)
|
from cryptoconditions import Fulfillment, ThresholdSha256, Ed25519Sha256
|
||||||
from cryptoconditions.exceptions import ParsingError
|
from cryptoconditions.exceptions import (
|
||||||
|
ParsingError, ASN1DecodeError, ASN1EncodeError)
|
||||||
|
|
||||||
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)
|
AmountError, AssetIdMismatch)
|
||||||
from bigchaindb.common.utils import serialize, gen_timestamp
|
from bigchaindb.common.utils import serialize
|
||||||
import bigchaindb.version
|
import bigchaindb.version
|
||||||
|
|
||||||
|
|
||||||
@ -65,7 +66,7 @@ class Input(object):
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
fulfillment = self.fulfillment.serialize_uri()
|
fulfillment = self.fulfillment.serialize_uri()
|
||||||
except (TypeError, AttributeError):
|
except (TypeError, AttributeError, ASN1EncodeError):
|
||||||
# NOTE: When a non-signed transaction is casted to a dict,
|
# NOTE: When a non-signed transaction is casted to a dict,
|
||||||
# `self.inputs` value is lost, as in the node's
|
# `self.inputs` value is lost, as in the node's
|
||||||
# transaction model that is saved to the database, does not
|
# transaction model that is saved to the database, does not
|
||||||
@ -114,15 +115,18 @@ class Input(object):
|
|||||||
Raises:
|
Raises:
|
||||||
InvalidSignature: If an Input's URI couldn't be parsed.
|
InvalidSignature: If an Input's URI couldn't be parsed.
|
||||||
"""
|
"""
|
||||||
try:
|
fulfillment = data['fulfillment']
|
||||||
fulfillment = Fulfillment.from_uri(data['fulfillment'])
|
if not isinstance(fulfillment, Fulfillment):
|
||||||
except ValueError:
|
try:
|
||||||
# TODO FOR CC: Throw an `InvalidSignature` error in this case.
|
fulfillment = Fulfillment.from_uri(data['fulfillment'])
|
||||||
raise InvalidSignature("Fulfillment URI couldn't been parsed")
|
except ASN1DecodeError:
|
||||||
except TypeError:
|
# TODO Remove as it is legacy code, and simply fall back on
|
||||||
# NOTE: See comment about this special case in
|
# ASN1DecodeError
|
||||||
# `Input.to_dict`
|
raise InvalidSignature("Fulfillment URI couldn't been parsed")
|
||||||
fulfillment = Fulfillment.from_dict(data['fulfillment'])
|
except TypeError:
|
||||||
|
# NOTE: See comment about this special case in
|
||||||
|
# `Input.to_dict`
|
||||||
|
fulfillment = Fulfillment.from_dict(data['fulfillment'])
|
||||||
fulfills = TransactionLink.from_dict(data['fulfills'])
|
fulfills = TransactionLink.from_dict(data['fulfills'])
|
||||||
return cls(fulfillment, data['owners_before'], fulfills)
|
return cls(fulfillment, data['owners_before'], fulfills)
|
||||||
|
|
||||||
@ -310,13 +314,14 @@ class Output(object):
|
|||||||
raise ValueError('`public_keys` needs to contain at least one'
|
raise ValueError('`public_keys` needs to contain at least one'
|
||||||
'owner')
|
'owner')
|
||||||
elif len(public_keys) == 1 and not isinstance(public_keys[0], list):
|
elif len(public_keys) == 1 and not isinstance(public_keys[0], list):
|
||||||
try:
|
if isinstance(public_keys[0], Fulfillment):
|
||||||
ffill = Ed25519Fulfillment(public_key=public_keys[0])
|
|
||||||
except TypeError:
|
|
||||||
ffill = public_keys[0]
|
ffill = public_keys[0]
|
||||||
|
else:
|
||||||
|
ffill = Ed25519Sha256(
|
||||||
|
public_key=base58.b58decode(public_keys[0]))
|
||||||
return cls(ffill, public_keys, amount=amount)
|
return cls(ffill, public_keys, amount=amount)
|
||||||
else:
|
else:
|
||||||
initial_cond = ThresholdSha256Fulfillment(threshold=threshold)
|
initial_cond = ThresholdSha256(threshold=threshold)
|
||||||
threshold_cond = reduce(cls._gen_condition, public_keys,
|
threshold_cond = reduce(cls._gen_condition, public_keys,
|
||||||
initial_cond)
|
initial_cond)
|
||||||
return cls(threshold_cond, public_keys, amount=amount)
|
return cls(threshold_cond, public_keys, amount=amount)
|
||||||
@ -331,13 +336,13 @@ class Output(object):
|
|||||||
:meth:`~.Output.generate`.
|
:meth:`~.Output.generate`.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
initial (:class:`cryptoconditions.ThresholdSha256Fulfillment`):
|
initial (:class:`cryptoconditions.ThresholdSha256`):
|
||||||
A Condition representing the overall root.
|
A Condition representing the overall root.
|
||||||
new_public_keys (:obj:`list` of :obj:`str`|str): A list of new
|
new_public_keys (:obj:`list` of :obj:`str`|str): A list of new
|
||||||
owners or a single new owner.
|
owners or a single new owner.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
:class:`cryptoconditions.ThresholdSha256Fulfillment`:
|
:class:`cryptoconditions.ThresholdSha256`:
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
threshold = len(new_public_keys)
|
threshold = len(new_public_keys)
|
||||||
@ -345,7 +350,7 @@ class Output(object):
|
|||||||
threshold = None
|
threshold = None
|
||||||
|
|
||||||
if isinstance(new_public_keys, list) and len(new_public_keys) > 1:
|
if isinstance(new_public_keys, list) and len(new_public_keys) > 1:
|
||||||
ffill = ThresholdSha256Fulfillment(threshold=threshold)
|
ffill = ThresholdSha256(threshold=threshold)
|
||||||
reduce(cls._gen_condition, new_public_keys, ffill)
|
reduce(cls._gen_condition, new_public_keys, ffill)
|
||||||
elif isinstance(new_public_keys, list) and len(new_public_keys) <= 1:
|
elif isinstance(new_public_keys, list) and len(new_public_keys) <= 1:
|
||||||
raise ValueError('Sublist cannot contain single owner')
|
raise ValueError('Sublist cannot contain single owner')
|
||||||
@ -354,16 +359,17 @@ class Output(object):
|
|||||||
new_public_keys = new_public_keys.pop()
|
new_public_keys = new_public_keys.pop()
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
try:
|
# NOTE: Instead of submitting base58 encoded addresses, a user
|
||||||
ffill = Ed25519Fulfillment(public_key=new_public_keys)
|
# of this class can also submit fully instantiated
|
||||||
except TypeError:
|
# Cryptoconditions. In the case of casting
|
||||||
# NOTE: Instead of submitting base58 encoded addresses, a user
|
# `new_public_keys` to a Ed25519Fulfillment with the
|
||||||
# of this class can also submit fully instantiated
|
# result of a `TypeError`, we're assuming that
|
||||||
# Cryptoconditions. In the case of casting
|
# `new_public_keys` is a Cryptocondition then.
|
||||||
# `new_public_keys` to a Ed25519Fulfillment with the
|
if isinstance(new_public_keys, Fulfillment):
|
||||||
# result of a `TypeError`, we're assuming that
|
|
||||||
# `new_public_keys` is a Cryptocondition then.
|
|
||||||
ffill = new_public_keys
|
ffill = new_public_keys
|
||||||
|
else:
|
||||||
|
ffill = Ed25519Sha256(
|
||||||
|
public_key=base58.b58decode(new_public_keys))
|
||||||
initial.add_subfulfillment(ffill)
|
initial.add_subfulfillment(ffill)
|
||||||
return initial
|
return initial
|
||||||
|
|
||||||
@ -661,7 +667,7 @@ class Transaction(object):
|
|||||||
This method works only for the following Cryptoconditions
|
This method works only for the following Cryptoconditions
|
||||||
currently:
|
currently:
|
||||||
- Ed25519Fulfillment
|
- Ed25519Fulfillment
|
||||||
- ThresholdSha256Fulfillment
|
- ThresholdSha256
|
||||||
Furthermore, note that all keys required to fully sign the
|
Furthermore, note that all keys required to fully sign the
|
||||||
Transaction have to be passed to this method. A subset of all
|
Transaction have to be passed to this method. A subset of all
|
||||||
will cause this method to fail.
|
will cause this method to fail.
|
||||||
@ -712,7 +718,7 @@ class Transaction(object):
|
|||||||
This method works only for the following Cryptoconditions
|
This method works only for the following Cryptoconditions
|
||||||
currently:
|
currently:
|
||||||
- Ed25519Fulfillment
|
- Ed25519Fulfillment
|
||||||
- ThresholdSha256Fulfillment.
|
- ThresholdSha256.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
input_ (:class:`~bigchaindb.common.transaction.
|
input_ (:class:`~bigchaindb.common.transaction.
|
||||||
@ -720,10 +726,10 @@ class Transaction(object):
|
|||||||
message (str): The message to be signed
|
message (str): The message to be signed
|
||||||
key_pairs (dict): The keys to sign the Transaction with.
|
key_pairs (dict): The keys to sign the Transaction with.
|
||||||
"""
|
"""
|
||||||
if isinstance(input_.fulfillment, Ed25519Fulfillment):
|
if isinstance(input_.fulfillment, Ed25519Sha256):
|
||||||
return cls._sign_simple_signature_fulfillment(input_, message,
|
return cls._sign_simple_signature_fulfillment(input_, message,
|
||||||
key_pairs)
|
key_pairs)
|
||||||
elif isinstance(input_.fulfillment, ThresholdSha256Fulfillment):
|
elif isinstance(input_.fulfillment, ThresholdSha256):
|
||||||
return cls._sign_threshold_signature_fulfillment(input_, message,
|
return cls._sign_threshold_signature_fulfillment(input_, message,
|
||||||
key_pairs)
|
key_pairs)
|
||||||
else:
|
else:
|
||||||
@ -749,7 +755,10 @@ class Transaction(object):
|
|||||||
try:
|
try:
|
||||||
# cryptoconditions makes no assumptions of the encoding of the
|
# cryptoconditions makes no assumptions of the encoding of the
|
||||||
# message to sign or verify. It only accepts bytestrings
|
# message to sign or verify. It only accepts bytestrings
|
||||||
input_.fulfillment.sign(message.encode(), key_pairs[public_key])
|
input_.fulfillment.sign(
|
||||||
|
message.encode(),
|
||||||
|
base58.b58decode(key_pairs[public_key].encode()),
|
||||||
|
)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise KeypairMismatchException('Public key {} is not a pair to '
|
raise KeypairMismatchException('Public key {} is not a pair to '
|
||||||
'any of the private keys'
|
'any of the private keys'
|
||||||
@ -758,7 +767,7 @@ class Transaction(object):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _sign_threshold_signature_fulfillment(cls, input_, message, key_pairs):
|
def _sign_threshold_signature_fulfillment(cls, input_, message, key_pairs):
|
||||||
"""Signs a ThresholdSha256Fulfillment.
|
"""Signs a ThresholdSha256.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
input_ (:class:`~bigchaindb.common.transaction.
|
input_ (:class:`~bigchaindb.common.transaction.
|
||||||
@ -778,7 +787,8 @@ class Transaction(object):
|
|||||||
# TODO FOR CC: `get_subcondition` is singular. One would not
|
# TODO FOR CC: `get_subcondition` is singular. One would not
|
||||||
# expect to get a list back.
|
# expect to get a list back.
|
||||||
ccffill = input_.fulfillment
|
ccffill = input_.fulfillment
|
||||||
subffills = ccffill.get_subcondition_from_vk(owner_before)
|
subffills = ccffill.get_subcondition_from_vk(
|
||||||
|
base58.b58decode(owner_before))
|
||||||
if not subffills:
|
if not subffills:
|
||||||
raise KeypairMismatchException('Public key {} cannot be found '
|
raise KeypairMismatchException('Public key {} cannot be found '
|
||||||
'in the fulfillment'
|
'in the fulfillment'
|
||||||
@ -793,7 +803,7 @@ class Transaction(object):
|
|||||||
# cryptoconditions makes no assumptions of the encoding of the
|
# cryptoconditions makes no assumptions of the encoding of the
|
||||||
# message to sign or verify. It only accepts bytestrings
|
# message to sign or verify. It only accepts bytestrings
|
||||||
for subffill in subffills:
|
for subffill in subffills:
|
||||||
subffill.sign(message.encode(), private_key)
|
subffill.sign(message.encode(), base58.b58decode(private_key.encode()))
|
||||||
return input_
|
return input_
|
||||||
|
|
||||||
def inputs_valid(self, outputs=None):
|
def inputs_valid(self, outputs=None):
|
||||||
@ -882,7 +892,8 @@ class Transaction(object):
|
|||||||
ccffill = input_.fulfillment
|
ccffill = input_.fulfillment
|
||||||
try:
|
try:
|
||||||
parsed_ffill = Fulfillment.from_uri(ccffill.serialize_uri())
|
parsed_ffill = Fulfillment.from_uri(ccffill.serialize_uri())
|
||||||
except (TypeError, ValueError, ParsingError):
|
except (TypeError, ValueError,
|
||||||
|
ParsingError, ASN1DecodeError, ASN1EncodeError):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if operation in (Transaction.CREATE, Transaction.GENESIS):
|
if operation in (Transaction.CREATE, Transaction.GENESIS):
|
||||||
@ -897,8 +908,7 @@ class Transaction(object):
|
|||||||
|
|
||||||
# cryptoconditions makes no assumptions of the encoding of the
|
# cryptoconditions makes no assumptions of the encoding of the
|
||||||
# message to sign or verify. It only accepts bytestrings
|
# message to sign or verify. It only accepts bytestrings
|
||||||
ffill_valid = parsed_ffill.validate(message=tx_serialized.encode(),
|
ffill_valid = parsed_ffill.validate(message=tx_serialized.encode())
|
||||||
now=gen_timestamp())
|
|
||||||
return output_valid and ffill_valid
|
return output_valid and ffill_valid
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
@ -940,7 +950,7 @@ class Transaction(object):
|
|||||||
tx_dict = deepcopy(tx_dict)
|
tx_dict = deepcopy(tx_dict)
|
||||||
for input_ in tx_dict['inputs']:
|
for input_ in tx_dict['inputs']:
|
||||||
# NOTE: Not all Cryptoconditions return a `signature` key (e.g.
|
# NOTE: Not all Cryptoconditions return a `signature` key (e.g.
|
||||||
# ThresholdSha256Fulfillment), so setting it to `None` in any
|
# ThresholdSha256), so setting it to `None` in any
|
||||||
# case could yield incorrect signatures. This is why we only
|
# case could yield incorrect signatures. This is why we only
|
||||||
# set it to `None` if it's set in the dict.
|
# set it to `None` if it's set in the dict.
|
||||||
input_['fulfillment'] = None
|
input_['fulfillment'] = None
|
||||||
|
@ -22,6 +22,7 @@ services:
|
|||||||
- ./setup.cfg:/usr/src/app/setup.cfg
|
- ./setup.cfg:/usr/src/app/setup.cfg
|
||||||
- ./pytest.ini:/usr/src/app/pytest.ini
|
- ./pytest.ini:/usr/src/app/pytest.ini
|
||||||
- ./tox.ini:/usr/src/app/tox.ini
|
- ./tox.ini:/usr/src/app/tox.ini
|
||||||
|
- ../cryptoconditions:/usr/src/app/cryptoconditions
|
||||||
environment:
|
environment:
|
||||||
BIGCHAINDB_DATABASE_BACKEND: mongodb
|
BIGCHAINDB_DATABASE_BACKEND: mongodb
|
||||||
BIGCHAINDB_DATABASE_HOST: mdb
|
BIGCHAINDB_DATABASE_HOST: mdb
|
||||||
|
4
setup.py
4
setup.py
@ -67,7 +67,7 @@ install_requires = [
|
|||||||
'rethinkdb~=2.3', # i.e. a version between 2.3 and 3.0
|
'rethinkdb~=2.3', # i.e. a version between 2.3 and 3.0
|
||||||
'pymongo~=3.4',
|
'pymongo~=3.4',
|
||||||
'pysha3~=1.0.2',
|
'pysha3~=1.0.2',
|
||||||
'cryptoconditions>=0.5.0',
|
'cryptoconditions>=0.6.0.dev',
|
||||||
'python-rapidjson==0.0.11',
|
'python-rapidjson==0.0.11',
|
||||||
'logstats>=0.2.1',
|
'logstats>=0.2.1',
|
||||||
'flask>=0.10.1',
|
'flask>=0.10.1',
|
||||||
@ -126,7 +126,7 @@ setup(
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
install_requires=install_requires,
|
install_requires=install_requires,
|
||||||
setup_requires=['pytest-runner'],
|
setup_requires=['pytest-runner', 'cryptoconditions'],
|
||||||
tests_require=tests_require,
|
tests_require=tests_require,
|
||||||
extras_require={
|
extras_require={
|
||||||
'test': tests_require,
|
'test': tests_require,
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from base58 import b58decode
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@ -11,8 +12,13 @@ USER3_PRIVATE_KEY = '4rNQFzWQbVwuTiDVxwuFMvLG5zd8AhrQKCtVovBvcYsB'
|
|||||||
USER3_PUBLIC_KEY = 'Gbrg7JtxdjedQRmr81ZZbh1BozS7fBW88ZyxNDy7WLNC'
|
USER3_PUBLIC_KEY = 'Gbrg7JtxdjedQRmr81ZZbh1BozS7fBW88ZyxNDy7WLNC'
|
||||||
|
|
||||||
|
|
||||||
CC_FULFILLMENT_URI = 'cf:0:'
|
CC_FULFILLMENT_URI = (
|
||||||
CC_CONDITION_URI = 'cc:0:3:47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU:0'
|
'pGSAINdamAGCsQq31Uv-08lkBzoO4XLz2qYjJa8CGmj3B1EagUDlVkMAw2CscpCG4syAboKKh'
|
||||||
|
'Id_Hrjl2XTYc-BlIkkBVV-4ghWQozusxh45cBz5tGvSW_XwWVu-JGVRQUOOehAL'
|
||||||
|
)
|
||||||
|
CC_CONDITION_URI = ('ni:///sha-256;'
|
||||||
|
'eZI5q6j8T_fqv7xMROaei9_tmTMk4S7WR5Kr4onPHV8'
|
||||||
|
'?fpt=ed25519-sha-256&cost=131072')
|
||||||
|
|
||||||
ASSET_DEFINITION = {
|
ASSET_DEFINITION = {
|
||||||
'data': {
|
'data': {
|
||||||
@ -71,25 +77,25 @@ def cond_uri():
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def user_Ed25519(user_pub):
|
def user_Ed25519(user_pub):
|
||||||
from cryptoconditions import Ed25519Fulfillment
|
from cryptoconditions import Ed25519Sha256
|
||||||
return Ed25519Fulfillment(public_key=user_pub)
|
return Ed25519Sha256(public_key=b58decode(user_pub))
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def user_user2_threshold(user_pub, user2_pub):
|
def user_user2_threshold(user_pub, user2_pub):
|
||||||
from cryptoconditions import (ThresholdSha256Fulfillment,
|
from cryptoconditions import ThresholdSha256, Ed25519Sha256
|
||||||
Ed25519Fulfillment)
|
|
||||||
user_pub_keys = [user_pub, user2_pub]
|
user_pub_keys = [user_pub, user2_pub]
|
||||||
threshold = ThresholdSha256Fulfillment(threshold=len(user_pub_keys))
|
threshold = ThresholdSha256(threshold=len(user_pub_keys))
|
||||||
for user_pub in user_pub_keys:
|
for user_pub in user_pub_keys:
|
||||||
threshold.add_subfulfillment(Ed25519Fulfillment(public_key=user_pub))
|
threshold.add_subfulfillment(
|
||||||
|
Ed25519Sha256(public_key=b58decode(user_pub)))
|
||||||
return threshold
|
return threshold
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def user2_Ed25519(user2_pub):
|
def user2_Ed25519(user2_pub):
|
||||||
from cryptoconditions import Ed25519Fulfillment
|
from cryptoconditions import Ed25519Sha256
|
||||||
return Ed25519Fulfillment(public_key=user2_pub)
|
return Ed25519Sha256(public_key=b58decode(user2_pub))
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
These are tests of the API of the Transaction class and associated classes.
|
These are tests of the API of the Transaction class and associated classes.
|
||||||
Tests for transaction validation are separate.
|
Tests for transaction validation are separate.
|
||||||
"""
|
"""
|
||||||
|
from copy import deepcopy
|
||||||
|
|
||||||
|
from base58 import b58decode
|
||||||
from pytest import raises
|
from pytest import raises
|
||||||
|
|
||||||
|
|
||||||
@ -110,10 +112,10 @@ def test_output_deserialization(user_Ed25519, user_pub):
|
|||||||
|
|
||||||
def test_output_hashlock_serialization():
|
def test_output_hashlock_serialization():
|
||||||
from bigchaindb.common.transaction import Output
|
from bigchaindb.common.transaction import Output
|
||||||
from cryptoconditions import PreimageSha256Fulfillment
|
from cryptoconditions import PreimageSha256
|
||||||
|
|
||||||
secret = b'wow much secret'
|
secret = b'wow much secret'
|
||||||
hashlock = PreimageSha256Fulfillment(preimage=secret).condition_uri
|
hashlock = PreimageSha256(preimage=secret).condition_uri
|
||||||
|
|
||||||
expected = {
|
expected = {
|
||||||
'condition': {
|
'condition': {
|
||||||
@ -129,10 +131,10 @@ def test_output_hashlock_serialization():
|
|||||||
|
|
||||||
def test_output_hashlock_deserialization():
|
def test_output_hashlock_deserialization():
|
||||||
from bigchaindb.common.transaction import Output
|
from bigchaindb.common.transaction import Output
|
||||||
from cryptoconditions import PreimageSha256Fulfillment
|
from cryptoconditions import PreimageSha256
|
||||||
|
|
||||||
secret = b'wow much secret'
|
secret = b'wow much secret'
|
||||||
hashlock = PreimageSha256Fulfillment(preimage=secret).condition_uri
|
hashlock = PreimageSha256(preimage=secret).condition_uri
|
||||||
expected = Output(hashlock, amount=1)
|
expected = Output(hashlock, amount=1)
|
||||||
|
|
||||||
cond = {
|
cond = {
|
||||||
@ -161,15 +163,15 @@ def test_invalid_output_initialization(cond_uri, user_pub):
|
|||||||
|
|
||||||
def test_generate_output_split_half_recursive(user_pub, user2_pub, user3_pub):
|
def test_generate_output_split_half_recursive(user_pub, user2_pub, user3_pub):
|
||||||
from bigchaindb.common.transaction import Output
|
from bigchaindb.common.transaction import Output
|
||||||
from cryptoconditions import Ed25519Fulfillment, ThresholdSha256Fulfillment
|
from cryptoconditions import Ed25519Sha256, ThresholdSha256
|
||||||
|
|
||||||
expected_simple1 = Ed25519Fulfillment(public_key=user_pub)
|
expected_simple1 = Ed25519Sha256(public_key=b58decode(user_pub))
|
||||||
expected_simple2 = Ed25519Fulfillment(public_key=user2_pub)
|
expected_simple2 = Ed25519Sha256(public_key=b58decode(user2_pub))
|
||||||
expected_simple3 = Ed25519Fulfillment(public_key=user3_pub)
|
expected_simple3 = Ed25519Sha256(public_key=b58decode(user3_pub))
|
||||||
|
|
||||||
expected = ThresholdSha256Fulfillment(threshold=2)
|
expected = ThresholdSha256(threshold=2)
|
||||||
expected.add_subfulfillment(expected_simple1)
|
expected.add_subfulfillment(expected_simple1)
|
||||||
expected_threshold = ThresholdSha256Fulfillment(threshold=2)
|
expected_threshold = ThresholdSha256(threshold=2)
|
||||||
expected_threshold.add_subfulfillment(expected_simple2)
|
expected_threshold.add_subfulfillment(expected_simple2)
|
||||||
expected_threshold.add_subfulfillment(expected_simple3)
|
expected_threshold.add_subfulfillment(expected_simple3)
|
||||||
expected.add_subfulfillment(expected_threshold)
|
expected.add_subfulfillment(expected_threshold)
|
||||||
@ -181,14 +183,14 @@ def test_generate_output_split_half_recursive(user_pub, user2_pub, user3_pub):
|
|||||||
def test_generate_outputs_split_half_single_owner(user_pub,
|
def test_generate_outputs_split_half_single_owner(user_pub,
|
||||||
user2_pub, user3_pub):
|
user2_pub, user3_pub):
|
||||||
from bigchaindb.common.transaction import Output
|
from bigchaindb.common.transaction import Output
|
||||||
from cryptoconditions import Ed25519Fulfillment, ThresholdSha256Fulfillment
|
from cryptoconditions import Ed25519Sha256, ThresholdSha256
|
||||||
|
|
||||||
expected_simple1 = Ed25519Fulfillment(public_key=user_pub)
|
expected_simple1 = Ed25519Sha256(public_key=b58decode(user_pub))
|
||||||
expected_simple2 = Ed25519Fulfillment(public_key=user2_pub)
|
expected_simple2 = Ed25519Sha256(public_key=b58decode(user2_pub))
|
||||||
expected_simple3 = Ed25519Fulfillment(public_key=user3_pub)
|
expected_simple3 = Ed25519Sha256(public_key=b58decode(user3_pub))
|
||||||
|
|
||||||
expected = ThresholdSha256Fulfillment(threshold=2)
|
expected = ThresholdSha256(threshold=2)
|
||||||
expected_threshold = ThresholdSha256Fulfillment(threshold=2)
|
expected_threshold = ThresholdSha256(threshold=2)
|
||||||
expected_threshold.add_subfulfillment(expected_simple2)
|
expected_threshold.add_subfulfillment(expected_simple2)
|
||||||
expected_threshold.add_subfulfillment(expected_simple3)
|
expected_threshold.add_subfulfillment(expected_simple3)
|
||||||
expected.add_subfulfillment(expected_threshold)
|
expected.add_subfulfillment(expected_threshold)
|
||||||
@ -200,13 +202,13 @@ def test_generate_outputs_split_half_single_owner(user_pub,
|
|||||||
|
|
||||||
def test_generate_outputs_flat_ownage(user_pub, user2_pub, user3_pub):
|
def test_generate_outputs_flat_ownage(user_pub, user2_pub, user3_pub):
|
||||||
from bigchaindb.common.transaction import Output
|
from bigchaindb.common.transaction import Output
|
||||||
from cryptoconditions import Ed25519Fulfillment, ThresholdSha256Fulfillment
|
from cryptoconditions import Ed25519Sha256, ThresholdSha256
|
||||||
|
|
||||||
expected_simple1 = Ed25519Fulfillment(public_key=user_pub)
|
expected_simple1 = Ed25519Sha256(public_key=b58decode(user_pub))
|
||||||
expected_simple2 = Ed25519Fulfillment(public_key=user2_pub)
|
expected_simple2 = Ed25519Sha256(public_key=b58decode(user2_pub))
|
||||||
expected_simple3 = Ed25519Fulfillment(public_key=user3_pub)
|
expected_simple3 = Ed25519Sha256(public_key=b58decode(user3_pub))
|
||||||
|
|
||||||
expected = ThresholdSha256Fulfillment(threshold=3)
|
expected = ThresholdSha256(threshold=3)
|
||||||
expected.add_subfulfillment(expected_simple1)
|
expected.add_subfulfillment(expected_simple1)
|
||||||
expected.add_subfulfillment(expected_simple2)
|
expected.add_subfulfillment(expected_simple2)
|
||||||
expected.add_subfulfillment(expected_simple3)
|
expected.add_subfulfillment(expected_simple3)
|
||||||
@ -217,9 +219,9 @@ def test_generate_outputs_flat_ownage(user_pub, user2_pub, user3_pub):
|
|||||||
|
|
||||||
def test_generate_output_single_owner(user_pub):
|
def test_generate_output_single_owner(user_pub):
|
||||||
from bigchaindb.common.transaction import Output
|
from bigchaindb.common.transaction import Output
|
||||||
from cryptoconditions import Ed25519Fulfillment
|
from cryptoconditions import Ed25519Sha256
|
||||||
|
|
||||||
expected = Ed25519Fulfillment(public_key=user_pub)
|
expected = Ed25519Sha256(public_key=b58decode(user_pub))
|
||||||
cond = Output.generate([user_pub], 1)
|
cond = Output.generate([user_pub], 1)
|
||||||
|
|
||||||
assert cond.fulfillment.to_dict() == expected.to_dict()
|
assert cond.fulfillment.to_dict() == expected.to_dict()
|
||||||
@ -227,9 +229,9 @@ def test_generate_output_single_owner(user_pub):
|
|||||||
|
|
||||||
def test_generate_output_single_owner_with_output(user_pub):
|
def test_generate_output_single_owner_with_output(user_pub):
|
||||||
from bigchaindb.common.transaction import Output
|
from bigchaindb.common.transaction import Output
|
||||||
from cryptoconditions import Ed25519Fulfillment
|
from cryptoconditions import Ed25519Sha256
|
||||||
|
|
||||||
expected = Ed25519Fulfillment(public_key=user_pub)
|
expected = Ed25519Sha256(public_key=b58decode(user_pub))
|
||||||
cond = Output.generate([expected], 1)
|
cond = Output.generate([expected], 1)
|
||||||
|
|
||||||
assert cond.fulfillment.to_dict() == expected.to_dict()
|
assert cond.fulfillment.to_dict() == expected.to_dict()
|
||||||
@ -489,15 +491,13 @@ def test_sign_with_invalid_parameters(utx, user_priv):
|
|||||||
|
|
||||||
def test_validate_tx_simple_create_signature(user_input, user_output, user_priv,
|
def test_validate_tx_simple_create_signature(user_input, user_output, user_priv,
|
||||||
asset_definition):
|
asset_definition):
|
||||||
from copy import deepcopy
|
|
||||||
from bigchaindb.common.crypto import PrivateKey
|
|
||||||
from bigchaindb.common.transaction import Transaction
|
from bigchaindb.common.transaction import Transaction
|
||||||
from .utils import validate_transaction_model
|
from .utils import validate_transaction_model
|
||||||
|
|
||||||
tx = Transaction(Transaction.CREATE, asset_definition, [user_input], [user_output])
|
tx = Transaction(Transaction.CREATE, asset_definition, [user_input], [user_output])
|
||||||
expected = deepcopy(user_output)
|
expected = deepcopy(user_output)
|
||||||
message = str(tx).encode()
|
message = str(tx).encode()
|
||||||
expected.fulfillment.sign(message, PrivateKey(user_priv))
|
expected.fulfillment.sign(message, b58decode(user_priv))
|
||||||
tx.sign([user_priv])
|
tx.sign([user_priv])
|
||||||
|
|
||||||
assert tx.inputs[0].to_dict()['fulfillment'] == \
|
assert tx.inputs[0].to_dict()['fulfillment'] == \
|
||||||
@ -527,7 +527,7 @@ def test_sign_threshold_with_invalid_params(utx, user_user2_threshold_input,
|
|||||||
'somemessage',
|
'somemessage',
|
||||||
{user3_pub: user3_priv})
|
{user3_pub: user3_priv})
|
||||||
with raises(KeypairMismatchException):
|
with raises(KeypairMismatchException):
|
||||||
user_user2_threshold_input.owners_before = ['somewrongvalue']
|
user_user2_threshold_input.owners_before = [58 * 'a']
|
||||||
utx._sign_threshold_signature_fulfillment(user_user2_threshold_input,
|
utx._sign_threshold_signature_fulfillment(user_user2_threshold_input,
|
||||||
'somemessage',
|
'somemessage',
|
||||||
None)
|
None)
|
||||||
@ -551,9 +551,6 @@ def test_validate_tx_threshold_create_signature(user_user2_threshold_input,
|
|||||||
user_priv,
|
user_priv,
|
||||||
user2_priv,
|
user2_priv,
|
||||||
asset_definition):
|
asset_definition):
|
||||||
from copy import deepcopy
|
|
||||||
|
|
||||||
from bigchaindb.common.crypto import PrivateKey
|
|
||||||
from bigchaindb.common.transaction import Transaction
|
from bigchaindb.common.transaction import Transaction
|
||||||
from .utils import validate_transaction_model
|
from .utils import validate_transaction_model
|
||||||
|
|
||||||
@ -562,10 +559,10 @@ def test_validate_tx_threshold_create_signature(user_user2_threshold_input,
|
|||||||
[user_user2_threshold_output])
|
[user_user2_threshold_output])
|
||||||
message = str(tx).encode()
|
message = str(tx).encode()
|
||||||
expected = deepcopy(user_user2_threshold_output)
|
expected = deepcopy(user_user2_threshold_output)
|
||||||
expected.fulfillment.subconditions[0]['body'].sign(message,
|
expected.fulfillment.subconditions[0]['body'].sign(
|
||||||
PrivateKey(user_priv))
|
message, b58decode(user_priv))
|
||||||
expected.fulfillment.subconditions[1]['body'].sign(message,
|
expected.fulfillment.subconditions[1]['body'].sign(
|
||||||
PrivateKey(user2_priv))
|
message, b58decode(user2_priv))
|
||||||
tx.sign([user_priv, user2_priv])
|
tx.sign([user_priv, user2_priv])
|
||||||
|
|
||||||
assert tx.inputs[0].to_dict()['fulfillment'] == \
|
assert tx.inputs[0].to_dict()['fulfillment'] == \
|
||||||
@ -577,14 +574,14 @@ def test_validate_tx_threshold_create_signature(user_user2_threshold_input,
|
|||||||
|
|
||||||
def test_validate_tx_threshold_duplicated_pk(user_pub, user_priv,
|
def test_validate_tx_threshold_duplicated_pk(user_pub, user_priv,
|
||||||
asset_definition):
|
asset_definition):
|
||||||
from copy import deepcopy
|
from cryptoconditions import Ed25519Sha256, ThresholdSha256
|
||||||
from cryptoconditions import Ed25519Fulfillment, ThresholdSha256Fulfillment
|
|
||||||
from bigchaindb.common.transaction import Input, Output, Transaction
|
from bigchaindb.common.transaction import Input, Output, Transaction
|
||||||
from bigchaindb.common.crypto import PrivateKey
|
|
||||||
|
|
||||||
threshold = ThresholdSha256Fulfillment(threshold=2)
|
threshold = ThresholdSha256(threshold=2)
|
||||||
threshold.add_subfulfillment(Ed25519Fulfillment(public_key=user_pub))
|
threshold.add_subfulfillment(
|
||||||
threshold.add_subfulfillment(Ed25519Fulfillment(public_key=user_pub))
|
Ed25519Sha256(public_key=b58decode(user_pub)))
|
||||||
|
threshold.add_subfulfillment(
|
||||||
|
Ed25519Sha256(public_key=b58decode(user_pub)))
|
||||||
|
|
||||||
threshold_input = Input(threshold, [user_pub, user_pub])
|
threshold_input = Input(threshold, [user_pub, user_pub])
|
||||||
threshold_output = Output(threshold, [user_pub, user_pub])
|
threshold_output = Output(threshold, [user_pub, user_pub])
|
||||||
@ -592,10 +589,10 @@ def test_validate_tx_threshold_duplicated_pk(user_pub, user_priv,
|
|||||||
tx = Transaction(Transaction.CREATE, asset_definition,
|
tx = Transaction(Transaction.CREATE, asset_definition,
|
||||||
[threshold_input], [threshold_output])
|
[threshold_input], [threshold_output])
|
||||||
expected = deepcopy(threshold_input)
|
expected = deepcopy(threshold_input)
|
||||||
expected.fulfillment.subconditions[0]['body'].sign(str(tx).encode(),
|
expected.fulfillment.subconditions[0]['body'].sign(
|
||||||
PrivateKey(user_priv))
|
str(tx).encode(), b58decode(user_priv))
|
||||||
expected.fulfillment.subconditions[1]['body'].sign(str(tx).encode(),
|
expected.fulfillment.subconditions[1]['body'].sign(
|
||||||
PrivateKey(user_priv))
|
str(tx).encode(), b58decode(user_priv))
|
||||||
|
|
||||||
tx.sign([user_priv, user_priv])
|
tx.sign([user_priv, user_priv])
|
||||||
|
|
||||||
@ -616,10 +613,9 @@ def test_multiple_input_validation_of_transfer_tx(user_input, user_output,
|
|||||||
user2_priv, user3_pub,
|
user2_priv, user3_pub,
|
||||||
user3_priv,
|
user3_priv,
|
||||||
asset_definition):
|
asset_definition):
|
||||||
from copy import deepcopy
|
|
||||||
from bigchaindb.common.transaction import (Transaction, TransactionLink,
|
from bigchaindb.common.transaction import (Transaction, TransactionLink,
|
||||||
Input, Output)
|
Input, Output)
|
||||||
from cryptoconditions import Ed25519Fulfillment
|
from cryptoconditions import Ed25519Sha256
|
||||||
from .utils import validate_transaction_model
|
from .utils import validate_transaction_model
|
||||||
|
|
||||||
tx = Transaction(Transaction.CREATE, asset_definition, [user_input],
|
tx = Transaction(Transaction.CREATE, asset_definition, [user_input],
|
||||||
@ -629,8 +625,10 @@ def test_multiple_input_validation_of_transfer_tx(user_input, user_output,
|
|||||||
inputs = [Input(cond.fulfillment, cond.public_keys,
|
inputs = [Input(cond.fulfillment, cond.public_keys,
|
||||||
TransactionLink(tx.id, index))
|
TransactionLink(tx.id, index))
|
||||||
for index, cond in enumerate(tx.outputs)]
|
for index, cond in enumerate(tx.outputs)]
|
||||||
outputs = [Output(Ed25519Fulfillment(public_key=user3_pub), [user3_pub]),
|
outputs = [Output(Ed25519Sha256(public_key=b58decode(user3_pub)),
|
||||||
Output(Ed25519Fulfillment(public_key=user3_pub), [user3_pub])]
|
[user3_pub]),
|
||||||
|
Output(Ed25519Sha256(public_key=b58decode(user3_pub)),
|
||||||
|
[user3_pub])]
|
||||||
transfer_tx = Transaction('TRANSFER', {'id': tx.id}, inputs, outputs)
|
transfer_tx = Transaction('TRANSFER', {'id': tx.id}, inputs, outputs)
|
||||||
transfer_tx = transfer_tx.sign([user_priv])
|
transfer_tx = transfer_tx.sign([user_priv])
|
||||||
|
|
||||||
@ -640,11 +638,11 @@ def test_multiple_input_validation_of_transfer_tx(user_input, user_output,
|
|||||||
|
|
||||||
|
|
||||||
def test_validate_inputs_of_transfer_tx_with_invalid_params(
|
def test_validate_inputs_of_transfer_tx_with_invalid_params(
|
||||||
transfer_tx, cond_uri, utx, user2_pub, user_priv):
|
transfer_tx, cond_uri, utx, user2_pub, user_priv, ffill_uri):
|
||||||
from bigchaindb.common.transaction import Output
|
from bigchaindb.common.transaction import Output
|
||||||
from cryptoconditions import Ed25519Fulfillment
|
from cryptoconditions import Ed25519Sha256
|
||||||
|
|
||||||
invalid_out = Output(Ed25519Fulfillment.from_uri('cf:0:'), ['invalid'])
|
invalid_out = Output(Ed25519Sha256.from_uri(ffill_uri), ['invalid'])
|
||||||
assert transfer_tx.inputs_valid([invalid_out]) is False
|
assert transfer_tx.inputs_valid([invalid_out]) is False
|
||||||
invalid_out = utx.outputs[0]
|
invalid_out = utx.outputs[0]
|
||||||
invalid_out.public_key = 'invalid'
|
invalid_out.public_key = 'invalid'
|
||||||
@ -826,8 +824,6 @@ def test_outputs_to_inputs(tx):
|
|||||||
|
|
||||||
def test_create_transfer_transaction_single_io(tx, user_pub, user2_pub,
|
def test_create_transfer_transaction_single_io(tx, user_pub, user2_pub,
|
||||||
user2_output, user_priv):
|
user2_output, user_priv):
|
||||||
from copy import deepcopy
|
|
||||||
from bigchaindb.common.crypto import PrivateKey
|
|
||||||
from bigchaindb.common.transaction import Transaction
|
from bigchaindb.common.transaction import Transaction
|
||||||
from bigchaindb.common.utils import serialize
|
from bigchaindb.common.utils import serialize
|
||||||
from .utils import validate_transaction_model
|
from .utils import validate_transaction_model
|
||||||
@ -861,8 +857,8 @@ def test_create_transfer_transaction_single_io(tx, user_pub, user2_pub,
|
|||||||
|
|
||||||
expected_input = deepcopy(inputs[0])
|
expected_input = deepcopy(inputs[0])
|
||||||
expected['id'] = transfer_tx['id']
|
expected['id'] = transfer_tx['id']
|
||||||
expected_input.fulfillment.sign(serialize(expected).encode(),
|
expected_input.fulfillment.sign(
|
||||||
PrivateKey(user_priv))
|
serialize(expected).encode(), b58decode(user_priv))
|
||||||
expected_ffill = expected_input.fulfillment.serialize_uri()
|
expected_ffill = expected_input.fulfillment.serialize_uri()
|
||||||
transfer_ffill = transfer_tx['inputs'][0]['fulfillment']
|
transfer_ffill = transfer_tx['inputs'][0]['fulfillment']
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
from time import sleep
|
from time import sleep
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from unittest.mock import patch
|
from base58 import b58decode
|
||||||
|
|
||||||
pytestmark = pytest.mark.bdb
|
pytestmark = pytest.mark.bdb
|
||||||
|
|
||||||
@ -577,14 +578,14 @@ class TestBigchainApi(object):
|
|||||||
|
|
||||||
@pytest.mark.usefixtures('inputs')
|
@pytest.mark.usefixtures('inputs')
|
||||||
def test_non_create_input_not_found(self, b, user_pk):
|
def test_non_create_input_not_found(self, b, user_pk):
|
||||||
from cryptoconditions import Ed25519Fulfillment
|
from cryptoconditions import Ed25519Sha256
|
||||||
from bigchaindb.common.exceptions import InputDoesNotExist
|
from bigchaindb.common.exceptions import InputDoesNotExist
|
||||||
from bigchaindb.common.transaction import Input, TransactionLink
|
from bigchaindb.common.transaction import Input, TransactionLink
|
||||||
from bigchaindb.models import Transaction
|
from bigchaindb.models import Transaction
|
||||||
from bigchaindb import Bigchain
|
from bigchaindb import Bigchain
|
||||||
|
|
||||||
# Create an input for a non existing transaction
|
# Create an input for a non existing transaction
|
||||||
input = Input(Ed25519Fulfillment(public_key=user_pk),
|
input = Input(Ed25519Sha256(public_key=b58decode(user_pk)),
|
||||||
[user_pk],
|
[user_pk],
|
||||||
TransactionLink('somethingsomething', 0))
|
TransactionLink('somethingsomething', 0))
|
||||||
tx = Transaction.transfer([input], [([user_pk], 1)],
|
tx = Transaction.transfer([input], [([user_pk], 1)],
|
||||||
|
@ -180,7 +180,7 @@ def test_vote_accumulates_transactions(b):
|
|||||||
validation = vote_obj.validate_tx(tx.to_dict(), 123, 1)
|
validation = vote_obj.validate_tx(tx.to_dict(), 123, 1)
|
||||||
assert validation == (True, 123, 1)
|
assert validation == (True, 123, 1)
|
||||||
|
|
||||||
tx.inputs[0].fulfillment.signature = None
|
tx.inputs[0].fulfillment.signature = 64*b'z'
|
||||||
validation = vote_obj.validate_tx(tx.to_dict(), 456, 10)
|
validation = vote_obj.validate_tx(tx.to_dict(), 456, 10)
|
||||||
assert validation == (False, 456, 10)
|
assert validation == (False, 456, 10)
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ def test_post_create_transaction_with_invalid_signature(mock_logger,
|
|||||||
|
|
||||||
tx = Transaction.create([user_pub], [([user_pub], 1)])
|
tx = Transaction.create([user_pub], [([user_pub], 1)])
|
||||||
tx = tx.sign([user_priv]).to_dict()
|
tx = tx.sign([user_priv]).to_dict()
|
||||||
tx['inputs'][0]['fulfillment'] = 'cf:0:0'
|
tx['inputs'][0]['fulfillment'] = 64 * '0'
|
||||||
|
|
||||||
res = client.post(TX_ENDPOINT, data=json.dumps(tx))
|
res = client.post(TX_ENDPOINT, data=json.dumps(tx))
|
||||||
expected_status_code = 400
|
expected_status_code = 400
|
||||||
|
1
tox.ini
1
tox.ini
@ -13,6 +13,7 @@ setenv =
|
|||||||
rethinkdb: BIGCHAINDB_DATABASE_BACKEND=rethinkdb
|
rethinkdb: BIGCHAINDB_DATABASE_BACKEND=rethinkdb
|
||||||
mongodb: BIGCHAINDB_DATABASE_BACKEND=mongodb
|
mongodb: BIGCHAINDB_DATABASE_BACKEND=mongodb
|
||||||
deps = {[base]deps}
|
deps = {[base]deps}
|
||||||
|
install_command = pip install {opts} {packages}
|
||||||
extras = test
|
extras = test
|
||||||
commands = pytest -v -n auto --cov=bigchaindb --basetemp={envtmpdir}
|
commands = pytest -v -n auto --cov=bigchaindb --basetemp={envtmpdir}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user