def txns(b, user_pk, user_sk, user2_pk, user2_sk): txs = [Transaction.create([user_pk], [([user2_pk], 1)]).sign([user_sk]), Transaction.create([user2_pk], [([user_pk], 1)]).sign([user2_sk]), Transaction.create([user_pk], [([user_pk], 1), ([user2_pk], 1)]) .sign([user_sk])] b.store_bulk_transactions(txs) return txs
def test_get_metadata_limit_tendermint(client, b, alice): from bigchaindb.models import Transaction # create two assets asset1 = {'msg': 'abc 1'} meta1 = {'key': 'meta 1'} tx1 = Transaction.create([alice.public_key], [([alice.public_key], 1)], metadata=meta1, asset=asset1).sign([alice.private_key]) 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_bulk_transactions([tx2]) # test that both assets are returned without limit res = client.get(METADATA_ENDPOINT + '?search=meta') assert res.status_code == 200 assert len(res.json) == 2 # test that only one asset is returned when using limit=1 res = client.get(METADATA_ENDPOINT + '?search=meta&limit=1') assert res.status_code == 200 assert len(res.json) == 1
def test_asset_id_mismatch(alice, user_pk): from bigchaindb.models import Transaction from bigchaindb.common.exceptions import AssetIdMismatch tx1 = Transaction.create([alice.public_key], [([user_pk], 1)], metadata={'msg': random.random()}) tx1.sign([alice.private_key]) tx2 = Transaction.create([alice.public_key], [([user_pk], 1)], metadata={'msg': random.random()}) tx2.sign([alice.private_key]) with pytest.raises(AssetIdMismatch): Transaction.get_asset_id([tx1, tx2])
def test_asset_is_separated_from_transaciton(b): import copy from bigchaindb.models import Transaction from bigchaindb.common.crypto import generate_key_pair alice = generate_key_pair() bob = generate_key_pair() asset = {'Never gonna': ['give you up', 'let you down', 'run around' 'desert you', 'make you cry', 'say goodbye', 'tell a lie', 'hurt you']} tx = Transaction.create([alice.public_key], [([bob.public_key], 1)], metadata=None, asset=asset)\ .sign([alice.private_key]) # 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).to_dict() == tx_dict
def test_get_spent_multiple_owners(self, b, user_sk, user_pk, alice): 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() transactions = [] for i in range(3): payload = {'somedata': i} tx = Transaction.create([alice.public_key], [([user_pk, user2_pk], 1)], payload) tx = tx.sign([alice.private_key]) transactions.append(tx) 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 # create a transaction tx = Transaction.transfer(transactions[0].to_inputs(), [([user3_pk], 1)], asset_id=transactions[0].id) tx = tx.sign([user_sk, user2_sk]) 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_spent_single_tx_single_output(self, b, user_sk, user_pk, alice): from bigchaindb.common import crypto 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]) b.store_bulk_transactions([tx]) owned_inputs_user1 = b.fastquery.get_outputs_by_public_key(user_pk).pop() # check spents input_txid = owned_inputs_user1.txid spent_inputs_user1 = b.get_spent(input_txid, 0) assert spent_inputs_user1 is None # create a transaction and send it tx = Transaction.transfer(tx.to_inputs(), [([user2_pk], 1)], asset_id=tx.id) tx = tx.sign([user_sk]) b.store_bulk_transactions([tx]) spent_inputs_user1 = b.get_spent(input_txid, 0) assert spent_inputs_user1 == tx
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 from bigchaindb.models import Transaction user2_sk, user2_pk = crypto.generate_key_pair() user3_sk, user3_pk = crypto.generate_key_pair() tx = Transaction.create([alice.public_key], [([user_pk, user2_pk], 1)]) tx = tx.sign([alice.private_key]) 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 assert owned_inputs_user1 == expected_owned_inputs_user1 tx = Transaction.transfer(tx.to_inputs(), [([user3_pk], 1)], asset_id=tx.id) tx = tx.sign([user_sk, user2_sk]) 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) assert owned_inputs_user1 == owned_inputs_user2 assert not spent_user1
def test_get_owned_ids_single_tx_multiple_outputs(self, b, user_sk, user_pk, 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() # 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]) b.store_bulk_transactions([tx_create_signed]) # get input 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)] assert owned_inputs_user1 == expected_owned_inputs_user1 assert owned_inputs_user2 == [] # transfer divisible asset divided in two outputs tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([user2_pk], 1), ([user2_pk], 1)], asset_id=tx_create.id) tx_transfer_signed = tx_transfer.sign([user_sk]) b.store_bulk_transactions([tx_transfer_signed]) 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)]
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 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]) 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) assert owned_inputs_user1 == [TransactionLink(tx.id, 0)] assert owned_inputs_user2 == [] 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.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 == [TransactionLink(tx_transfer.id, 0)]
def test_multiple_owners_before_multiple_owners_after_single_input(self, b, user_sk, user_pk, alice): 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() user4_sk, user4_pk = crypto.generate_key_pair() tx = Transaction.create([alice.public_key], [([user_pk, user2_pk], 1)]) tx = tx.sign([alice.private_key]) b.store_bulk_transactions([tx]) # get input 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(), [([user3_pk, user4_pk], 1)], asset_id=tx_input.id) tx = tx.sign([user_sk, user2_sk]) tx.validate(b) assert len(tx.inputs) == 1 assert len(tx.outputs) == 1
def test_get_spent_key_order(b, user_pk, user_sk, user2_pk, user2_sk): from bigchaindb import backend from bigchaindb.models import Transaction from bigchaindb.common.crypto import generate_key_pair from bigchaindb.common.exceptions import DoubleSpend alice = generate_key_pair() bob = generate_key_pair() tx1 = Transaction.create([user_pk], [([alice.public_key], 3), ([user_pk], 2)], asset=None)\ .sign([user_sk]) b.store_bulk_transactions([tx1]) inputs = tx1.to_inputs() tx2 = Transaction.transfer([inputs[1]], [([user2_pk], 2)], tx1.id).sign([user_sk]) assert tx2.validate(b) tx2_dict = tx2.to_dict() fulfills = tx2_dict['inputs'][0]['fulfills'] tx2_dict['inputs'][0]['fulfills'] = {'output_index': fulfills['output_index'], 'transaction_id': fulfills['transaction_id']} backend.query.store_transactions(b.connection, [tx2_dict]) tx3 = Transaction.transfer([inputs[1]], [([bob.public_key], 2)], tx1.id).sign([user_sk]) with pytest.raises(DoubleSpend): tx3.validate(b)
async def test_subscribe_events(tendermint_ws_url, b): from bigchaindb.event_stream import subscribe_events from bigchaindb.common.crypto import generate_key_pair from bigchaindb.models import Transaction session = ClientSession() ws = await session.ws_connect(tendermint_ws_url) stream_id = 'bigchaindb_stream_01' await subscribe_events(ws, stream_id) msg = await ws.receive() assert msg.data msg_data_dict = json.loads(msg.data) assert msg_data_dict['id'] == stream_id assert msg_data_dict['jsonrpc'] == '2.0' assert msg_data_dict['result'] == {} alice = generate_key_pair() tx = Transaction.create([alice.public_key], [([alice.public_key], 1)], asset=None)\ .sign([alice.private_key]) b.post_transaction(tx, 'broadcast_tx_async') msg = await ws.receive() msg_data_dict = json.loads(msg.data) raw_txn = msg_data_dict['result']['data']['value']['block']['data']['txs'][0] transaction = json.loads(base64.b64decode(raw_txn).decode('utf8')) assert transaction == tx.to_dict()
def test_memoize_from_dict(b): alice = generate_key_pair() asset = { 'data': {'id': 'test_id'}, } assert from_dict.cache_info().hits == 0 assert from_dict.cache_info().misses == 0 tx = Transaction.create([alice.public_key], [([alice.public_key], 1)], asset=asset,)\ .sign([alice.private_key]) tx_dict = deepcopy(tx.to_dict()) Transaction.from_dict(tx_dict) assert from_dict.cache_info().hits == 0 assert from_dict.cache_info().misses == 1 Transaction.from_dict(tx_dict) Transaction.from_dict(tx_dict) assert from_dict.cache_info().hits == 2 assert from_dict.cache_info().misses == 1
def test_post_create_transaction_with_invalid_id(mock_logger, b, client): from bigchaindb.common.exceptions import InvalidHash from bigchaindb.models import Transaction user_priv, user_pub = crypto.generate_key_pair() tx = Transaction.create([user_pub], [([user_pub], 1)]) tx = tx.sign([user_priv]).to_dict() tx['id'] = 'abcd' * 16 res = client.post(TX_ENDPOINT, data=json.dumps(tx)) expected_status_code = 400 expected_error_message = ( "Invalid transaction ({}): The transaction's id '{}' isn't equal to " "the hash of its body, i.e. it's not valid." ).format(InvalidHash.__name__, tx['id']) assert res.status_code == expected_status_code assert res.json['message'] == expected_error_message assert mock_logger.error.called assert ( 'HTTP API error: %(status)s - %(method)s:%(path)s - %(message)s' in mock_logger.error.call_args[0] ) assert ( { 'message': expected_error_message, 'status': expected_status_code, 'method': 'POST', 'path': TX_ENDPOINT } in mock_logger.error.call_args[0] )
def test_post_transaction_responses(tendermint_ws_url, b): from bigchaindb.common.crypto import generate_key_pair from bigchaindb.models import Transaction alice = generate_key_pair() bob = generate_key_pair() tx = Transaction.create([alice.public_key], [([alice.public_key], 1)], asset=None)\ .sign([alice.private_key]) code, message = b.write_transaction(tx, 'broadcast_tx_commit') assert code == 202 tx_transfer = Transaction.transfer(tx.to_inputs(), [([bob.public_key], 1)], asset_id=tx.id)\ .sign([alice.private_key]) code, message = b.write_transaction(tx_transfer, 'broadcast_tx_commit') assert code == 202 carly = generate_key_pair() double_spend = Transaction.transfer( tx.to_inputs(), [([carly.public_key], 1)], asset_id=tx.id, ).sign([alice.private_key]) for mode in ('broadcast_tx_sync', 'broadcast_tx_commit'): code, message = b.write_transaction(double_spend, mode) assert code == 500 assert message == 'Transaction validation failed'
def test_threshold_same_public_key(alice, b, user_pk, 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 # CREATE divisible asset tx_create = Transaction.create([alice.public_key], [([user_pk, user_pk], 100)], asset={'name': random.random()}) tx_create_signed = tx_create.sign([alice.private_key]) # TRANSFER tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([alice.public_key], 100)], asset_id=tx_create.id) tx_transfer_signed = tx_transfer.sign([user_sk, user_sk]) b.store_bulk_transactions([tx_create_signed]) assert tx_transfer_signed.validate(b) == tx_transfer_signed b.store_bulk_transactions([tx_transfer_signed]) with pytest.raises(DoubleSpend): tx_transfer_signed.validate(b)
def test_post_create_transaction_with_language(b, client, nested, language, expected_status_code): from bigchaindb.models import Transaction from bigchaindb.backend.localmongodb.connection import LocalMongoDBConnection if isinstance(b.connection, LocalMongoDBConnection): user_priv, user_pub = crypto.generate_key_pair() lang_obj = {'language': language} if nested: asset = {'root': lang_obj} else: asset = lang_obj tx = Transaction.create([user_pub], [([user_pub], 1)], asset=asset) tx = tx.sign([user_priv]) res = client.post(TX_ENDPOINT, data=json.dumps(tx.to_dict())) assert res.status_code == expected_status_code if res.status_code == 400: expected_error_message = ( 'Invalid transaction (ValidationError): MongoDB does not support ' 'text search for the language "{}". If you do not understand this ' 'error message then please rename key/field "language" to something ' 'else like "lang".').format(language) assert res.json['message'] == expected_error_message
def test_muiltiple_in_mix_own_multiple_out_single_own_transfer(alice, b, user_pk, user_sk): from bigchaindb.models import Transaction from bigchaindb.common.transaction import _fulfillment_to_details # CREATE divisible asset tx_create = Transaction.create([alice.public_key], [([user_pk], 50), ([user_pk, alice.public_key], 50)], asset={'name': random.random()}) tx_create_signed = tx_create.sign([alice.private_key]) # TRANSFER tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([alice.public_key], 100)], asset_id=tx_create.id) tx_transfer_signed = tx_transfer.sign([alice.private_key, user_sk]) b.store_bulk_transactions([tx_create_signed]) assert tx_transfer_signed.validate(b) == tx_transfer_signed assert len(tx_transfer_signed.outputs) == 1 assert tx_transfer_signed.outputs[0].amount == 100 assert len(tx_transfer_signed.inputs) == 2 ffill_fid0 = _fulfillment_to_details(tx_transfer_signed.inputs[0].fulfillment) ffill_fid1 = _fulfillment_to_details(tx_transfer_signed.inputs[1].fulfillment) assert 'subconditions' not in ffill_fid0 assert 'subconditions' in ffill_fid1 assert len(ffill_fid1['subconditions']) == 2 b.store_bulk_transactions([tx_transfer_signed]) with pytest.raises(DoubleSpend): tx_transfer_signed.validate(b)
def test_get_spent_with_double_spend_detected(self, b, alice): from bigchaindb.models import Transaction from bigchaindb.common.exceptions import DoubleSpend from bigchaindb.exceptions import CriticalDoubleSpend 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]) transfer_tx2 = Transaction.transfer(tx.to_inputs(), [([alice.public_key], 2)], asset_id=tx.id) transfer_tx2 = transfer_tx2.sign([alice.private_key]) with pytest.raises(DoubleSpend): b.validate_transaction(transfer_tx2, [transfer_tx]) b.store_bulk_transactions([transfer_tx]) with pytest.raises(DoubleSpend): b.validate_transaction(transfer_tx2) b.store_bulk_transactions([transfer_tx2]) with pytest.raises(CriticalDoubleSpend): b.get_spent(tx.id, 0)
def test_websocket_block_event(b, test_client, loop): from bigchaindb import events from bigchaindb.web.websocket_server import init_app, POISON_PILL, EVENTS_ENDPOINT from bigchaindb.models import Transaction from bigchaindb.common import crypto user_priv, user_pub = crypto.generate_key_pair() tx = Transaction.create([user_pub], [([user_pub], 1)]) tx = tx.sign([user_priv]) event_source = asyncio.Queue(loop=loop) app = init_app(event_source, loop=loop) client = yield from test_client(app) ws = yield from client.ws_connect(EVENTS_ENDPOINT) block = {'height': 1, 'transactions': [tx.to_dict()]} block_event = events.Event(events.EventTypes.BLOCK_VALID, block) yield from event_source.put(block_event) for tx in block['transactions']: result = yield from ws.receive() json_result = json.loads(result.data) assert json_result['transaction_id'] == tx['id'] # Since the transactions are all CREATEs, asset id == transaction id assert json_result['asset_id'] == tx['id'] assert json_result['height'] == block['height'] yield from event_source.put(POISON_PILL)
def test_single_in_single_own_multiple_out_mix_own_transfer(alice, b, user_pk, user_sk): from bigchaindb.models import Transaction # CREATE divisible asset tx_create = Transaction.create([alice.public_key], [([user_pk], 100)], asset={'name': random.random()}) tx_create_signed = tx_create.sign([alice.private_key]) # TRANSFER tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([alice.public_key], 50), ([alice.public_key, alice.public_key], 50)], asset_id=tx_create.id) tx_transfer_signed = tx_transfer.sign([user_sk]) b.store_bulk_transactions([tx_create_signed]) assert tx_transfer_signed.validate(b) == tx_transfer_signed assert len(tx_transfer_signed.outputs) == 2 assert tx_transfer_signed.outputs[0].amount == 50 assert tx_transfer_signed.outputs[1].amount == 50 output_cid1 = tx_transfer_signed.outputs[1].to_dict() assert 'subconditions' in output_cid1['condition']['details'] assert len(output_cid1['condition']['details']['subconditions']) == 2 assert len(tx_transfer_signed.inputs) == 1 b.store_bulk_transactions([tx_transfer_signed]) with pytest.raises(DoubleSpend): tx_transfer_signed.validate(b)
def test_deliver_tx__double_spend_fails(b, init_chain_request): from bigchaindb import App from bigchaindb.models import Transaction from bigchaindb.common.crypto import generate_key_pair alice = generate_key_pair() bob = generate_key_pair() tx = Transaction.create([alice.public_key], [([bob.public_key], 1)])\ .sign([alice.private_key]) app = App(b) app.init_chain(init_chain_request) begin_block = RequestBeginBlock() app.begin_block(begin_block) result = app.deliver_tx(encode_tx_to_bytes(tx)) assert result.code == CodeTypeOk app.end_block(RequestEndBlock(height=99)) app.commit() assert b.get_transaction(tx.id).id == tx.id result = app.deliver_tx(encode_tx_to_bytes(tx)) assert result.code == CodeTypeError
def test_deliver_tx__valid_create_updates_db_and_emits_event(b, init_chain_request): import multiprocessing as mp from bigchaindb import App from bigchaindb.models import Transaction from bigchaindb.common.crypto import generate_key_pair alice = generate_key_pair() bob = generate_key_pair() events = mp.Queue() tx = Transaction.create([alice.public_key], [([bob.public_key], 1)])\ .sign([alice.private_key]) app = App(b, events) app.init_chain(init_chain_request) begin_block = RequestBeginBlock() app.begin_block(begin_block) result = app.deliver_tx(encode_tx_to_bytes(tx)) assert result.code == CodeTypeOk app.end_block(RequestEndBlock(height=99)) app.commit() assert b.get_transaction(tx.id).id == tx.id block_event = events.get() assert block_event.data['transactions'] == [tx]
def test_amount_error_transfer(alice, b, user_pk, user_sk): from bigchaindb.models import Transaction from bigchaindb.common.exceptions import AmountError # CREATE divisible asset tx_create = Transaction.create([alice.public_key], [([user_pk], 100)], asset={'name': random.random()}) tx_create_signed = tx_create.sign([alice.private_key]) b.store_bulk_transactions([tx_create_signed]) # TRANSFER # output amount less than input amount tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([alice.public_key], 50)], asset_id=tx_create.id) 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(), [([alice.public_key], 101)], asset_id=tx_create.id) tx_transfer_signed = tx_transfer.sign([user_sk]) with pytest.raises(AmountError): tx_transfer_signed.validate(b)
def test_get_divisble_transactions_returns_500(b, client): from bigchaindb.models import Transaction from bigchaindb.common import crypto import json TX_ENDPOINT = '/api/v1/transactions' def mine(tx_list): b.store_bulk_transactions(tx_list) alice_priv, alice_pub = crypto.generate_key_pair() bob_priv, bob_pub = crypto.generate_key_pair() carly_priv, carly_pub = crypto.generate_key_pair() create_tx = Transaction.create([alice_pub], [([alice_pub], 4)]) create_tx.sign([alice_priv]) res = client.post(TX_ENDPOINT, data=json.dumps(create_tx.to_dict())) assert res.status_code == 202 mine([create_tx]) transfer_tx = Transaction.transfer(create_tx.to_inputs(), [([alice_pub], 3), ([bob_pub], 1)], asset_id=create_tx.id) transfer_tx.sign([alice_priv]) res = client.post(TX_ENDPOINT, data=json.dumps(transfer_tx.to_dict())) assert res.status_code == 202 mine([transfer_tx]) transfer_tx_carly = Transaction.transfer([transfer_tx.to_inputs()[1]], [([carly_pub], 1)], asset_id=create_tx.id) transfer_tx_carly.sign([bob_priv]) res = client.post(TX_ENDPOINT, data=json.dumps(transfer_tx_carly.to_dict())) assert res.status_code == 202 mine([transfer_tx_carly]) asset_id = create_tx.id url = TX_ENDPOINT + '?asset_id=' + asset_id assert client.get(url).status_code == 200 assert len(client.get(url).json) == 3 url = OUTPUTS_ENDPOINT + '?public_key=' + alice_pub assert client.get(url).status_code == 200 url = OUTPUTS_ENDPOINT + '?public_key=' + bob_pub assert client.get(url).status_code == 200 url = OUTPUTS_ENDPOINT + '?public_key=' + carly_pub assert client.get(url).status_code == 200
def test_single_in_single_own_single_out_single_own_create(alice, user_pk, b): from bigchaindb.models import Transaction tx = Transaction.create([alice.public_key], [([user_pk], 100)], asset={'name': random.random()}) tx_signed = tx.sign([alice.private_key]) assert tx_signed.validate(b) == tx_signed assert len(tx_signed.outputs) == 1 assert tx_signed.outputs[0].amount == 100 assert len(tx_signed.inputs) == 1
def generate_create_and_transfer(keypair=None): if not keypair: keypair = generate_key_pair() priv_key, pub_key = keypair create_tx = Transaction.create([pub_key], [([pub_key], 10)]).sign([priv_key]) transfer_tx = Transaction.transfer( create_tx.to_inputs(), [([pub_key], 10)], asset_id=create_tx.id).sign([priv_key]) return create_tx, transfer_tx
def test_double_inclusion(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]) with pytest.raises(OperationError): b.store_bulk_transactions([tx])
def test_post_transaction_invalid_mode(b): from bigchaindb.models import Transaction from bigchaindb.common.crypto import generate_key_pair from bigchaindb.common.exceptions import ValidationError alice = generate_key_pair() tx = Transaction.create([alice.public_key], [([alice.public_key], 1)], asset=None) \ .sign([alice.private_key]).to_dict() tx = b.validate_transaction(tx) with pytest.raises(ValidationError): b.write_transaction(tx, 'nope')
def test_validation_error(b): from bigchaindb.models import Transaction from bigchaindb.common.crypto import generate_key_pair alice = generate_key_pair() tx = Transaction.create([alice.public_key], [([alice.public_key], 1)], asset=None)\ .sign([alice.private_key]).to_dict() tx['metadata'] = '' assert not b.validate_transaction(tx)
def test_multiple_in_different_transactions(alice, b, user_pk, user_sk): from bigchaindb.models import Transaction # CREATE divisible asset # `b` creates a divisible asset and assigns 50 shares to `b` and # 50 shares to `user_pk` tx_create = Transaction.create([alice.public_key], [([user_pk], 50), ([alice.public_key], 50)], asset={'name': random.random()}) tx_create_signed = tx_create.sign([alice.private_key]) # TRANSFER divisible asset # `b` transfers its 50 shares to `user_pk` # after this transaction `user_pk` will have a total of 100 shares # split across two different transactions tx_transfer1 = Transaction.transfer(tx_create.to_inputs([1]), [([user_pk], 50)], asset_id=tx_create.id) tx_transfer1_signed = tx_transfer1.sign([alice.private_key]) # TRANSFER # `user_pk` 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]), [([alice.private_key], 100)], asset_id=tx_create.id) tx_transfer2_signed = tx_transfer2.sign([user_sk]) b.store_bulk_transactions([tx_create_signed, tx_transfer1_signed]) assert tx_transfer2_signed.validate(b) == tx_transfer2_signed assert len(tx_transfer2_signed.outputs) == 1 assert tx_transfer2_signed.outputs[0].amount == 100 assert len(tx_transfer2_signed.inputs) == 2 fid0_input = tx_transfer2_signed.inputs[0].fulfills.txid fid1_input = tx_transfer2_signed.inputs[1].fulfills.txid assert fid0_input == tx_create.id assert fid1_input == tx_transfer1.id
def test_muiltiple_in_mix_own_multiple_out_mix_own_transfer( alice, b, user_pk, user_sk): from bigchaindb.models import Transaction from bigchaindb.common.transaction import _fulfillment_to_details # CREATE divisible asset tx_create = Transaction.create([alice.public_key], [([user_pk], 50), ([user_pk, alice.public_key], 50)], asset={'name': random.random()}) tx_create_signed = tx_create.sign([alice.private_key]) # TRANSFER tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([alice.public_key], 50), ([alice.public_key, user_pk], 50)], asset_id=tx_create.id) tx_transfer_signed = tx_transfer.sign([alice.private_key, user_sk]) b.store_bulk_transactions([tx_create_signed, tx_transfer_signed]) assert tx_transfer_signed.validate(b) == tx_transfer_signed assert len(tx_transfer_signed.outputs) == 2 assert tx_transfer_signed.outputs[0].amount == 50 assert tx_transfer_signed.outputs[1].amount == 50 assert len(tx_transfer_signed.inputs) == 2 cond_cid0 = tx_transfer_signed.outputs[0].to_dict() cond_cid1 = tx_transfer_signed.outputs[1].to_dict() assert 'subconditions' not in cond_cid0['condition']['details'] assert 'subconditions' in cond_cid1['condition']['details'] assert len(cond_cid1['condition']['details']['subconditions']) == 2 ffill_fid0 = _fulfillment_to_details( tx_transfer_signed.inputs[0].fulfillment) ffill_fid1 = _fulfillment_to_details( tx_transfer_signed.inputs[1].fulfillment) assert 'subconditions' not in ffill_fid0 assert 'subconditions' in ffill_fid1 assert len(ffill_fid1['subconditions']) == 2
def test_upsert_validator(b, alice): from bigchaindb.backend.query import VALIDATOR_UPDATE_ID from bigchaindb.backend import query, connect from bigchaindb.models import Transaction from bigchaindb.tendermint_utils import public_key_to_base64 import time conn = connect() power = 1 public_key = '9B3119650DF82B9A5D8A12E38953EA47475C09F0C48A4E6A0ECE182944B24403' validator = { 'pub_key': { 'type': 'AC26791624DE60', 'data': public_key }, 'power': power } validator_update = { 'validator': validator, 'update_id': VALIDATOR_UPDATE_ID } query.store_validator_update(conn, deepcopy(validator_update)) tx = Transaction.create([alice.public_key], [([alice.public_key], 1)], asset=None)\ .sign([alice.private_key]) code, message = b.write_transaction(tx, 'broadcast_tx_commit') assert code == 202 time.sleep(5) validators = b.get_validators() validators = [(v['pub_key']['value'], v['voting_power']) for v in validators] public_key64 = public_key_to_base64(public_key) assert ((public_key64, str(power)) in validators)
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 == []
def test_get_spent_issue_1271(b, alice, bob, carol): from bigchaindb.models import Transaction tx_1 = Transaction.create( [carol.public_key], [([carol.public_key], 8)], ).sign([carol.private_key]) tx_2 = Transaction.transfer( tx_1.to_inputs(), [([bob.public_key], 2), ([alice.public_key], 2), ([carol.public_key], 4)], asset_id=tx_1.id, ).sign([carol.private_key]) tx_3 = Transaction.transfer( tx_2.to_inputs()[2:3], [([alice.public_key], 1), ([carol.public_key], 3)], asset_id=tx_1.id, ).sign([carol.private_key]) tx_4 = Transaction.transfer( tx_2.to_inputs()[1:2] + tx_3.to_inputs()[0:1], [([bob.public_key], 3)], asset_id=tx_1.id, ).sign([alice.private_key]) tx_5 = Transaction.transfer( tx_2.to_inputs()[0:1], [([alice.public_key], 2)], asset_id=tx_1.id, ).sign([bob.private_key]) block_5 = b.create_block([tx_1, tx_2, tx_3, tx_4, tx_5]) b.write_block(block_5) assert b.get_spent(tx_2.id, 0) == tx_5 assert not b.get_spent(tx_5.id, 0) assert b.get_outputs_filtered(alice.public_key) assert b.get_outputs_filtered(alice.public_key, spent=False)
def test_deliver_transfer_tx__double_spend_fails(b): from bigchaindb import App from bigchaindb.models import Transaction from bigchaindb.common.crypto import generate_key_pair app = App(b) app.init_chain(['ignore']) begin_block = RequestBeginBlock() app.begin_block(begin_block) alice = generate_key_pair() bob = generate_key_pair() carly = generate_key_pair() asset = {'msg': 'live long and prosper'} tx = Transaction.create([alice.public_key], [([alice.public_key], 1)], asset=asset)\ .sign([alice.private_key]) result = app.deliver_tx(encode_tx_to_bytes(tx)) assert result.code == CodeTypeOk tx_transfer = Transaction.transfer(tx.to_inputs(), [([bob.public_key], 1)], asset_id=tx.id)\ .sign([alice.private_key]) result = app.deliver_tx(encode_tx_to_bytes(tx_transfer)) assert result.code == CodeTypeOk double_spend = Transaction.transfer(tx.to_inputs(), [([carly.public_key], 1)], asset_id=tx.id)\ .sign([alice.private_key]) result = app.deliver_tx(encode_tx_to_bytes(double_spend)) assert result.code == CodeTypeError
def test_couple_assets(self, b): from bigchaindb.models import Block, Transaction assets = [ { 'msg': '1' }, { 'msg': '2' }, { 'msg': '3' }, ] txs = [] # create 3 assets for asset in assets: tx = Transaction.create([b.me], [([b.me], 1)], asset=asset) tx.sign([b.me_private]) txs.append(tx) # create a `TRANSFER` transaction. # the asset in `TRANSFER` transactions is not extracted tx = Transaction.transfer(txs[0].to_inputs(), [([b.me], 1)], asset_id=txs[0].id) tx.sign([b.me_private]) txs.append(tx) # create the block block = Block(txs) # decouple assets assets_from_block, block_dict = block.decouple_assets() # reconstruct the block block_dict_reconstructed = Block.couple_assets(block_dict, assets_from_block) # check that the reconstructed block is the same as the original block assert block == Block.from_dict(block_dict_reconstructed)
def test_upsert_validator(b, alice): from bigchaindb.backend.query import VALIDATOR_UPDATE_ID from bigchaindb.backend import query, connect from bigchaindb.models import Transaction from bigchaindb.tendermint.utils import public_key_to_base64 import time conn = connect() public_key = '1718D2DBFF00158A0852A17A01C78F4DCF3BA8E4FB7B8586807FAC182A535034' power = 1 validator = { 'pub_key': { 'type': 'AC26791624DE60', 'data': public_key }, 'power': power } validator_update = { 'validator': validator, 'update_id': VALIDATOR_UPDATE_ID } query.store_validator_update(conn, deepcopy(validator_update)) tx = Transaction.create([alice.public_key], [([alice.public_key], 1)], asset=None)\ .sign([alice.private_key]) code, message = b.write_transaction(tx, 'broadcast_tx_commit') assert code == 202 time.sleep(5) validators = b.get_validators() validators = [(v['pub_key']['value'], v['voting_power']) for v in validators] public_key64 = public_key_to_base64(public_key) assert ((public_key64, power) in validators)
def test_invalid_id_tx_in_block_voting(monkeypatch, b, user_pk, genesis_block): from bigchaindb.backend import query from bigchaindb.common import crypto, utils from bigchaindb.models import Transaction from bigchaindb.pipelines import vote inpipe = Pipe() outpipe = Pipe() monkeypatch.setattr('time.time', lambda: 1111111111) vote_pipeline = vote.create_pipeline() vote_pipeline.setup(indata=inpipe, outdata=outpipe) # NOTE: `tx` is invalid, because its id is not corresponding to its content tx = Transaction.create([b.me], [([b.me], 1)]) tx = tx.sign([b.me_private]) block = b.create_block([tx]).to_dict() block['block']['transactions'][0]['id'] = 'an invalid tx id' inpipe.put(block) vote_pipeline.start() vote_out = outpipe.get() vote_pipeline.terminate() vote_rs = query.get_votes_by_block_id_and_voter(b.connection, block['id'], b.me) vote_doc = vote_rs.next() assert vote_out['vote'] == vote_doc['vote'] assert vote_doc['vote'] == { 'voting_for_block': block['id'], 'previous_block': genesis_block.id, 'is_block_valid': False, 'invalid_reason': None, 'timestamp': '1111111111' } serialized_vote = utils.serialize(vote_doc['vote']).encode() assert vote_doc['node_pubkey'] == b.me assert crypto.PublicKey(b.me).verify(serialized_vote, vote_doc['signature']) is True
def test_post_wrong_asset_division_transfer_returns_400(b, client, user_pk): from bigchaindb.models import Transaction from bigchaindb.common.exceptions import AmountError priv_key, pub_key = crypto.generate_key_pair() create_tx = Transaction.create([pub_key], [([pub_key], 10)], asset={'test': 'asset'}).sign([priv_key]) res = client.post(TX_ENDPOINT + '?mode=commit', data=json.dumps(create_tx.to_dict())) assert res.status_code == 202 transfer_tx = Transaction.transfer(create_tx.to_inputs(), [([pub_key], 20)], # 20 > 10 asset_id=create_tx.id).sign([priv_key]) res = client.post(TX_ENDPOINT + '?mode=commit', data=json.dumps(transfer_tx.to_dict())) expected_error_message = \ f'Invalid transaction ({AmountError.__name__}): ' + \ 'The amount used in the inputs `10` needs to be same as the amount used in the outputs `20`' assert res.status_code == 400 assert res.json['message'] == expected_error_message
def test_post_create_transaction_with_invalid_schema(mock_logger, client): from bigchaindb.models import Transaction user_priv, user_pub = crypto.generate_key_pair() tx = Transaction.create([user_pub], [([user_pub], 1)]).to_dict() del tx['version'] ed25519 = Ed25519Sha256(public_key=base58.b58decode(user_pub)) message = json.dumps( tx, sort_keys=True, separators=(',', ':'), ensure_ascii=False, ).encode() ed25519.sign(message, base58.b58decode(user_priv)) tx['inputs'][0]['fulfillment'] = ed25519.serialize_uri() tx['id'] = sha3_256( json.dumps( tx, sort_keys=True, separators=(',', ':'), ensure_ascii=False, ).encode(), ).hexdigest() res = client.post(TX_ENDPOINT, data=json.dumps(tx)) expected_status_code = 400 expected_error_message = ( "Invalid transaction schema: 'version' is a required property") assert res.status_code == expected_status_code assert res.json['message'] == expected_error_message assert mock_logger.error.called assert ( 'HTTP API error: %(status)s - %(method)s:%(path)s - %(message)s' in mock_logger.error.call_args[0] ) assert ( { 'message': expected_error_message, 'status': expected_status_code, 'method': 'POST', 'path': TX_ENDPOINT } in mock_logger.error.call_args[0] )
def test_sign_block(self, b): from bigchaindb.common.crypto import PrivateKey, PublicKey from bigchaindb.common.utils import gen_timestamp, serialize from bigchaindb.models import Block, Transaction transactions = [Transaction.create([b.me], [([b.me], 1)])] timestamp = gen_timestamp() voters = ['Qaaa', 'Qbbb'] expected_block = { 'timestamp': timestamp, 'transactions': [tx.to_dict() for tx in transactions], 'node_pubkey': b.me, 'voters': voters, } expected_block_serialized = serialize(expected_block).encode() expected = PrivateKey(b.me_private).sign(expected_block_serialized) block = Block(transactions, b.me, timestamp, voters) block = block.sign(b.me_private) assert block.signature == expected.decode() public_key = PublicKey(b.me) assert public_key.verify(expected_block_serialized, block.signature)
def test_get_spent_transaction_critical_double_spend(b, alice, bob, carol): from bigchaindb.models import Transaction from bigchaindb.exceptions import CriticalDoubleSpend from bigchaindb.common.exceptions import DoubleSpend asset = {'test': 'asset'} tx = Transaction.create([alice.public_key], [([alice.public_key], 1)], asset=asset)\ .sign([alice.private_key]) tx_transfer = Transaction.transfer(tx.to_inputs(), [([bob.public_key], 1)], asset_id=tx.id)\ .sign([alice.private_key]) double_spend = Transaction.transfer(tx.to_inputs(), [([carol.public_key], 1)], asset_id=tx.id)\ .sign([alice.private_key]) b.store_bulk_transactions([tx]) assert b.get_spent(tx.id, tx_transfer.inputs[0].fulfills.output, [tx_transfer]) with pytest.raises(DoubleSpend): b.get_spent(tx.id, tx_transfer.inputs[0].fulfills.output, [tx_transfer, double_spend]) b.store_bulk_transactions([tx_transfer]) with pytest.raises(DoubleSpend): b.get_spent(tx.id, tx_transfer.inputs[0].fulfills.output, [double_spend]) b.store_bulk_transactions([double_spend]) with pytest.raises(CriticalDoubleSpend): b.get_spent(tx.id, tx_transfer.inputs[0].fulfills.output)
def inputs_shared(user_pk, user2_pk, genesis_block): from bigchaindb.models import Transaction # create blocks with transactions for `USER` to spend prev_block_id = genesis_block.id for block in range(4): transactions = [ Transaction.create( [b.me], [user_pk, user2_pk], metadata={ 'msg': random.random() }, ).sign([b.me_private]) for _ in range(10) ] block = b.create_block(transactions) b.write_block(block) # vote the blocks valid, so that the inputs are valid vote = b.vote(block.id, prev_block_id, True) prev_block_id = block.id b.write_vote(vote)
def test_cant_spend_same_input_twice_in_tx(b, alice): """Recreate duplicated fulfillments bug https://github.com/bigchaindb/bigchaindb/issues/1099 """ from bigchaindb.models import Transaction from bigchaindb.common.exceptions import DoubleSpend # create a divisible asset tx_create = Transaction.create([alice.public_key], [([alice.public_key], 100)]) tx_create_signed = tx_create.sign([alice.private_key]) assert b.validate_transaction(tx_create_signed) == tx_create_signed b.store_bulk_transactions([tx_create_signed]) # Create a transfer transaction with duplicated fulfillments dup_inputs = tx_create.to_inputs() + tx_create.to_inputs() tx_transfer = Transaction.transfer(dup_inputs, [([alice.public_key], 200)], asset_id=tx_create.id) tx_transfer_signed = tx_transfer.sign([alice.private_key]) with pytest.raises(DoubleSpend): tx_transfer_signed.validate(b)
def test_get_metadata_tendermint(client, b, alice): from bigchaindb.models import Transaction # test returns empty list when no assets are found res = client.get(METADATA_ENDPOINT + '?search=abc') assert res.json == [] assert res.status_code == 200 # create asset asset = {'msg': 'abc'} metadata = {'key': 'my_meta'} tx = Transaction.create([alice.public_key], [([alice.public_key], 1)], metadata=metadata, asset=asset).sign([alice.private_key]) b.store_bulk_transactions([tx]) # test that metadata is returned res = client.get(METADATA_ENDPOINT + '?search=my_meta') assert res.status_code == 200 assert len(res.json) == 1 assert res.json[0] == {'metadata': {'key': 'my_meta'}, 'id': tx.id}
def test_sum_amount(alice, b, user_pk, user_sk): from bigchaindb.models import Transaction # CREATE divisible asset with 3 outputs with amount 1 tx_create = Transaction.create([alice.public_key], [([user_pk], 1), ([user_pk], 1), ([user_pk], 1)], asset={'name': random.random()}) tx_create_signed = tx_create.sign([alice.private_key]) # create a transfer transaction with one output and check if the amount # is 3 tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([alice.public_key], 3)], asset_id=tx_create.id) tx_transfer_signed = tx_transfer.sign([user_sk]) b.store_bulk_transactions([tx_create_signed, tx_transfer_signed]) assert tx_transfer_signed.validate(b) == tx_transfer_signed assert len(tx_transfer_signed.outputs) == 1 assert tx_transfer_signed.outputs[0].amount == 3
def test_post_transaction_responses(tendermint_ws_url, b): from bigchaindb.common.crypto import generate_key_pair from bigchaindb.models import Transaction alice = generate_key_pair() bob = generate_key_pair() tx = Transaction.create([alice.public_key], [([alice.public_key], 1)], asset=None)\ .sign([alice.private_key]) code, message = b.write_transaction(tx, 'broadcast_tx_commit') assert code == 202 tx_transfer = Transaction.transfer(tx.to_inputs(), [([bob.public_key], 1)], asset_id=tx.id)\ .sign([alice.private_key]) code, message = b.write_transaction(tx_transfer, 'broadcast_tx_commit') assert code == 202
def test_deliver_tx__valid_create_updates_db(b): from bigchaindb.tendermint import App from bigchaindb.models import Transaction from bigchaindb.common.crypto import generate_key_pair alice = generate_key_pair() bob = generate_key_pair() tx = Transaction.create([alice.public_key], [([bob.public_key], 1)])\ .sign([alice.private_key]) app = App(b) app.init_chain(['ignore']) app.begin_block('ignore') result = app.deliver_tx(encode_tx_to_bytes(tx)) assert result.is_ok() app.end_block(99) app.commit() assert b.get_transaction(tx.id).id == tx.id
def test_get_owned_ids_single_tx_multiple_outputs(self, b, user_sk, user_pk, 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() # 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]) b.store_bulk_transactions([tx_create_signed]) # get input 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) ] assert owned_inputs_user1 == expected_owned_inputs_user1 assert owned_inputs_user2 == [] # transfer divisible asset divided in two outputs tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([user2_pk], 1), ([user2_pk], 1)], asset_id=tx_create.id) tx_transfer_signed = tx_transfer.sign([user_sk]) b.store_bulk_transactions([tx_transfer_signed]) 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) ]
def test_multiple_in_single_own_single_out_single_own_transfer( alice, b, user_pk, user_sk): from bigchaindb.models import Transaction # CREATE divisible asset tx_create = Transaction.create([alice.public_key], [([user_pk], 50), ([user_pk], 50)], asset={'name': random.random()}) tx_create_signed = tx_create.sign([alice.private_key]) # TRANSFER tx_transfer = Transaction.transfer(tx_create.to_inputs(), [([alice.public_key], 100)], asset_id=tx_create.id) tx_transfer_signed = tx_transfer.sign([user_sk]) b.store_bulk_transactions([tx_create_signed, tx_transfer_signed]) assert tx_transfer_signed.validate(b) assert len(tx_transfer_signed.outputs) == 1 assert tx_transfer_signed.outputs[0].amount == 100 assert len(tx_transfer_signed.inputs) == 2
def test_get_asset_ids(self, b): from bigchaindb.models import Block, Transaction assets = [ { 'msg': '1' }, { 'msg': '2' }, { 'msg': '3' }, ] txs = [] # create 3 assets for asset in assets: tx = Transaction.create([b.me], [([b.me], 1)], asset=asset) tx.sign([b.me_private]) txs.append(tx) # create a `TRANSFER` transaction. # the asset in `TRANSFER` transactions is not extracted tx = Transaction.transfer(txs[0].to_inputs(), [([b.me], 1)], asset_id=txs[0].id) tx.sign([b.me_private]) txs.append(tx) # create the block block = Block(txs) # decouple assets assets_from_block, block_dict = block.decouple_assets() # get the asset_ids and check that they are the same as the `CREATE` # transactions asset_ids = Block.get_asset_ids(block_dict) assert asset_ids == [tx.id for tx in txs[:-1]]
def test_filter_spent_outputs(b, user_pk, user_sk): out = [([user_pk], 1)] tx1 = Transaction.create([user_pk], out * 3) tx1.sign([user_sk]) # There are 3 inputs inputs = tx1.to_inputs() # Each spent individually tx2 = Transaction.transfer([inputs[0]], out, tx1.id) tx2.sign([user_sk]) tx3 = Transaction.transfer([inputs[1]], out, tx1.id) tx3.sign([user_sk]) tx4 = Transaction.transfer([inputs[2]], out, tx1.id) tx4.sign([user_sk]) # The CREATE and first TRANSFER are valid. tx2 produces a new unspent. for tx in [tx1, tx2]: block = Block([tx]) b.write_block(block) b.write_vote(b.vote(block.id, '', True)) # The second TRANSFER is invalid. inputs[1] remains unspent. block = Block([tx3]) b.write_block(block) b.write_vote(b.vote(block.id, '', False)) # The third TRANSFER is undecided. It procuces a new unspent. block = Block([tx4]) b.write_block(block) outputs = b.fastquery.get_outputs_by_public_key(user_pk) unspents = b.fastquery.filter_spent_outputs(outputs) assert set(unspents) == { inputs[1].fulfills, tx2.to_inputs()[0].fulfills, tx4.to_inputs()[0].fulfills }
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 # create a new users user2_sk, user2_pk = crypto.generate_key_pair() # create a divisible asset with 3 outputs tx_create = Transaction.create([alice.public_key], [([user_pk], 1), ([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) owned_inputs_user1 = b.get_owned_ids(user_pk) # check spents for input_tx in owned_inputs_user1: assert b.get_spent(input_tx.txid, input_tx.output) is None # transfer the first 2 inputs tx_transfer = Transaction.transfer(tx_create.to_inputs()[:2], [([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) # check that used inputs are marked as spent for ffill in tx_create.to_inputs()[:2]: spent_tx = b.get_spent(ffill.fulfills.txid, ffill.fulfills.output) assert spent_tx == tx_transfer_signed # check if remaining transaction that was unspent is also perceived # spendable by BigchainDB assert b.get_spent(tx_create.to_inputs()[2].fulfills.txid, 2) is None
def test_store_pre_commit_state_in_end_block(b, alice, init_chain_request): from bigchaindb import App from bigchaindb.backend import query from bigchaindb.models import Transaction tx = Transaction.create([alice.public_key], [([alice.public_key], 1)], asset={'msg': 'live long and prosper'})\ .sign([alice.private_key]) app = App(b) app.init_chain(init_chain_request) begin_block = RequestBeginBlock() app.begin_block(begin_block) app.deliver_tx(encode_tx_to_bytes(tx)) app.end_block(RequestEndBlock(height=99)) resp = query.get_pre_commit_state(b.connection) assert resp['height'] == 99 assert resp['transactions'] == [tx.id] app.begin_block(begin_block) app.deliver_tx(encode_tx_to_bytes(tx)) app.end_block(RequestEndBlock(height=100)) resp = query.get_pre_commit_state(b.connection) assert resp['height'] == 100 assert resp['transactions'] == [tx.id] # simulate a chain migration and assert the height is shifted b.store_abci_chain(100, 'new-chain') app = App(b) app.begin_block(begin_block) app.deliver_tx(encode_tx_to_bytes(tx)) app.end_block(RequestEndBlock(height=1)) resp = query.get_pre_commit_state(b.connection) assert resp['height'] == 101 assert resp['transactions'] == [tx.id]
def test_block_serialization(self, b, alice): from bigchaindb.common.crypto import hash_data from bigchaindb.common.utils import gen_timestamp, serialize from bigchaindb.models import Block, Transaction transactions = [ Transaction.create([alice.public_key], [([alice.public_key], 1)]) ] timestamp = gen_timestamp() expected_block = { 'timestamp': timestamp, 'transactions': [tx.to_dict() for tx in transactions], 'node_pubkey': alice.public_key, } expected = { 'id': hash_data(serialize(expected_block)), 'block': expected_block, 'signature': None, } block = Block(transactions, alice.public_key, timestamp) assert block.to_dict() == expected
def test_sign_block(self, b, alice): from bigchaindb.common.crypto import PrivateKey, PublicKey from bigchaindb.common.utils import gen_timestamp, serialize from bigchaindb.models import Block, Transaction transactions = [ Transaction.create([alice.public_key], [([alice.public_key], 1)]) ] timestamp = gen_timestamp() expected_block = { 'timestamp': timestamp, 'transactions': [tx.to_dict() for tx in transactions], 'node_pubkey': alice.public_key, } expected_block_serialized = serialize(expected_block).encode() expected = PrivateKey( alice.private_key).sign(expected_block_serialized) block = Block(transactions, alice.public_key, timestamp) block = block.sign(alice.private_key) assert block.signature == expected.decode() public_key = PublicKey(alice.public_key) assert public_key.verify(expected_block_serialized, block.signature)
def test_get_assets_tendermint(client, tb, alice): from bigchaindb.models import Transaction # test returns empty list when no assets are found res = client.get(ASSETS_ENDPOINT + '?search=abc') assert res.json == [] assert res.status_code == 200 # create asset asset = {'msg': 'abc'} tx = Transaction.create([alice.public_key], [([alice.public_key], 1)], asset=asset).sign([alice.private_key]) tb.store_transaction(tx) # test that asset is returned res = client.get(ASSETS_ENDPOINT + '?search=abc') assert res.status_code == 200 assert len(res.json) == 1 assert res.json[0] == { 'data': {'msg': 'abc'}, 'id': tx.id }
def test_get_last_voted_block_cyclic_blockchain(self, b, monkeypatch, alice): from bigchaindb.common.crypto import PrivateKey from bigchaindb.common.exceptions import CyclicBlockchainError from bigchaindb.common.utils import serialize from bigchaindb.models import Transaction tx = Transaction.create([alice.public_key], [([alice.public_key], 1)]) tx = tx.sign([alice.private_key]) monkeypatch.setattr('time.time', lambda: 1) block1 = b.create_block([tx]) b.write_block(block1) # Manipulate vote to create a cyclic Blockchain vote = b.vote(block1.id, b.get_last_voted_block().id, True) vote['vote']['previous_block'] = block1.id vote_data = serialize(vote['vote']) vote['signature'] = PrivateKey(alice.private_key).sign( vote_data.encode()) b.write_vote(vote) with pytest.raises(CyclicBlockchainError): b.get_last_voted_block()
def test_block_serialization(self, b): from bigchaindb.common.crypto import hash_data from bigchaindb.common.utils import gen_timestamp, serialize from bigchaindb.models import Block, Transaction transactions = [Transaction.create([b.me], [([b.me], 1)])] timestamp = gen_timestamp() voters = ['Qaaa', 'Qbbb'] expected_block = { 'timestamp': timestamp, 'transactions': [tx.to_dict() for tx in transactions], 'node_pubkey': b.me, 'voters': voters, } expected = { 'id': hash_data(serialize(expected_block)), 'block': expected_block, 'signature': None, } block = Block(transactions, b.me, timestamp, voters) assert block.to_dict() == expected