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_init_chain_ignores_invalid_init_chain_requests(b): validators = [generate_validator()] request = generate_init_chain_request('chain-XYZ', validators) res = App(b).init_chain(request) assert res == ResponseInitChain() validator_set = query.get_validator_set(b.connection) invalid_requests = [ request, # the same request again # different validator set generate_init_chain_request('chain-XYZ'), # different chain ID generate_init_chain_request('chain-ABC', validators), ] for r in invalid_requests: with pytest.raises(SystemExit): App(b).init_chain(r) # assert nothing changed - neither validator set, nor chain ID new_validator_set = query.get_validator_set(b.connection) assert new_validator_set == validator_set new_chain_id = query.get_latest_abci_chain(b.connection)['chain_id'] assert new_chain_id == 'chain-XYZ' assert query.get_latest_block(b.connection) == { 'height': 0, 'app_hash': '', 'transactions': [], }
def test_deliver_tx__valid_create_updates_db(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
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_end_block_return_validator_updates(b, init_chain_request): app = App(b) app.init_chain(init_chain_request) begin_block = RequestBeginBlock() app.begin_block(begin_block) # generate a block containing a concluded validator election validators = generate_validators([1] * 4) b.store_validator_set(1, [v['storage'] for v in validators]) new_validator = generate_validators([1])[0] public_key = validators[0]['public_key'] private_key = validators[0]['private_key'] voter_keys = [v['private_key'] for v in validators] election, votes = generate_election(b, ValidatorElection, public_key, private_key, new_validator['election'], voter_keys) b.store_block( Block(height=1, transactions=[election.id], app_hash='')._asdict()) b.store_bulk_transactions([election]) Election.process_block(b, 1, [election]) app.block_transactions = votes resp = app.end_block(RequestEndBlock(height=2)) assert resp.validator_updates[0].power == new_validator['election'][ 'power'] expected = bytes.fromhex(new_validator['election']['public_key']['value']) assert expected == resp.validator_updates[0].pub_key.data
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_info(b): r = RequestInfo(version=__tm_supported_versions__[0]) app = App(b) res = app.info(r) assert res.last_block_height == 0 assert res.last_block_app_hash == b'' b.store_block(Block(app_hash='1', height=1, transactions=[])._asdict()) res = app.info(r) assert res.last_block_height == 1 assert res.last_block_app_hash == b'1' # simulate a migration and assert the height is shifted b.store_abci_chain(2, 'chain-XYZ') app = App(b) b.store_block(Block(app_hash='2', height=2, transactions=[])._asdict()) res = app.info(r) assert res.last_block_height == 0 assert res.last_block_app_hash == b'2' b.store_block(Block(app_hash='3', height=3, transactions=[])._asdict()) res = app.info(r) assert res.last_block_height == 1 assert res.last_block_app_hash == b'3' # it's always the latest migration that is taken into account b.store_abci_chain(4, 'chain-XYZ-new') app = App(b) b.store_block(Block(app_hash='4', height=4, transactions=[])._asdict()) res = app.info(r) assert res.last_block_height == 0 assert res.last_block_app_hash == b'4'
def test_exit_when_tm_ver_not_supported(b): from bigchaindb import App app = App(b) p = ProtocolHandler(app) with pytest.raises(SystemExit): p.process('info', types.Request(info=types.RequestInfo(version='2')))
def test_end_block_return_validator_updates(b): from bigchaindb import App from bigchaindb.backend import query from bigchaindb.core import encode_validator from bigchaindb.backend.query import VALIDATOR_UPDATE_ID app = App(b) app.init_chain(['ignore']) begin_block = RequestBeginBlock() app.begin_block(begin_block) validator = { 'pub_key': { 'type': 'ed25519', 'data': 'B0E42D2589A455EAD339A035D6CE1C8C3E25863F268120AA0162AD7D003A4014' }, 'power': 10 } validator_update = { 'validator': validator, 'update_id': VALIDATOR_UPDATE_ID } query.store_validator_update(b.connection, validator_update) resp = app.end_block(RequestEndBlock(height=99)) assert resp.validator_updates[0] == encode_validator(validator) updates = b.get_validator_update() assert updates == []
def test_end_block_return_validator_updates(b, init_chain_request): app = App(b) app.init_chain(init_chain_request) begin_block = RequestBeginBlock() app.begin_block(begin_block) # generate a block containing a concluded validator election validators = generate_validators([1] * 4) b.store_validator_set(1, [v['storage'] for v in validators]) new_validator = generate_validators([1])[0] public_key = validators[0]['public_key'] private_key = validators[0]['private_key'] voter_keys = [v['private_key'] for v in validators] election, votes = generate_election(b, ValidatorElection, public_key, private_key, new_validator['election'], voter_keys) b.store_block(Block(height=1, transactions=[election.id], app_hash='')._asdict()) b.store_bulk_transactions([election]) Election.process_block(b, 1, [election]) app.block_transactions = votes resp = app.end_block(RequestEndBlock(height=2)) assert resp.validator_updates[0].power == new_validator['election']['power'] expected = bytes.fromhex(new_validator['election']['public_key']['value']) assert expected == resp.validator_updates[0].pub_key.data
def test_init_chain_successfully_registers_chain(b): request = generate_init_chain_request('chain-XYZ') res = App(b).init_chain(request) assert res == ResponseInitChain() chain = query.get_latest_abci_chain(b.connection) assert chain == {'height': 0, 'chain_id': 'chain-XYZ', 'is_synced': True} assert query.get_latest_block(b.connection) == { 'height': 0, 'app_hash': '', 'transactions': [], }
def test_deliver_transfer_tx__double_spend_fails(a, b, init_chain_request): from bigchaindb import App from bigchaindb.models import Transaction from bigchaindb.common.crypto import generate_key_pair app = App(a, b) app.init_chain(init_chain_request) begin_block = types.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_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_end_block_return_validator_updates(b, init_chain_request): from bigchaindb import App from bigchaindb.backend import query from bigchaindb.core import encode_validator from bigchaindb.backend.query import VALIDATOR_UPDATE_ID app = App(b) app.init_chain(init_chain_request) begin_block = RequestBeginBlock() app.begin_block(begin_block) validator = {'pub_key': {'type': 'ed25519', 'data': 'B0E42D2589A455EAD339A035D6CE1C8C3E25863F268120AA0162AD7D003A4014'}, 'power': 10} validator_update = {'validator': validator, 'update_id': VALIDATOR_UPDATE_ID} query.store_validator_update(b.connection, validator_update) resp = app.end_block(RequestEndBlock(height=99)) assert resp.validator_updates[0] == encode_validator(validator) updates = b.approved_update() assert not updates
def test_commit_aborts_if_chain_is_not_synced(b): b.store_abci_chain(0, 'chain-XYZ', False) with pytest.raises(SystemExit): App(b).commit()
def test_end_block_aborts_if_chain_is_not_synced(b): b.store_abci_chain(0, 'chain-XYZ', False) with pytest.raises(SystemExit): App(b).info(RequestEndBlock())
def test_deliver_tx_aborts_if_chain_is_not_synced(b): b.store_abci_chain(0, 'chain-XYZ', False) with pytest.raises(SystemExit): App(b).deliver_tx('some bytes')
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_deliver_tx__double_spend_fails(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)])\ .sign([alice.private_key]) app = App(b) app.init_chain(['ignore']) 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_init_chain_recognizes_new_chain_after_migration(b): validators = [generate_validator()] request = generate_init_chain_request('chain-XYZ', validators) res = App(b).init_chain(request) assert res == ResponseInitChain() validator_set = query.get_validator_set(b.connection)['validators'] # simulate a migration query.store_block(b.connection, Block(app_hash='', height=1, transactions=[])._asdict()) b.migrate_abci_chain() # the same or other mismatching requests are ignored invalid_requests = [ request, generate_init_chain_request('unknown', validators), generate_init_chain_request('chain-XYZ'), generate_init_chain_request('chain-XYZ-migrated-at-height-1'), ] for r in invalid_requests: with pytest.raises(SystemExit): App(b).init_chain(r) assert query.get_latest_abci_chain(b.connection) == { 'chain_id': 'chain-XYZ-migrated-at-height-1', 'is_synced': False, 'height': 2, } new_validator_set = query.get_validator_set(b.connection)['validators'] assert new_validator_set == validator_set # a request with the matching chain ID and matching validator set # completes the migration request = generate_init_chain_request('chain-XYZ-migrated-at-height-1', validators) res = App(b).init_chain(request) assert res == ResponseInitChain() assert query.get_latest_abci_chain(b.connection) == { 'chain_id': 'chain-XYZ-migrated-at-height-1', 'is_synced': True, 'height': 2, } assert query.get_latest_block(b.connection) == { 'height': 2, 'app_hash': '', 'transactions': [], } # requests with old chain ID and other requests are ignored invalid_requests = [ request, generate_init_chain_request('chain-XYZ', validators), generate_init_chain_request('chain-XYZ-migrated-at-height-1'), ] for r in invalid_requests: with pytest.raises(SystemExit): App(b).init_chain(r) assert query.get_latest_abci_chain(b.connection) == { 'chain_id': 'chain-XYZ-migrated-at-height-1', 'is_synced': True, 'height': 2, } new_validator_set = query.get_validator_set(b.connection)['validators'] assert new_validator_set == validator_set assert query.get_latest_block(b.connection) == { 'height': 2, 'app_hash': '', 'transactions': [], }
def test_begin_aborts_if_chain_is_not_synced(a, b): b.store_abci_chain(0, 'chain-XYZ', False) with pytest.raises(SystemExit): App(a, b).info(types.RequestBeginBlock())
def test_store_pre_commit_state_in_end_block(b, alice): from bigchaindb import App from bigchaindb.backend import query from bigchaindb.models import Transaction from bigchaindb.backend.query import PRE_COMMIT_ID 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(['ignore']) 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, PRE_COMMIT_ID) assert resp['commit_id'] == PRE_COMMIT_ID 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, PRE_COMMIT_ID) assert resp['commit_id'] == PRE_COMMIT_ID assert resp['height'] == 100 assert resp['transactions'] == [tx.id]
def test_info(b): r = RequestInfo(version=__tm_supported_versions__[0]) app = App(b) res = app.info(r) assert res.last_block_height == 0 assert res.last_block_app_hash == b'' b.store_block(Block(app_hash='1', height=1, transactions=[])._asdict()) res = app.info(r) assert res.last_block_height == 1 assert res.last_block_app_hash == b'1' # simulate a migration and assert the height is shifted b.store_abci_chain(2, 'chain-XYZ') app = App(b) b.store_block(Block(app_hash='2', height=2, transactions=[])._asdict()) res = app.info(r) assert res.last_block_height == 0 assert res.last_block_app_hash == b'2' b.store_block(Block(app_hash='3', height=3, transactions=[])._asdict()) res = app.info(r) assert res.last_block_height == 1 assert res.last_block_app_hash == b'3' # it's always the latest migration that is taken into account b.store_abci_chain(4, 'chain-XYZ-new') app = App(b) b.store_block(Block(app_hash='4', height=4, transactions=[])._asdict()) res = app.info(r) assert res.last_block_height == 0 assert res.last_block_app_hash == b'4'
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_app(tb, init_chain_request): from bigchaindb 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', types.Request(info=types.RequestInfo(version='2'))) res = next(read_messages(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', types.Request(init_chain=init_chain_request)) block0 = b.get_latest_block() assert block0 assert block0['height'] == 0 assert block0['app_hash'] == '' pk = codecs.encode(init_chain_request.validators[0].pub_key.data, 'base64').decode().strip('\n') [validator] = b.get_validators(height=1) assert validator['pub_key']['data'] == pk assert validator['voting_power'] == 10 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 = types.Request(check_tx=types.RequestCheckTx(tx=etxn)) data = p.process('check_tx', r) res = next(read_messages(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 = types.Request(deliver_tx=types.RequestDeliverTx(tx=etxn)) data = p.process('deliver_tx', r) res = next(read_messages(BytesIO(data), types.Response)) assert res assert res.deliver_tx.code == 0 new_block_txn_hash = calculate_hash([tx.id]) r = types.Request(end_block=types.RequestEndBlock(height=1)) data = p.process('end_block', r) res = next(read_messages(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 = next(read_messages(BytesIO(data), types.Response)) 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) res = next(read_messages(BytesIO(data), types.Response)) 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 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]