Merge branch 'master' into upsert-val-approve

This commit is contained in:
z-bowen 2018-08-22 18:17:50 +02:00
commit 59f6d878fc
16 changed files with 155 additions and 410 deletions

View File

@ -15,16 +15,6 @@ from bigchaindb.common.transaction import Transaction
register_query = module_dispatch_registrar(backend.query)
@register_query(LocalMongoDBConnection)
def store_transaction(conn, signed_transaction):
try:
return conn.run(
conn.collection('transactions')
.insert_one(signed_transaction))
except DuplicateKeyError:
pass
@register_query(LocalMongoDBConnection)
def store_transactions(conn, signed_transactions):
return conn.run(conn.collection('transactions')

View File

@ -31,8 +31,7 @@ def create_database(conn, dbname):
@register_schema(LocalMongoDBConnection)
def create_tables(conn, dbname):
for table_name in ['transactions', 'utxos', 'assets', 'blocks', 'metadata',
'validators', 'pre_commit']:
for table_name in backend.schema.TABLES:
logger.info('Create `%s` table.', table_name)
# create the table
# TODO: read and write concerns can be declared here

View File

@ -12,20 +12,6 @@ VALIDATOR_UPDATE_ID = 'a_unique_id_string'
PRE_COMMIT_ID = 'a_unique_id_string'
@singledispatch
def store_transaction(connection, signed_transaction):
"""Write a transaction to the backlog table.
Args:
signed_transaction (dict): a signed transaction.
Returns:
The result of the operation.
"""
raise NotImplementedError
@singledispatch
def store_asset(connection, asset):
"""Write an asset to the asset table.

View File

@ -2,16 +2,7 @@
# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
# Code is Apache-2.0 and docs are CC-BY-4.0
"""Database creation and schema-providing interfaces for backends.
Attributes:
TABLES (tuple): The three standard tables BigchainDB relies on:
* ``backlog`` for incoming transactions awaiting to be put into
a block.
* ``bigchain`` for blocks.
"""
"""Database creation and schema-providing interfaces for backends."""
from functools import singledispatch
import logging
@ -23,7 +14,10 @@ from bigchaindb.common.utils import validate_all_values_for_key
logger = logging.getLogger(__name__)
TABLES = ('bigchain', 'backlog', 'assets', 'metadata')
# Tables/collections that every backend database must create
TABLES = ('transactions', 'blocks', 'assets', 'metadata',
'validators', 'pre_commit', 'utxos')
VALID_LANGUAGES = ('danish', 'dutch', 'english', 'finnish', 'french', 'german',
'hungarian', 'italian', 'norwegian', 'portuguese', 'romanian',
'russian', 'spanish', 'swedish', 'turkish', 'none',

View File

@ -70,12 +70,6 @@ class InvalidSignature(ValidationError):
"""
class TransactionNotInValidBlock(ValidationError):
"""Raised when a transfer transaction is attempting to fulfill the
outputs of a transaction that is in an invalid or undecided block
"""
class AssetIdMismatch(ValidationError):
"""Raised when multiple transaction inputs related to different assets"""

View File

@ -8,7 +8,6 @@ MongoDB.
"""
import logging
from collections import namedtuple
from copy import deepcopy
from uuid import uuid4
try:
@ -119,25 +118,6 @@ class BigchainDB(object):
def process_status_code(self, status_code, failure_msg):
return (202, '') if status_code == 0 else (500, failure_msg)
def store_transaction(self, transaction):
"""Store a valid transaction to the transactions collection."""
# self.update_utxoset(transaction)
transaction = deepcopy(transaction.to_dict())
if transaction['operation'] == 'CREATE':
asset = transaction.pop('asset')
asset['id'] = transaction['id']
if asset['data']:
backend.query.store_asset(self.connection, asset)
metadata = transaction.pop('metadata')
transaction_metadata = {'id': transaction['id'],
'metadata': metadata}
backend.query.store_metadatas(self.connection, [transaction_metadata])
return backend.query.store_transaction(self.connection, transaction)
def store_bulk_transactions(self, transactions):
txns = []
assets = []

View File

@ -51,6 +51,15 @@ that allows you to discover the BigchainDB API endpoints:
Transactions
------------
.. note::
If you want to do more sophisticated queries
than those provided by the BigchainDB HTTP API,
then one option is to connect to MongoDB directly (if possible)
and do whatever queries MongoDB allows.
For more about that option, see
`the page about querying BigchainDB <https://docs.bigchaindb.com/en/latest/query.html>`_.
.. http:get:: /api/v1/transactions/{transaction_id}
Get the transaction with the ID ``transaction_id``.
@ -184,6 +193,14 @@ The ``/api/v1/outputs`` endpoint returns transactions outputs filtered by a
given public key, and optionally filtered to only include either spent or
unspent outputs.
.. note::
If you want to do more sophisticated queries
than those provided by the BigchainDB HTTP API,
then one option is to connect to MongoDB directly (if possible)
and do whatever queries MongoDB allows.
For more about that option, see
`the page about querying BigchainDB <https://docs.bigchaindb.com/en/latest/query.html>`_.
.. http:get:: /api/v1/outputs
@ -294,6 +311,15 @@ unspent outputs.
Assets
------
.. note::
If you want to do more sophisticated queries
than those provided by the BigchainDB HTTP API,
then one option is to connect to MongoDB directly (if possible)
and do whatever queries MongoDB allows.
For more about that option, see
`the page about querying BigchainDB <https://docs.bigchaindb.com/en/latest/query.html>`_.
.. http:get:: /api/v1/assets
Return all the assets that match a given text search.
@ -302,10 +328,6 @@ Assets
:query int limit: (Optional) Limit the number of returned assets. Defaults
to ``0`` meaning return all matching assets.
.. note::
Currently this endpoint is only supported if using MongoDB.
.. http:get:: /api/v1/assets/?search={search}
Return all assets that match a given text search.
@ -413,6 +435,15 @@ Assets
Transaction Metadata
--------------------
.. note::
If you want to do more sophisticated queries
than those provided by the BigchainDB HTTP API,
then one option is to connect to MongoDB directly (if possible)
and do whatever queries MongoDB allows.
For more about that option, see
`the page about querying BigchainDB <https://docs.bigchaindb.com/en/latest/query.html>`_.
.. http:get:: /api/v1/metadata
Return all the metadata objects that match a given text search.
@ -421,10 +452,6 @@ Transaction Metadata
:query int limit: (Optional) Limit the number of returned metadata objects. Defaults
to ``0`` meaning return all matching objects.
.. note::
Currently this endpoint is only supported if using MongoDB.
.. http:get:: /api/v1/metadata/?search={search}
Return all metadata objects that match a given text search.

View File

@ -387,7 +387,7 @@ def test_upsert_validator_new_without_tendermint(b, priv_validator_path, user_sk
from bigchaindb.commands.bigchaindb import run_upsert_validator_new
def mock_write(tx, mode):
b.store_transaction(tx)
b.store_bulk_transactions([tx])
return (202, '')
b.get_validators = mock_get

View File

@ -10,6 +10,8 @@ import pytest
from unittest.mock import patch
pytestmark = pytest.mark.tendermint
@pytest.fixture
def reset_bigchaindb_config(monkeypatch):

View File

@ -2,7 +2,6 @@
# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
# Code is Apache-2.0 and docs are CC-BY-4.0
from time import sleep
from unittest.mock import patch
import pytest
@ -13,27 +12,6 @@ pytestmark = pytest.mark.bdb
class TestBigchainApi(object):
@pytest.mark.tendermint
def test_get_spent_with_double_inclusion_detected(self, b, alice):
from bigchaindb.models import Transaction
from bigchaindb.backend.exceptions import OperationError
tx = Transaction.create([alice.public_key], [([alice.public_key], 1)])
tx = tx.sign([alice.private_key])
b.store_bulk_transactions([tx])
transfer_tx = Transaction.transfer(tx.to_inputs(), [([alice.public_key], 1)],
asset_id=tx.id)
transfer_tx = transfer_tx.sign([alice.private_key])
b.store_bulk_transactions([transfer_tx])
transfer_tx2 = Transaction.transfer(tx.to_inputs(), [([alice.public_key], 1)],
asset_id=tx.id)
transfer_tx2 = transfer_tx2.sign([alice.private_key])
with pytest.raises(OperationError):
b.store_bulk_transactions([transfer_tx2])
@pytest.mark.tendermint
def test_get_spent_with_double_spend_detected(self, b, alice):
from bigchaindb.models import Transaction
@ -66,7 +44,7 @@ class TestBigchainApi(object):
b.get_spent(tx.id, 0)
@pytest.mark.tendermint
def test_get_block_status_for_tx_with_double_inclusion(self, b, alice):
def test_double_inclusion(self, b, alice):
from bigchaindb.models import Transaction
from bigchaindb.backend.exceptions import OperationError
@ -81,8 +59,6 @@ class TestBigchainApi(object):
@pytest.mark.tendermint
def test_text_search(self, b, alice):
from bigchaindb.models import Transaction
from bigchaindb.backend.exceptions import OperationError
from bigchaindb.backend.localmongodb.connection import LocalMongoDBConnection
# define the assets
asset1 = {'msg': 'BigchainDB 1'}
@ -101,13 +77,8 @@ class TestBigchainApi(object):
b.store_bulk_transactions([tx1, tx2, tx3])
# get the assets through text search
# this query only works with MongoDB
try:
assets = list(b.text_search('bigchaindb'))
except OperationError as exc:
assert not isinstance(b.connection, LocalMongoDBConnection)
else:
assert len(assets) == 3
assets = list(b.text_search('bigchaindb'))
assert len(assets) == 3
@pytest.mark.usefixtures('inputs')
@pytest.mark.tendermint
@ -148,7 +119,9 @@ class TestBigchainApi(object):
class TestTransactionValidation(object):
def test_non_create_input_not_found(self, b, user_pk, signed_transfer_tx):
@pytest.mark.tendermint
def test_non_create_input_not_found(self, b, signed_transfer_tx):
from bigchaindb.common.exceptions import InputDoesNotExist
from bigchaindb.common.transaction import TransactionLink
@ -156,13 +129,14 @@ class TestTransactionValidation(object):
with pytest.raises(InputDoesNotExist):
b.validate_transaction(signed_transfer_tx)
@pytest.mark.tendermint
@pytest.mark.usefixtures('inputs')
def test_non_create_valid_input_wrong_owner(self, b, user_pk):
from bigchaindb.common.crypto import generate_key_pair
from bigchaindb.common.exceptions import InvalidSignature
from bigchaindb.models import Transaction
input_tx = b.get_owned_ids(user_pk).pop()
input_tx = b.fastquery.get_outputs_by_public_key(user_pk).pop()
input_transaction = b.get_transaction(input_tx.txid)
sk, pk = generate_key_pair()
tx = Transaction.create([pk], [([user_pk], 1)])
@ -173,93 +147,29 @@ class TestTransactionValidation(object):
with pytest.raises(InvalidSignature):
b.validate_transaction(tx)
@pytest.mark.tendermint
@pytest.mark.usefixtures('inputs')
def test_non_create_double_spend(self, b, signed_create_tx,
signed_transfer_tx, double_spend_tx):
from bigchaindb.common.exceptions import DoubleSpend
block1 = b.create_block([signed_create_tx])
b.write_block(block1)
# vote block valid
vote = b.vote(block1.id, b.get_last_voted_block().id, True)
b.write_vote(vote)
b.write_transaction(signed_transfer_tx)
block = b.create_block([signed_transfer_tx])
b.write_block(block)
# vote block valid
vote = b.vote(block.id, b.get_last_voted_block().id, True)
b.write_vote(vote)
sleep(1)
b.store_bulk_transactions([signed_create_tx, signed_transfer_tx])
with pytest.raises(DoubleSpend):
b.validate_transaction(double_spend_tx)
@pytest.mark.usefixtures('inputs')
def test_valid_non_create_transaction_after_block_creation(self, b,
user_pk,
user_sk):
from bigchaindb.models import Transaction
input_tx = b.get_owned_ids(user_pk).pop()
input_tx = b.get_transaction(input_tx.txid)
inputs = input_tx.to_inputs()
transfer_tx = Transaction.transfer(inputs, [([user_pk], 1)],
asset_id=input_tx.id)
transfer_tx = transfer_tx.sign([user_sk])
assert transfer_tx == b.validate_transaction(transfer_tx)
# create block
block = b.create_block([transfer_tx])
assert b.validate_block(block) == block
b.write_block(block)
# check that the transaction is still valid after being written to the
# bigchain
assert transfer_tx == b.validate_transaction(transfer_tx)
@pytest.mark.usefixtures('inputs')
def test_transaction_not_in_valid_block(self, b, user_pk, user_sk):
from bigchaindb.models import Transaction
from bigchaindb.common.exceptions import TransactionNotInValidBlock
input_tx = b.get_owned_ids(user_pk).pop()
input_tx = b.get_transaction(input_tx.txid)
inputs = input_tx.to_inputs()
# create a transaction that's valid but not in a voted valid block
transfer_tx = Transaction.transfer(inputs, [([user_pk], 1)],
asset_id=input_tx.id)
transfer_tx = transfer_tx.sign([user_sk])
assert transfer_tx == b.validate_transaction(transfer_tx)
# create block
block = b.create_block([transfer_tx])
b.write_block(block)
# create transaction with the undecided input
tx_invalid = Transaction.transfer(transfer_tx.to_inputs(),
[([user_pk], 1)],
asset_id=transfer_tx.asset['id'])
tx_invalid = tx_invalid.sign([user_sk])
with pytest.raises(TransactionNotInValidBlock):
b.validate_transaction(tx_invalid)
class TestMultipleInputs(object):
@pytest.mark.tendermint
def test_transfer_single_owner_single_input(self, b, inputs, user_pk,
user_sk):
from bigchaindb.common import crypto
from bigchaindb.models import Transaction
user2_sk, user2_pk = crypto.generate_key_pair()
tx_link = b.get_owned_ids(user_pk).pop()
tx_link = b.fastquery.get_outputs_by_public_key(user_pk).pop()
input_tx = b.get_transaction(tx_link.txid)
inputs = input_tx.to_inputs()
tx = Transaction.transfer(inputs, [([user2_pk], 1)],
@ -271,6 +181,7 @@ class TestMultipleInputs(object):
assert len(tx.inputs) == 1
assert len(tx.outputs) == 1
@pytest.mark.tendermint
def test_single_owner_before_multiple_owners_after_single_input(self, b,
user_sk,
user_pk,
@ -280,9 +191,8 @@ class TestMultipleInputs(object):
user2_sk, user2_pk = crypto.generate_key_pair()
user3_sk, user3_pk = crypto.generate_key_pair()
tx_link = b.fastquery.get_outputs_by_public_key(user_pk).pop()
owned_inputs = b.get_owned_ids(user_pk)
tx_link = owned_inputs.pop()
input_tx = b.get_transaction(tx_link.txid)
tx = Transaction.transfer(input_tx.to_inputs(),
[([user2_pk, user3_pk], 1)],
@ -293,6 +203,7 @@ class TestMultipleInputs(object):
assert len(tx.inputs) == 1
assert len(tx.outputs) == 1
@pytest.mark.tendermint
@pytest.mark.usefixtures('inputs')
def test_multiple_owners_before_single_owner_after_single_input(self, b,
user_sk,
@ -306,14 +217,9 @@ class TestMultipleInputs(object):
tx = Transaction.create([alice.public_key], [([user_pk, user2_pk], 1)])
tx = tx.sign([alice.private_key])
block = b.create_block([tx])
b.write_block(block)
b.store_bulk_transactions([tx])
# vote block valid
vote = b.vote(block.id, b.get_last_voted_block().id, True)
b.write_vote(vote)
owned_input = b.get_owned_ids(user_pk).pop()
owned_input = b.fastquery.get_outputs_by_public_key(user_pk).pop()
input_tx = b.get_transaction(owned_input.txid)
inputs = input_tx.to_inputs()
@ -326,6 +232,7 @@ class TestMultipleInputs(object):
assert len(transfer_tx.inputs) == 1
assert len(transfer_tx.outputs) == 1
@pytest.mark.tendermint
@pytest.mark.usefixtures('inputs')
def test_multiple_owners_before_multiple_owners_after_single_input(self, b,
user_sk,
@ -340,15 +247,10 @@ class TestMultipleInputs(object):
tx = Transaction.create([alice.public_key], [([user_pk, user2_pk], 1)])
tx = tx.sign([alice.private_key])
block = b.create_block([tx])
b.write_block(block)
# vote block valid
vote = b.vote(block.id, b.get_last_voted_block().id, True)
b.write_vote(vote)
b.store_bulk_transactions([tx])
# get input
tx_link = b.get_owned_ids(user_pk).pop()
tx_link = b.fastquery.get_outputs_by_public_key(user_pk).pop()
tx_input = b.get_transaction(tx_link.txid)
tx = Transaction.transfer(tx_input.to_inputs(),
@ -360,6 +262,7 @@ class TestMultipleInputs(object):
assert len(tx.inputs) == 1
assert len(tx.outputs) == 1
@pytest.mark.tendermint
def test_get_owned_ids_single_tx_single_output(self, b, user_sk, user_pk, alice):
from bigchaindb.common import crypto
from bigchaindb.common.transaction import TransactionLink
@ -369,69 +272,25 @@ class TestMultipleInputs(object):
tx = Transaction.create([alice.public_key], [([user_pk], 1)])
tx = tx.sign([alice.private_key])
block = b.create_block([tx])
b.write_block(block)
b.store_bulk_transactions([tx])
owned_inputs_user1 = b.get_owned_ids(user_pk)
owned_inputs_user2 = b.get_owned_ids(user2_pk)
owned_inputs_user1 = b.fastquery.get_outputs_by_public_key(user_pk)
owned_inputs_user2 = b.fastquery.get_outputs_by_public_key(user2_pk)
assert owned_inputs_user1 == [TransactionLink(tx.id, 0)]
assert owned_inputs_user2 == []
tx = Transaction.transfer(tx.to_inputs(), [([user2_pk], 1)],
asset_id=tx.id)
tx = tx.sign([user_sk])
block = b.create_block([tx])
b.write_block(block)
tx_transfer = Transaction.transfer(tx.to_inputs(), [([user2_pk], 1)],
asset_id=tx.id)
tx_transfer = tx_transfer.sign([user_sk])
b.store_bulk_transactions([tx_transfer])
owned_inputs_user1 = b.get_owned_ids(user_pk)
owned_inputs_user2 = b.get_owned_ids(user2_pk)
assert owned_inputs_user1 == []
assert owned_inputs_user2 == [TransactionLink(tx.id, 0)]
owned_inputs_user1 = b.fastquery.get_outputs_by_public_key(user_pk)
owned_inputs_user2 = b.fastquery.get_outputs_by_public_key(user2_pk)
def test_get_owned_ids_single_tx_single_output_invalid_block(self, b,
user_sk,
user_pk,
genesis_block,
alice):
from bigchaindb.common import crypto
from bigchaindb.common.transaction import TransactionLink
from bigchaindb.models import Transaction
user2_sk, user2_pk = crypto.generate_key_pair()
tx = Transaction.create([alice.public_key], [([user_pk], 1)])
tx = tx.sign([alice.private_key])
block = b.create_block([tx])
b.write_block(block)
# vote the block VALID
vote = b.vote(block.id, genesis_block.id, True)
b.write_vote(vote)
owned_inputs_user1 = b.get_owned_ids(user_pk)
owned_inputs_user2 = b.get_owned_ids(user2_pk)
assert owned_inputs_user1 == [TransactionLink(tx.id, 0)]
assert owned_inputs_user2 == []
# NOTE: The transaction itself is valid, still will mark the block
# as invalid to mock the behavior.
tx_invalid = Transaction.transfer(tx.to_inputs(), [([user2_pk], 1)],
asset_id=tx.id)
tx_invalid = tx_invalid.sign([user_sk])
block = b.create_block([tx_invalid])
b.write_block(block)
# vote the block invalid
vote = b.vote(block.id, b.get_last_voted_block().id, False)
b.write_vote(vote)
owned_inputs_user1 = b.get_owned_ids(user_pk)
owned_inputs_user2 = b.get_owned_ids(user2_pk)
# should be the same as before (note tx, not tx_invalid)
assert owned_inputs_user1 == [TransactionLink(tx.id, 0)]
assert owned_inputs_user2 == []
assert owned_inputs_user2 == [TransactionLink(tx_transfer.id, 0)]
@pytest.mark.tendermint
def test_get_owned_ids_single_tx_multiple_outputs(self, b, user_sk,
user_pk, alice):
from bigchaindb.common import crypto
@ -443,12 +302,11 @@ class TestMultipleInputs(object):
# create divisible asset
tx_create = Transaction.create([alice.public_key], [([user_pk], 1), ([user_pk], 1)])
tx_create_signed = tx_create.sign([alice.private_key])
block = b.create_block([tx_create_signed])
b.write_block(block)
b.store_bulk_transactions([tx_create_signed])
# get input
owned_inputs_user1 = b.get_owned_ids(user_pk)
owned_inputs_user2 = b.get_owned_ids(user2_pk)
owned_inputs_user1 = b.fastquery.get_outputs_by_public_key(user_pk)
owned_inputs_user2 = b.fastquery.get_outputs_by_public_key(user2_pk)
expected_owned_inputs_user1 = [TransactionLink(tx_create.id, 0),
TransactionLink(tx_create.id, 1)]
@ -460,15 +318,15 @@ class TestMultipleInputs(object):
[([user2_pk], 1), ([user2_pk], 1)],
asset_id=tx_create.id)
tx_transfer_signed = tx_transfer.sign([user_sk])
block = b.create_block([tx_transfer_signed])
b.write_block(block)
b.store_bulk_transactions([tx_transfer_signed])
owned_inputs_user1 = b.get_owned_ids(user_pk)
owned_inputs_user2 = b.get_owned_ids(user2_pk)
assert owned_inputs_user1 == []
owned_inputs_user1 = b.fastquery.get_outputs_by_public_key(user_pk)
owned_inputs_user2 = b.fastquery.get_outputs_by_public_key(user2_pk)
assert owned_inputs_user1 == expected_owned_inputs_user1
assert owned_inputs_user2 == [TransactionLink(tx_transfer.id, 0),
TransactionLink(tx_transfer.id, 1)]
@pytest.mark.tendermint
def test_get_owned_ids_multiple_owners(self, b, user_sk, user_pk, alice):
from bigchaindb.common import crypto
from bigchaindb.common.transaction import TransactionLink
@ -479,11 +337,11 @@ class TestMultipleInputs(object):
tx = Transaction.create([alice.public_key], [([user_pk, user2_pk], 1)])
tx = tx.sign([alice.private_key])
block = b.create_block([tx])
b.write_block(block)
owned_inputs_user1 = b.get_owned_ids(user_pk)
owned_inputs_user2 = b.get_owned_ids(user2_pk)
b.store_bulk_transactions([tx])
owned_inputs_user1 = b.fastquery.get_outputs_by_public_key(user_pk)
owned_inputs_user2 = b.fastquery.get_outputs_by_public_key(user_pk)
expected_owned_inputs_user1 = [TransactionLink(tx.id, 0)]
assert owned_inputs_user1 == owned_inputs_user2
@ -492,14 +350,16 @@ class TestMultipleInputs(object):
tx = Transaction.transfer(tx.to_inputs(), [([user3_pk], 1)],
asset_id=tx.id)
tx = tx.sign([user_sk, user2_sk])
block = b.create_block([tx])
b.write_block(block)
b.store_bulk_transactions([tx])
owned_inputs_user1 = b.fastquery.get_outputs_by_public_key(user_pk)
owned_inputs_user2 = b.fastquery.get_outputs_by_public_key(user2_pk)
spent_user1 = b.get_spent(tx.id, 0)
owned_inputs_user1 = b.get_owned_ids(user_pk)
owned_inputs_user2 = b.get_owned_ids(user2_pk)
assert owned_inputs_user1 == owned_inputs_user2
assert owned_inputs_user1 == []
assert not spent_user1
@pytest.mark.tendermint
def test_get_spent_single_tx_single_output(self, b, user_sk, user_pk, alice):
from bigchaindb.common import crypto
from bigchaindb.models import Transaction
@ -508,72 +368,25 @@ class TestMultipleInputs(object):
tx = Transaction.create([alice.public_key], [([user_pk], 1)])
tx = tx.sign([alice.private_key])
block = b.create_block([tx])
b.write_block(block)
b.store_bulk_transactions([tx])
owned_inputs_user1 = b.get_owned_ids(user_pk).pop()
owned_inputs_user1 = b.fastquery.get_outputs_by_public_key(user_pk).pop()
# check spents
input_txid = owned_inputs_user1.txid
input_idx = owned_inputs_user1.output
spent_inputs_user1 = b.get_spent(input_txid, input_idx)
spent_inputs_user1 = b.get_spent(input_txid, 0)
assert spent_inputs_user1 is None
# create a transaction and block
# create a transaction and send it
tx = Transaction.transfer(tx.to_inputs(), [([user2_pk], 1)],
asset_id=tx.id)
tx = tx.sign([user_sk])
block = b.create_block([tx])
b.write_block(block)
b.store_bulk_transactions([tx])
spent_inputs_user1 = b.get_spent(input_txid, input_idx)
spent_inputs_user1 = b.get_spent(input_txid, 0)
assert spent_inputs_user1 == tx
def test_get_spent_single_tx_single_output_invalid_block(self, b,
user_sk,
user_pk,
genesis_block,
alice):
from bigchaindb.common import crypto
from bigchaindb.models import Transaction
# create a new users
user2_sk, user2_pk = crypto.generate_key_pair()
tx = Transaction.create([alice.public_key], [([user_pk], 1)])
tx = tx.sign([alice.private_key])
block = b.create_block([tx])
b.write_block(block)
# vote the block VALID
vote = b.vote(block.id, genesis_block.id, True)
b.write_vote(vote)
owned_inputs_user1 = b.get_owned_ids(user_pk).pop()
# check spents
input_txid = owned_inputs_user1.txid
input_idx = owned_inputs_user1.output
spent_inputs_user1 = b.get_spent(input_txid, input_idx)
assert spent_inputs_user1 is None
# create a transaction and block
tx = Transaction.transfer(tx.to_inputs(), [([user2_pk], 1)],
asset_id=tx.id)
tx = tx.sign([user_sk])
block = b.create_block([tx])
b.write_block(block)
# vote the block invalid
vote = b.vote(block.id, b.get_last_voted_block().id, False)
b.write_vote(vote)
# NOTE: I have no idea why this line is here
b.get_transaction(tx.id)
spent_inputs_user1 = b.get_spent(input_txid, input_idx)
# Now there should be no spents (the block is invalid)
assert spent_inputs_user1 is None
@pytest.mark.tendermint
def test_get_spent_single_tx_multiple_outputs(self, b, user_sk, user_pk, alice):
from bigchaindb.common import crypto
from bigchaindb.models import Transaction
@ -587,10 +400,9 @@ class TestMultipleInputs(object):
([user_pk], 1),
([user_pk], 1)])
tx_create_signed = tx_create.sign([alice.private_key])
block = b.create_block([tx_create_signed])
b.write_block(block)
b.store_bulk_transactions([tx_create_signed])
owned_inputs_user1 = b.get_owned_ids(user_pk)
owned_inputs_user1 = b.fastquery.get_outputs_by_public_key(user_pk)
# check spents
for input_tx in owned_inputs_user1:
@ -601,8 +413,7 @@ class TestMultipleInputs(object):
[([user2_pk], 1), ([user2_pk], 1)],
asset_id=tx_create.id)
tx_transfer_signed = tx_transfer.sign([user_sk])
block = b.create_block([tx_transfer_signed])
b.write_block(block)
b.store_bulk_transactions([tx_transfer_signed])
# check that used inputs are marked as spent
for ffill in tx_create.to_inputs()[:2]:
@ -613,6 +424,7 @@ class TestMultipleInputs(object):
# spendable by BigchainDB
assert b.get_spent(tx_create.to_inputs()[2].fulfills.txid, 2) is None
@pytest.mark.tendermint
def test_get_spent_multiple_owners(self, b, user_sk, user_pk, alice):
from bigchaindb.common import crypto
from bigchaindb.models import Transaction
@ -627,11 +439,10 @@ class TestMultipleInputs(object):
payload)
tx = tx.sign([alice.private_key])
transactions.append(tx)
block = b.create_block(transactions)
b.write_block(block)
owned_inputs_user1 = b.get_owned_ids(user_pk)
b.store_bulk_transactions(transactions)
owned_inputs_user1 = b.fastquery.get_outputs_by_public_key(user_pk)
# check spents
for input_tx in owned_inputs_user1:
assert b.get_spent(input_tx.txid, input_tx.output) is None
@ -641,26 +452,15 @@ class TestMultipleInputs(object):
[([user3_pk], 1)],
asset_id=transactions[0].id)
tx = tx.sign([user_sk, user2_sk])
block = b.create_block([tx])
b.write_block(block)
b.store_bulk_transactions([tx])
# check that used inputs are marked as spent
assert b.get_spent(transactions[0].id, 0) == tx
# check that the other remain marked as unspent
for unspent in transactions[1:]:
assert b.get_spent(unspent.id, 0) is None
def test_get_owned_ids_calls_get_outputs_filtered():
from bigchaindb import BigchainDB
with patch('bigchaindb.BigchainDB.get_outputs_filtered') as gof:
b = BigchainDB()
res = b.get_owned_ids('abc')
gof.assert_called_once_with('abc', spent=False)
assert res == gof()
@pytest.mark.tendermint
def test_get_outputs_filtered_only_unspent():
from bigchaindb.common.transaction import TransactionLink
@ -712,8 +512,8 @@ def test_get_outputs_filtered(filter_spent, filter_unspent):
assert out == get_outputs.return_value
@pytest.mark.bdb
def test_cant_spend_same_input_twice_in_tx(b, genesis_block, alice):
@pytest.mark.tendermint
def test_cant_spend_same_input_twice_in_tx(b, alice):
"""Recreate duplicated fulfillments bug
https://github.com/bigchaindb/bigchaindb/issues/1099
"""
@ -725,11 +525,7 @@ def test_cant_spend_same_input_twice_in_tx(b, genesis_block, alice):
tx_create_signed = tx_create.sign([alice.private_key])
assert b.validate_transaction(tx_create_signed) == tx_create_signed
# create a block and valid vote
block = b.create_block([tx_create_signed])
b.write_block(block)
vote = b.vote(block.id, genesis_block.id, True)
b.write_vote(vote)
b.store_bulk_transactions([tx_create_signed])
# Create a transfer transaction with duplicated fulfillments
dup_inputs = tx_create.to_inputs() + tx_create.to_inputs()
@ -740,8 +536,9 @@ def test_cant_spend_same_input_twice_in_tx(b, genesis_block, alice):
tx_transfer_signed.validate(b)
@pytest.mark.bdb
@pytest.mark.tendermint
def test_transaction_unicode(b, alice):
import copy
from bigchaindb.common.utils import serialize
from bigchaindb.models import Transaction
@ -751,45 +548,8 @@ def test_transaction_unicode(b, alice):
tx = (Transaction.create([alice.public_key], [([alice.public_key], 100)], beer_python)
).sign([alice.private_key])
block = b.create_block([tx])
b.write_block(block)
assert b.get_block(block.id) == block.to_dict()
assert block.validate(b) == block
assert beer_json in serialize(block.to_dict())
tx_1 = copy.deepcopy(tx)
b.store_bulk_transactions([tx])
@pytest.mark.bdb
def test_is_new_transaction(b, genesis_block, alice):
from bigchaindb.models import Transaction
def write_tx(n):
tx = Transaction.create([alice.public_key], [([alice.public_key], n)])
tx = tx.sign([alice.private_key])
# Tx is new because it's not in any block
assert b.is_new_transaction(tx.id)
block = b.create_block([tx])
b.write_block(block)
return tx, block
# test VALID case
tx, block = write_tx(1)
# Tx is now in undecided block
assert not b.is_new_transaction(tx.id)
assert b.is_new_transaction(tx.id, exclude_block_id=block.id)
# After voting valid, should not be new
vote = b.vote(block.id, genesis_block.id, True)
b.write_vote(vote)
assert not b.is_new_transaction(tx.id)
assert b.is_new_transaction(tx.id, exclude_block_id=block.id)
# test INVALID case
tx, block = write_tx(2)
# Tx is now in undecided block
assert not b.is_new_transaction(tx.id)
assert b.is_new_transaction(tx.id, exclude_block_id=block.id)
vote = b.vote(block.id, genesis_block.id, False)
b.write_vote(vote)
# Tx is new because it's only found in an invalid block
assert b.is_new_transaction(tx.id)
assert b.is_new_transaction(tx.id, exclude_block_id=block.id)
assert beer_json in serialize(tx_1.to_dict())

View File

@ -22,6 +22,7 @@ pytestmark = pytest.mark.tendermint
@pytest.mark.bdb
def test_asset_is_separated_from_transaciton(b):
import copy
from bigchaindb.models import Transaction
from bigchaindb.common.crypto import generate_key_pair
@ -43,10 +44,16 @@ def test_asset_is_separated_from_transaciton(b):
asset=asset)\
.sign([alice.private_key])
b.store_transaction(tx)
# with store_bulk_transactions we use `insert_many` where PyMongo
# automatically adds an `_id` field to the tx, therefore we need the
# deepcopy, for more info see:
# https://api.mongodb.com/python/current/faq.html#writes-and-ids
tx_dict = copy.deepcopy(tx.to_dict())
b.store_bulk_transactions([tx])
assert 'asset' not in backend.query.get_transaction(b.connection, tx.id)
assert backend.query.get_asset(b.connection, tx.id)['data'] == asset
assert b.get_transaction(tx.id) == tx
assert b.get_transaction(tx.id).to_dict() == tx_dict
@pytest.mark.bdb
@ -183,21 +190,22 @@ def test_update_utxoset(tb, signed_create_tx, signed_transfer_tx, db_context):
@pytest.mark.bdb
def test_store_transaction(mocker, tb, signed_create_tx,
signed_transfer_tx, db_context):
mocked_store_asset = mocker.patch('bigchaindb.backend.query.store_asset')
mocked_store_asset = mocker.patch('bigchaindb.backend.query.store_assets')
mocked_store_metadata = mocker.patch(
'bigchaindb.backend.query.store_metadatas')
mocked_store_transaction = mocker.patch(
'bigchaindb.backend.query.store_transaction')
tb.store_transaction(signed_create_tx)
'bigchaindb.backend.query.store_transactions')
tb.store_bulk_transactions([signed_create_tx])
# mongo_client = MongoClient(host=db_context.host, port=db_context.port)
# utxoset = mongo_client[db_context.name]['utxos']
# assert utxoset.count() == 1
# utxo = utxoset.find_one()
# assert utxo['transaction_id'] == signed_create_tx.id
# assert utxo['output_index'] == 0
mocked_store_asset.assert_called_once_with(
tb.connection,
{'id': signed_create_tx.id, 'data': signed_create_tx.asset['data']},
[{'id': signed_create_tx.id, 'data': signed_create_tx.asset['data']}],
)
mocked_store_metadata.assert_called_once_with(
tb.connection,
@ -205,13 +213,13 @@ def test_store_transaction(mocker, tb, signed_create_tx,
)
mocked_store_transaction.assert_called_once_with(
tb.connection,
{k: v for k, v in signed_create_tx.to_dict().items()
if k not in ('asset', 'metadata')},
[{k: v for k, v in signed_create_tx.to_dict().items()
if k not in ('asset', 'metadata')}],
)
mocked_store_asset.reset_mock()
mocked_store_metadata.reset_mock()
mocked_store_transaction.reset_mock()
tb.store_transaction(signed_transfer_tx)
tb.store_bulk_transactions([signed_transfer_tx])
# assert utxoset.count() == 1
# utxo = utxoset.find_one()
# assert utxo['transaction_id'] == signed_transfer_tx.id
@ -219,12 +227,12 @@ def test_store_transaction(mocker, tb, signed_create_tx,
assert not mocked_store_asset.called
mocked_store_metadata.asser_called_once_with(
tb.connection,
{'id': signed_transfer_tx.id, 'metadata': signed_transfer_tx.metadata},
[{'id': signed_transfer_tx.id, 'metadata': signed_transfer_tx.metadata}],
)
mocked_store_transaction.assert_called_once_with(
tb.connection,
{k: v for k, v in signed_transfer_tx.to_dict().items()
if k != 'metadata'},
[{k: v for k, v in signed_transfer_tx.to_dict().items()
if k != 'metadata'}],
)

View File

@ -7,7 +7,6 @@ import pytest
pytestmark = pytest.mark.tendermint
@pytest.mark.skipif(reason='will be fixed in another PR')
@pytest.fixture
def config(request, monkeypatch):
backend = request.config.getoption('--database-backend')

View File

@ -37,7 +37,7 @@ def test_get_assets_tendermint(client, tb, alice):
tx = Transaction.create([alice.public_key], [([alice.public_key], 1)],
asset=asset).sign([alice.private_key])
tb.store_transaction(tx)
tb.store_bulk_transactions([tx])
# test that asset is returned
res = client.get(ASSETS_ENDPOINT + '?search=abc')
@ -64,8 +64,8 @@ def test_get_assets_limit_tendermint(client, tb, alice):
tx2 = Transaction.create([alice.public_key], [([alice.public_key], 1)],
asset=asset2).sign([alice.private_key])
b.store_transaction(tx1)
b.store_transaction(tx2)
b.store_bulk_transactions([tx1])
b.store_bulk_transactions([tx2])
# test that both assets are returned without limit
res = client.get(ASSETS_ENDPOINT + '?search=abc')

View File

@ -15,10 +15,17 @@ pytestmark = pytest.mark.tendermint
@pytest.mark.bdb
@pytest.mark.usefixtures('inputs')
def test_get_block_endpoint(tb, client, alice):
import copy
b = tb
tx = Transaction.create([alice.public_key], [([alice.public_key], 1)], asset={'cycle': 'hero'})
tx = tx.sign([alice.private_key])
b.store_transaction(tx)
# with store_bulk_transactions we use `insert_many` where PyMongo
# automatically adds an `_id` field to the tx, therefore we need the
# deepcopy, for more info see:
# https://api.mongodb.com/python/current/faq.html#writes-and-ids
tx_dict = copy.deepcopy(tx.to_dict())
b.store_bulk_transactions([tx])
block = Block(app_hash='random_utxo',
height=31,
@ -26,7 +33,7 @@ def test_get_block_endpoint(tb, client, alice):
b.store_block(block._asdict())
res = client.get(BLOCKS_ENDPOINT + str(block.height))
expected_response = {'height': block.height, 'transactions': [tx.to_dict()]}
expected_response = {'height': block.height, 'transactions': [tx_dict]}
assert res.json == expected_response
assert res.status_code == 200
@ -46,7 +53,7 @@ def test_get_block_containing_transaction(tb, client, alice):
b = tb
tx = Transaction.create([alice.public_key], [([alice.public_key], 1)], asset={'cycle': 'hero'})
tx = tx.sign([alice.private_key])
b.store_transaction(tx)
b.store_bulk_transactions([tx])
block = Block(app_hash='random_utxo',
height=13,

View File

@ -39,7 +39,7 @@ def test_get_metadata_tendermint(client, tb, alice):
tx = Transaction.create([alice.public_key], [([alice.public_key], 1)], metadata=metadata,
asset=asset).sign([alice.private_key])
b.store_transaction(tx)
b.store_bulk_transactions([tx])
# test that metadata is returned
res = client.get(METADATA_ENDPOINT + '?search=my_meta')
@ -63,13 +63,13 @@ def test_get_metadata_limit_tendermint(client, tb, alice):
meta1 = {'key': 'meta 1'}
tx1 = Transaction.create([alice.public_key], [([alice.public_key], 1)], metadata=meta1,
asset=asset1).sign([alice.private_key])
b.store_transaction(tx1)
b.store_bulk_transactions([tx1])
asset2 = {'msg': 'abc 2'}
meta2 = {'key': 'meta 2'}
tx2 = Transaction.create([alice.public_key], [([alice.public_key], 1)], metadata=meta2,
asset=asset2).sign([alice.private_key])
b.store_transaction(tx2)
b.store_bulk_transactions([tx2])
# test that both assets are returned without limit
res = client.get(METADATA_ENDPOINT + '?search=meta')

View File

@ -265,7 +265,6 @@ def test_post_create_transaction_with_invalid_schema(mock_logger, client):
('ValidationError', 'Create and transfer!'),
('InputDoesNotExist', 'Hallucinations?'),
('TransactionOwnerError', 'Not yours!'),
('TransactionNotInValidBlock', 'Wait, maybe?'),
('ValidationError', '?'),
))
@patch('bigchaindb.web.views.base.logger')