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_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_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_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_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_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)
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_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_check_tx__unsigned_create_is_error(b): 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)]) app = App(b) result = app.check_tx(encode_tx_to_bytes(tx)) assert result.code == CodeTypeError
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_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 run_start(args): """Start the processes to run the node""" logger.info('BigchainDB Version {}'.format(bigchaindb.__version__)) bigchaindb.config_utils.autoconfigure(filename=args.config, force=True) if args.allow_temp_keypair: if not (bigchaindb.config['keypair']['private'] or bigchaindb.config['keypair']['public']): private_key, public_key = crypto.generate_key_pair() bigchaindb.config['keypair']['private'] = private_key bigchaindb.config['keypair']['public'] = public_key else: logger.warning('Keypair found, no need to create one on the fly.') if args.start_rethinkdb: try: proc = utils.start_rethinkdb() except StartupError as e: sys.exit('Error starting RethinkDB, reason is: {}'.format(e)) logger.info('RethinkDB started with PID %s' % proc.pid) try: db.init() except DatabaseAlreadyExists: pass except KeypairNotFoundException: sys.exit("Can't start BigchainDB, no keypair found. " 'Did you run `bigchaindb configure`?') logger.info('Starting BigchainDB main process with public key %s', bigchaindb.config['keypair']['public']) processes.start()
def test_eventify_block_works_with_any_transaction(): from bigchaindb.web.websocket_server import eventify_block from bigchaindb.common.crypto import generate_key_pair from bigchaindb.lib import Transaction alice = generate_key_pair() tx = Transaction.create([alice.public_key], [([alice.public_key], 1)])\ .sign([alice.private_key]) tx_transfer = Transaction.transfer(tx.to_inputs(), [([alice.public_key], 1)], asset_id=tx.id)\ .sign([alice.private_key]) block = {'height': 1, 'transactions': [tx, tx_transfer]} expected_events = [{ 'height': 1, 'asset_id': tx.id, 'transaction_id': tx.id }, { 'height': 1, 'asset_id': tx_transfer.asset['id'], 'transaction_id': tx_transfer.id }] for event, expected in zip(eventify_block(block), expected_events): assert event == expected
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_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_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_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 run_start(args): """Start the processes to run the node""" logger.info('BigchainDB Version %s', bigchaindb.__version__) if args.allow_temp_keypair: if not (bigchaindb.config['keypair']['private'] or bigchaindb.config['keypair']['public']): private_key, public_key = crypto.generate_key_pair() bigchaindb.config['keypair']['private'] = private_key bigchaindb.config['keypair']['public'] = public_key else: logger.warning('Keypair found, no need to create one on the fly.') if args.start_rethinkdb: try: proc = utils.start_rethinkdb() except StartupError as e: sys.exit(RETHINKDB_STARTUP_ERROR.format(e)) logger.info('RethinkDB started with PID %s' % proc.pid) try: _run_init() except DatabaseAlreadyExists: pass except KeypairNotFoundException: sys.exit(CANNOT_START_KEYPAIR_NOT_FOUND) logger.info('Starting BigchainDB main process with public key %s', bigchaindb.config['keypair']['public']) processes.start()
def test_upsert_validator_delegate_election_vote(b_mock, valid_upsert_validator_election, ed25519_node_keys): alice = generate_key_pair() b_mock.store_bulk_transactions([valid_upsert_validator_election]) input0 = valid_upsert_validator_election.to_inputs()[0] votes = valid_upsert_validator_election.outputs[0].amount public_key0 = input0.owners_before[0] key0 = ed25519_node_keys[public_key0] delegate_vote = Vote.generate([input0], [([alice.public_key], 3), ([key0.public_key], votes-3)], election_id=valid_upsert_validator_election.id)\ .sign([key0.private_key]) assert delegate_vote.validate(b_mock) b_mock.store_bulk_transactions([delegate_vote]) election_pub_key = ValidatorElection.to_public_key(valid_upsert_validator_election.id) alice_votes = delegate_vote.to_inputs()[0] alice_casted_vote = Vote.generate([alice_votes], [([election_pub_key], 3)], election_id=valid_upsert_validator_election.id)\ .sign([alice.private_key]) assert alice_casted_vote.validate(b_mock) key0_votes = delegate_vote.to_inputs()[1] key0_casted_vote = Vote.generate([key0_votes], [([election_pub_key], votes-3)], election_id=valid_upsert_validator_election.id)\ .sign([key0.private_key]) assert key0_casted_vote.validate(b_mock)
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_upsert_validator_invalid_inputs_election(b_mock, new_validator, node_key): from bigchaindb.common.crypto import generate_key_pair alice = generate_key_pair() voters = ValidatorElection.recipients(b_mock) election = ValidatorElection.generate([node_key.public_key, alice.public_key], voters, new_validator, None).sign([node_key.private_key, alice.private_key]) with pytest.raises(MultipleInputsError): election.validate(b_mock)
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 run_configure(args, skip_if_exists=False): """Run a script to configure the current node. Args: skip_if_exists (bool): skip the function if a config file already exists """ config_path = args.config or bigchaindb.config_utils.CONFIG_DEFAULT_PATH config_file_exists = False # if the config path is `-` then it's stdout if config_path != "-": config_file_exists = os.path.exists(config_path) if config_file_exists and skip_if_exists: return if config_file_exists and not args.yes: want = input( "Config file `{}` exists, do you want to override it? " "(cannot be undone) [y/N]: ".format(config_path) ) if want != "y": return conf = copy.deepcopy(bigchaindb.config) # Patch the default configuration with the new values conf = bigchaindb.config_utils.update(conf, bigchaindb.config_utils.env_config(bigchaindb.config)) print("Generating keypair", file=sys.stderr) conf["keypair"]["private"], conf["keypair"]["public"] = crypto.generate_key_pair() if not args.yes: for key in ("bind",): val = conf["server"][key] conf["server"][key] = input("API Server {}? (default `{}`): ".format(key, val)) or val for key in ("host", "port", "name"): val = conf["database"][key] conf["database"][key] = input("Database {}? (default `{}`): ".format(key, val)) or val for key in ("host", "port", "rate"): val = conf["statsd"][key] conf["statsd"][key] = input("Statsd {}? (default `{}`): ".format(key, val)) or val val = conf["backlog_reassign_delay"] conf["backlog_reassign_delay"] = ( input("Stale transaction reassignment delay (in seconds)? (default `{}`): ".format(val)) or val ) if config_path != "-": bigchaindb.config_utils.write_config(conf, config_path) else: print(json.dumps(conf, indent=4, sort_keys=True)) print("Configuration written to {}".format(config_path), file=sys.stderr) print("Ready to go!", file=sys.stderr)
def test_deliver_transfer_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 app = App(b) app.init_chain(init_chain_request) 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_single_owner_before_multiple_owners_after_single_input(self, b, user_sk, user_pk, inputs): 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() tx_link = b.fastquery.get_outputs_by_public_key(user_pk).pop() input_tx = b.get_transaction(tx_link.txid) tx = Transaction.transfer(input_tx.to_inputs(), [([user2_pk, user3_pk], 1)], asset_id=input_tx.id) tx = tx.sign([user_sk]) tx.validate(b) assert len(tx.inputs) == 1 assert len(tx.outputs) == 1
def test_get_spending_transactions_multiple_inputs(): from bigchaindb.backend import connect, query from bigchaindb.models import Transaction from bigchaindb.common.crypto import generate_key_pair conn = connect() (alice_sk, alice_pk) = generate_key_pair() (bob_sk, bob_pk) = generate_key_pair() (carol_sk, carol_pk) = generate_key_pair() out = [([alice_pk], 9)] tx1 = Transaction.create([alice_pk], out).sign([alice_sk]) inputs1 = tx1.to_inputs() tx2 = Transaction.transfer([inputs1[0]], [([alice_pk], 6), ([bob_pk], 3)], tx1.id).sign([alice_sk]) inputs2 = tx2.to_inputs() tx3 = Transaction.transfer([inputs2[0]], [([bob_pk], 3), ([carol_pk], 3)], tx1.id).sign([alice_sk]) inputs3 = tx3.to_inputs() tx4 = Transaction.transfer([inputs2[1], inputs3[0]], [([carol_pk], 6)], tx1.id).sign([bob_sk]) txns = [deepcopy(tx.to_dict()) for tx in [tx1, tx2, tx3, tx4]] conn.db.transactions.insert_many(txns) links = [ ({'transaction_id': tx2.id, 'output_index': 0}, 1, [tx3.id]), ({'transaction_id': tx2.id, 'output_index': 1}, 1, [tx4.id]), ({'transaction_id': tx3.id, 'output_index': 0}, 1, [tx4.id]), ({'transaction_id': tx3.id, 'output_index': 1}, 0, None), ] for l, num, match in links: txns = list(query.get_spending_transactions(conn, [l])) assert len(txns) == num if len(txns): assert [tx['id'] for tx in txns] == match
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_validation_worker_process_multiple_transactions(b): import multiprocessing as mp from bigchaindb.parallel_validation import ValidationWorker, RESET, EXIT keypair = generate_key_pair() create_tx, transfer_tx = generate_create_and_transfer(keypair) double_spend = Transaction.transfer( create_tx.to_inputs(), [([keypair.public_key], 10)], asset_id=create_tx.id).sign([keypair.private_key]) in_queue, results_queue = mp.Queue(), mp.Queue() vw = ValidationWorker(in_queue, results_queue) # Note: in the following instructions, the worker will encounter two # `RESET` messages, and an `EXIT` message. When a worker processes a # `RESET` message, it forgets all transactions it has validated. This allow # us to re-validate the same transactions. This won't happen in real life, # but it's quite handy to check if the worker actually forgot about the # past transactions (if not, it will return `False` because the # transactions look like a double spend). # `EXIT` makes the worker to stop the infinite loop. in_queue.put((0, create_tx.to_dict())) in_queue.put((10, transfer_tx.to_dict())) in_queue.put((20, double_spend.to_dict())) in_queue.put(RESET) in_queue.put((0, create_tx.to_dict())) in_queue.put((5, transfer_tx.to_dict())) in_queue.put(RESET) in_queue.put((20, create_tx.to_dict())) in_queue.put((25, double_spend.to_dict())) in_queue.put((30, transfer_tx.to_dict())) in_queue.put(EXIT) vw.run() assert results_queue.get() == (0, create_tx) assert results_queue.get() == (10, transfer_tx) assert results_queue.get() == (20, False) assert results_queue.get() == (0, create_tx) assert results_queue.get() == (5, transfer_tx) assert results_queue.get() == (20, create_tx) assert results_queue.get() == (25, double_spend) assert results_queue.get() == (30, False)
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_valid_election_votes_received(b_mock, valid_election, ed25519_node_keys): alice = generate_key_pair() b_mock.store_bulk_transactions([valid_election]) assert valid_election.get_commited_votes(b_mock) == 0 input0 = valid_election.to_inputs()[0] votes = valid_election.outputs[0].amount public_key0 = input0.owners_before[0] key0 = ed25519_node_keys[public_key0] # delegate some votes to alice delegate_vote = ValidatorElectionVote.generate([input0], [([alice.public_key], 4), ([key0.public_key], votes-4)], election_id=valid_election.id)\ .sign([key0.private_key]) b_mock.store_bulk_transactions([delegate_vote]) assert valid_election.get_commited_votes(b_mock) == 0 election_public_key = ValidatorElection.to_public_key(valid_election.id) alice_votes = delegate_vote.to_inputs()[0] key0_votes = delegate_vote.to_inputs()[1] alice_casted_vote = ValidatorElectionVote.generate([alice_votes], [([election_public_key], 2), ([alice.public_key], 2)], election_id=valid_election.id)\ .sign([alice.private_key]) assert alice_casted_vote.validate(b_mock) b_mock.store_bulk_transactions([alice_casted_vote]) # Check if the delegated vote is count as valid vote assert valid_election.get_commited_votes(b_mock) == 2 key0_casted_vote = ValidatorElectionVote.generate([key0_votes], [([election_public_key], votes-4)], election_id=valid_election.id)\ .sign([key0.private_key]) assert key0_casted_vote.validate(b_mock) b_mock.store_bulk_transactions([key0_casted_vote]) assert valid_election.get_commited_votes(b_mock) == votes - 2
def test_write_and_post_transaction(mock_post, b): from bigchaindb.models import Transaction from bigchaindb.common.crypto import generate_key_pair from bigchaindb.tendermint.utils import encode_transaction 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) b.write_transaction(tx, 'broadcast_tx_async') assert mock_post.called args, kwargs = mock_post.call_args assert 'broadcast_tx_async' == kwargs['json']['method'] encoded_tx = [encode_transaction(tx.to_dict())] assert encoded_tx == kwargs['json']['params']
def test_post_invalid_transfer_transaction_returns_400(b, client, user_pk): from bigchaindb.models import Transaction from bigchaindb.common.exceptions import InvalidSignature user_pub = crypto.generate_key_pair()[1] input_valid = b.get_owned_ids(user_pk).pop() create_tx = b.get_transaction(input_valid.txid) transfer_tx = Transaction.transfer(create_tx.to_inputs(), [([user_pub], 1)], asset_id=create_tx.id) transfer_tx._hash() res = client.post(TX_ENDPOINT, data=json.dumps(transfer_tx.to_dict())) expected_status_code = 400 expected_error_message = 'Invalid transaction ({}): {}'.format( InvalidSignature.__name__, 'Transaction signature is invalid.') assert res.status_code == expected_status_code assert res.json['message'] == expected_error_message
def test_consensus_rules_frontend(b): from .utils import create_simple_tx, post_tx alice_priv, alice_pub = crypto.generate_key_pair() create_a = create_simple_tx( alice_pub, alice_priv, asset={ 'policy': [ { 'condition': "'INIT' == 'INIT'", 'rule': "'INIT' == 'INIT'" }, ] }, metadata={ 'state': "INIT" }) response = post_tx(b, None, create_a) assert response.status_code == 202
def test_validation_with_transaction_buffer(b): from bigchaindb.common.crypto import generate_key_pair from bigchaindb.models import Transaction priv_key, pub_key = generate_key_pair() 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]) double_spend = Transaction.transfer(create_tx.to_inputs(), [([pub_key], 10)], asset_id=create_tx.id).sign([priv_key]) assert b.is_valid_transaction(create_tx) assert b.is_valid_transaction(transfer_tx, [create_tx]) assert not b.is_valid_transaction(create_tx, [create_tx]) assert not b.is_valid_transaction(transfer_tx, [create_tx, transfer_tx]) assert not b.is_valid_transaction(double_spend, [create_tx, transfer_tx])
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]) block = b.create_block([tx_create_signed]) b.write_block(block) # get input owned_inputs_user1 = b.get_owned_ids(user_pk) owned_inputs_user2 = b.get_owned_ids(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]) block = b.create_block([tx_transfer_signed]) b.write_block(block) 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_transfer.id, 0), TransactionLink(tx_transfer.id, 1) ]
def generate_validators(powers): """Generates an arbitrary number of validators with random public keys. The object under the `storage` key is in the format expected by DB. The object under the `eleciton` key is in the format expected by the upsert validator election. `public_key`, `private_key` are in the format used for signing transactions. Args: powers: A list of intergers representing the voting power to assign to the corresponding validators. """ validators = [] for power in powers: kp = crypto.generate_key_pair() validators.append({ 'storage': { 'public_key': { 'value': key_to_base64(base58.b58decode(kp.public_key).hex()), 'type': 'ed25519-base64', }, 'voting_power': power, }, 'election': { 'node_id': f'node-{random.choice(range(100))}', 'power': power, 'public_key': { 'value': base64.b16encode(base58.b58decode( kp.public_key)).decode('utf-8'), 'type': 'ed25519-base16', }, }, 'public_key': kp.public_key, 'private_key': kp.private_key, }) return validators
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
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)]).sign([user_priv]).to_dict() del tx['version'] 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_permissions(b): from .utils import create_simple_tx, post_tx admin_priv, admin_pub = crypto.generate_key_pair() # class App(): # allowed_permissions = [ # 'ADD_USER' # ] # # def __init__(owner, uuid): # self.uuid = uuid # self.owner = owner # for allowed_permission in allowed_permissions: # self.permissions[allowed_permission] = [] # # admin CREATES permissions ASSET # admin CREATES app ASSET # admin LINKS permission ID # # def grant_permission(permission, user): # self.permissions[permission].append(user) # # def revoke_permission(permission, user): # self.permissions[permission].remove(user) # create_permission_add_user = create_simple_tx( admin_pub, admin_priv, asset={'permission': 'ADD_USER'}) response = post_tx(b, None, create_permission_add_user) assert response.status_code == 202 create_app = create_simple_tx(admin_pub, admin_priv, asset={ 'uuid': 'app', 'links': create_permission_add_user.id }) response = post_tx(b, None, create_app) assert response.status_code == 202
def federation(n): """ Return a list of Bigchain objects and pipeline steppers to represent a BigchainDB federation """ keys = [generate_key_pair() for _ in range(n)] config_orig = bigchaindb.config @contextmanager def make_nodes(i): """ make_nodes is a recursive context manager. Essentially it is doing: with f(a[0]) as b0: with f(a[1]) as b1: with f(a[2]) as b2: yield [b0, b1, b2] with an arbitrary depth. It is also temporarily patching global configuration to simulate nodes with separate identities. """ nonlocal keys if i == 0: yield [] else: config = deepcopy(config_orig) keys = [keys[-1]] + keys[:-1] # Rotate keys config['keyring'] = [pub for _, pub in keys[1:]] config['keypair']['private'] = keys[0][0] config['keypair']['public'] = keys[0][1] bigchaindb.config = config stepper = create_stepper() with stepper.start(): node = (Bigchain(), stepper) with make_nodes(i - 1) as rest: yield [node] + rest with make_nodes(n) as steppers: bigchaindb.config = config_orig yield zip(*steppers)
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_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_roles(b): from .utils import create_simple_tx, post_tx admin_priv, admin_pub = crypto.generate_key_pair() # class AddUserRole(): # permissions = [ # 'ADD_USER' # ] # # def __init__(user): # self.user = user # # class App(): # def __init__(owner, uuid): # self.uuid = uuid # self.owner = owner # self.roles = {} # # def add_role(role): # self.roles[role.user] = role # # def remove_role(role): # self.roles[role.user] = None # create_permission_add_user = create_simple_tx( admin_pub, admin_priv, asset={'permission': 'ADD_USER'}) response = post_tx(b, None, create_permission_add_user) assert response.status_code == 202 create_app = create_simple_tx(admin_pub, admin_priv, asset={ 'uuid': 'app', 'links': create_permission_add_user.id }) response = post_tx(b, None, create_app) assert response.status_code == 202
def run_start(args): """Start the processes to run the node""" logger.info('{} Version {}'.format(app_setup_name, bigchaindb.__version__)) bigchaindb.config_utils.autoconfigure(filename=args.config, force=True) if args.allow_temp_keypair: if not (bigchaindb.config['keypair']['private'] or bigchaindb.config['keypair']['public']): private_key, public_key = crypto.generate_key_pair() bigchaindb.config['keypair']['private'] = private_key bigchaindb.config['keypair']['public'] = public_key else: logger.warning('Keypair found, no need to create one on the fly.') if args.start_rethinkdb: try: proc = utils.start_rethinkdb() except StartupError as e: sys.exit('Error starting RethinkDB, reason is: {}'.format(e)) logger.info('RethinkDB started with PID %s' % proc.pid) try: db.init() except DatabaseAlreadyExists: pass except KeypairNotFoundException: sys.exit("Can't start {}, no keypair found. " 'Did you run `{} configure`?'.format(app_setup_name, app_service_name)) db.init_databaseData() logger.info( 'Starting {} main process with public key %s'.format(app_setup_name), bigchaindb.config['keypair']['public']) processes.start()
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_post_create_transaction_with_invalid_key(b, client, field, value, err_key, expected_status_code): from bigchaindb.models import Transaction from bigchaindb.backend.mongodb.connection import MongoDBConnection user_priv, user_pub = crypto.generate_key_pair() if isinstance(b.connection, MongoDBConnection): if field == 'asset': tx = Transaction.create([user_pub], [([user_pub], 1)], asset=value) elif field == 'metadata': tx = Transaction.create([user_pub], [([user_pub], 1)], metadata=value) 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): Invalid key name "{}" ' 'in {} object. The key name cannot contain characters ' '".", "$" or null characters').format(err_key, field) assert res.json['message'] == expected_error_message
def test_post_create_transaction_with_invalid_signature(mock_logger, b, client): from bigchaindb.common.exceptions import InvalidSignature from bigchaindb.models import Transaction user_priv, user_pub = crypto.generate_key_pair() tx = Transaction.create([user_pub], [([user_pub], 1)]).to_dict() tx['inputs'][0]['fulfillment'] = 64 * '0' 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 ({}): Fulfillment URI ' 'couldn\'t been parsed' ).format(InvalidSignature.__name__) 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_recover_db_from_zombie_block(b, monkeypatch): from bigchaindb.commands.bigchaindb import run_recover from bigchaindb.models import Transaction from bigchaindb.common.crypto import generate_key_pair from bigchaindb.tendermint.lib import Block from bigchaindb import backend alice = generate_key_pair() tx = Transaction.create([alice.public_key], [([alice.public_key], 1)], asset={'cycle': 'hero'}, metadata={'name': 'hohenheim'}) \ .sign([alice.private_key]) b.store_bulk_transactions([tx]) block9 = Block(app_hash='random_app_hash', height=9, transactions=[])._asdict() b.store_block(block9) block10 = Block(app_hash='random_app_hash', height=10, transactions=[tx.id])._asdict() b.store_block(block10) def mock_get(uri): return MockResponse(9) monkeypatch.setattr('requests.get', mock_get) run_recover(b) assert list(backend.query.get_metadata(b.connection, [tx.id])) == [] assert not backend.query.get_asset(b.connection, tx.id) assert not b.get_transaction(tx.id) block = b.get_latest_block() assert block['height'] == 9
def test_upsert_validator_delegate_election_vote(b_mock, valid_election, ed25519_node_keys): from bigchaindb.common.crypto import generate_key_pair alice = generate_key_pair() b_mock.store_bulk_transactions([valid_election]) input0 = valid_election.to_inputs()[0] votes = valid_election.outputs[0].amount public_key0 = input0.owners_before[0] key0 = ed25519_node_keys[public_key0] delegate_vote = ValidatorElectionVote.generate([input0], [([alice.public_key], 3), ([key0.public_key], votes-3)], election_id=valid_election.id)\ .sign([key0.private_key]) assert delegate_vote.validate(b_mock) b_mock.store_bulk_transactions([delegate_vote]) election_pub_key = ValidatorElectionVote.to_public_key(valid_election.id) alice_votes = delegate_vote.to_inputs()[0] alice_casted_vote = ValidatorElectionVote.generate([alice_votes], [([election_pub_key], 3)], election_id=valid_election.id)\ .sign([alice.private_key]) assert alice_casted_vote.validate(b_mock) key0_votes = delegate_vote.to_inputs()[1] key0_casted_vote = ValidatorElectionVote.generate([key0_votes], [([election_pub_key], votes-3)], election_id=valid_election.id)\ .sign([key0.private_key]) assert key0_casted_vote.validate(b_mock)
def test_get_votes_endpoint_multiple_votes(b, client): from bigchaindb.common.crypto import generate_key_pair tx = Transaction.create([b.me], [([b.me], 1)]) tx = tx.sign([b.me_private]) block = b.create_block([tx]) b.write_block(block) last_block = b.get_last_voted_block().id # vote the block valid vote_valid = b.vote(block.id, last_block, True) b.write_vote(vote_valid) # vote the block invalid # a note can only vote once so we need a new node_pubkey for the second # vote _, pk = generate_key_pair() vote_invalid = b.vote(block.id, last_block, False) vote_invalid['node_pubkey'] = pk b.write_vote(vote_invalid) res = client.get(VOTES_ENDPOINT + '?block_id=' + block.id) assert len(res.json) == 2 assert res.status_code == 200
def test_consensus_rules(b): from .utils import create_simple_tx, post_tx alice_priv, alice_pub = crypto.generate_key_pair() create_a = create_simple_tx( alice_pub, alice_priv, asset={ 'policy': [ { 'condition': 'transaction.metadata["state"] == "INIT"', 'rule': 'AMOUNT(transaction.outputs) == 1' }, { 'condition': 'transaction.inputs[0].owners_before == "{}"'.format(alice_pub), 'rule': 'LEN(transaction.outputs) == 1' }, ] }, metadata={ 'state': "INIT" }) response = post_tx(b, None, create_a) assert response.status_code == 202
def test_app(tb): from bigchaindb.tendermint import App from bigchaindb.tendermint.utils import calculate_hash from bigchaindb.common.crypto import generate_key_pair from bigchaindb.models import Transaction b = tb app = App(b) p = ProtocolHandler(app) data = p.process('info', None) res, err = read_message(BytesIO(data), types.Response) assert res assert res.info.last_block_app_hash == b'' assert res.info.last_block_height == 0 assert not b.get_latest_block() p.process('init_chain', None) block0 = b.get_latest_block() assert block0 assert block0['height'] == 0 assert block0['app_hash'] == '' alice = generate_key_pair() bob = generate_key_pair() tx = Transaction.create([alice.public_key], [([bob.public_key], 1)])\ .sign([alice.private_key]) etxn = json.dumps(tx.to_dict()).encode('utf8') r = to_request_check_tx(etxn) data = p.process('check_tx', r) res, err = read_message(BytesIO(data), types.Response) assert res assert res.check_tx.code == 0 r = types.Request() r.begin_block.hash = b'' p.process('begin_block', r) r = to_request_deliver_tx(etxn) data = p.process('deliver_tx', r) res, err = read_message(BytesIO(data), types.Response) assert res assert res.deliver_tx.code == 0 new_block_txn_hash = calculate_hash([tx.id]) r = types.Request() r.end_block.height = 1 data = p.process('end_block', r) res, err = read_message(BytesIO(data), types.Response) assert res assert 'end_block' == res.WhichOneof('value') new_block_hash = calculate_hash([block0['app_hash'], new_block_txn_hash]) data = p.process('commit', None) res, err = read_message(BytesIO(data), types.Response) assert res assert res.commit.code == 0 assert res.commit.data == new_block_hash.encode('utf-8') assert b.get_transaction(tx.id).id == tx.id block0 = b.get_latest_block() assert block0 assert block0['height'] == 1 assert block0['app_hash'] == new_block_hash # empty block should not update height r = types.Request() r.begin_block.hash = new_block_hash.encode('utf-8') p.process('begin_block', r) r = types.Request() r.end_block.height = 2 p.process('end_block', r) data = p.process('commit', None) assert res.commit.data == new_block_hash.encode('utf-8') block0 = b.get_latest_block() assert block0 assert block0['height'] == 1 # when empty block is generated hash of previous block should be returned assert block0['app_hash'] == new_block_hash
def merlin(): from bigchaindb.common.crypto import generate_key_pair return generate_key_pair()
import os import copy import random from collections import namedtuple from logging import getLogger from logging.config import dictConfig import pytest from pymongo import MongoClient from bigchaindb.common import crypto from bigchaindb.tendermint.lib import Block TEST_DB_NAME = 'bigchain_test' USER2_SK, USER2_PK = crypto.generate_key_pair() # Test user. inputs will be created for this user. Cryptography Keys USER_PRIVATE_KEY = '8eJ8q9ZQpReWyQT5aFCiwtZ5wDZC4eDnCen88p3tQ6ie' USER_PUBLIC_KEY = 'JEAkEJqLbbgDRAtMm8YAjGp759Aq2qTn9eaEHUj2XePE' def pytest_runtest_setup(item): if isinstance(item, item.Function): backend = item.session.config.getoption('--database-backend') if (item.get_marker('localmongodb') and backend != 'localmongodb'): pytest.skip( 'Skip tendermint specific tests if not using localmongodb') def pytest_addoption(parser):
import requests import time from bigchaindb.common.transaction import Transaction, Asset, Fulfillment from bigchaindb.common.crypto import generate_key_pair # 一对多转账,生成1+3个公钥 pri_1, pub_1 = generate_key_pair() pri_n1, pub_n1 = generate_key_pair() pri_n2, pub_n2 = generate_key_pair() pri_n3, pub_n3 = generate_key_pair() delay = 4 # 发送交易后,等待建块投票延迟 msg = "1_to_n" asset = Asset(data={'money': 'RMB'}, data_id='20170628150000', divisible=True) metadata = {'raw': msg} # pub_1先创建10000,再转给n1,n2,n3各2000/3000/5000 amount = 10000 amount_n1 = 2000 amount_n2 = 3000 amount_n3 = 5000 # pub_1创建交易 tx = Transaction.create([pub_1], [([pub_1], amount)], metadata=metadata, asset=asset) tx = tx.sign([pri_1]).to_dict() print("========create tx======") # 发送给区块链节点 with requests.Session() as session: res = session.post('http://localhost:9984/uniledger/v1/transaction/createOrTransferTx', json=tx)
import json import sha3 from cryptoconditions import Ed25519Fulfillment, ThresholdSha256Fulfillment from cryptoconditions.crypto import Ed25519SigningKey from bigchaindb.models import Transaction from bigchaindb.common.crypto import generate_key_pair from bigchaindb.common.transaction import Output VW_SK, VW_PK = generate_key_pair() TEL_SK, TEL_PK = generate_key_pair() def create_asset(vw_sk, vw_pk, vehicle_id, data, metadata): # Create asset VW -> [VW, TEL] # Custom crypto condition multisig 1-2 threshold_fulfillment = ThresholdSha256Fulfillment(threshold=1) vw_fulfillment = Ed25519Fulfillment(public_key=vw_pk) tel_fulfillment = Ed25519Fulfillment(public_key=vehicle_id) threshold_fulfillment.add_subfulfillment(vw_fulfillment) threshold_fulfillment.add_subfulfillment(tel_fulfillment) output = { 'amount': 1, 'condition': { 'details': threshold_fulfillment.to_dict(), 'uri': threshold_fulfillment.condition.serialize_uri() },
def get_key_pair(self): return generate_key_pair()