diff --git a/docker-compose-aio.yml b/docker-compose-aio.yml new file mode 100644 index 0000000..e1be07a --- /dev/null +++ b/docker-compose-aio.yml @@ -0,0 +1,40 @@ +# Copyright © 2020 Interplanetary Database Association e.V., +# Planetmint and IPDB software contributors. +# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0) +# Code is Apache-2.0 and docs are CC-BY-4.0 + +version: '2.2' + +services: + planetmint-all-in-one: + image: planetmint/planetmint-aio:latest + expose: + - "22" + - "9984" + - "9985" + - "26656" + - "26657" + - "26658" + command: ["/usr/src/app/scripts/pre-config-planetmint.sh", "/usr/src/app/scripts/all-in-one.bash"] + volumes: + - ./integration/scripts:/usr/src/app/scripts + - shared:/shared + scale: ${SCALE:-4} + + test: + build: + context: . + dockerfile: integration/python/Dockerfile + depends_on: + - planetmint-all-in-one + command: ["/scripts/pre-config-test.sh", "/scripts/wait-for-planetmint.sh", "/scripts/test.sh", "pytest", "/src"] + environment: + SCALE: ${SCALE:-4} + volumes: + - ./integration/python/src:/src + - ./integration/scripts:/scripts + - ./integration/cli:/tests + - shared:/shared + +volumes: + shared: \ No newline at end of file diff --git a/planetmint/application/validator.py b/planetmint/application/validator.py index 08ada08..e55fced 100644 --- a/planetmint/application/validator.py +++ b/planetmint/application/validator.py @@ -28,14 +28,14 @@ from planetmint.backend.models.output import Output from planetmint.model.dataaccessor import DataAccessor from planetmint.config import Config from planetmint.config_utils import load_validation_plugin +from planetmint.utils.singleton import Singleton logger = logging.getLogger(__name__) class Validator: - def __init__(self, async_io: bool = False): - self.async_io = async_io - self.models = DataAccessor(async_io=async_io) + def __init__(self): + self.models = DataAccessor() self.validation = Validator._get_validation_method() @staticmethod @@ -269,7 +269,7 @@ class Validator: value as the `voting_power` """ validators = {} - for validator in self.models.get_validators(height): + for validator in self.models.get_validators(height=height): # NOTE: we assume that Tendermint encodes public key in base64 public_key = public_key_from_ed25519_key(key_from_base64(validator["public_key"]["value"])) validators[public_key] = validator["voting_power"] @@ -493,7 +493,7 @@ class Validator: self.migrate_abci_chain() if election.operation == VALIDATOR_ELECTION: validator_updates = [election.assets[0].data] - curr_validator_set = self.models.get_validators(new_height) + curr_validator_set = self.models.get_validators(height=new_height) updated_validator_set = new_validator_set(curr_validator_set, validator_updates) updated_validator_set = [v for v in updated_validator_set if v["voting_power"] > 0] diff --git a/planetmint/backend/connection.py b/planetmint/backend/connection.py index 10eb022..b7129da 100644 --- a/planetmint/backend/connection.py +++ b/planetmint/backend/connection.py @@ -64,7 +64,6 @@ class DBConnection(metaclass=DBSingleton): backend: str = None, connection_timeout: int = None, max_tries: int = None, - async_io: bool = False, **kwargs ): """Create a new :class:`~.Connection` instance. diff --git a/planetmint/backend/tarantool/sync_io/query.py b/planetmint/backend/tarantool/sync_io/query.py index 0a8359d..de544fc 100644 --- a/planetmint/backend/tarantool/sync_io/query.py +++ b/planetmint/backend/tarantool/sync_io/query.py @@ -407,11 +407,12 @@ def store_validator_set(conn, validators_update: dict): conn.connect().select(TARANT_TABLE_VALIDATOR_SETS, validators_update["height"], index="height", limit=1).data ) unique_id = uuid4().hex if _validator is None or len(_validator) == 0 else _validator[0][0] - conn.connect().upsert( + result = conn.connect().upsert( TARANT_TABLE_VALIDATOR_SETS, (unique_id, validators_update["height"], validators_update["validators"]), op_list=[("=", 1, validators_update["height"]), ("=", 2, validators_update["validators"])], ) + return result @register_query(TarantoolDBConnection) diff --git a/planetmint/model/dataaccessor.py b/planetmint/model/dataaccessor.py index de80a17..aa29d29 100644 --- a/planetmint/model/dataaccessor.py +++ b/planetmint/model/dataaccessor.py @@ -22,12 +22,19 @@ from planetmint.backend.models.output import Output from planetmint.backend.models.asset import Asset from planetmint.backend.models.metadata import MetaData from planetmint.backend.models.dbtransaction import DbTransaction +from planetmint.utils.singleton import Singleton -class DataAccessor: - def __init__(self, database_connection=None, async_io: bool = False): +class DataAccessor(metaclass=Singleton): + def __init__(self, database_connection=None): config_utils.autoconfigure() - self.connection = database_connection if database_connection is not None else Connection(async_io=async_io) + self.connection = database_connection if database_connection is not None else Connection() + + def close_connection(self): + self.connection.close() + + def connect(self): + self.connection.connect() def store_bulk_transactions(self, transactions): txns = [] @@ -144,7 +151,7 @@ class DataAccessor: value as the `voting_power` """ validators = {} - for validator in self.get_validators(height): + for validator in self.get_validators(height=height): # NOTE: we assume that Tendermint encodes public key in base64 public_key = public_key_from_ed25519_key(key_from_base64(validator["public_key"]["value"])) validators[public_key] = validator["voting_power"] diff --git a/pytest.ini b/pytest.ini index b13d936..01b5ef6 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,7 +1,7 @@ [pytest] testpaths = tests/ norecursedirs = .* *.egg *.egg-info env* devenv* docs -addopts = -m "abci" +addopts = -m "not abci" looponfailroots = planetmint tests asyncio_mode = strict markers = diff --git a/tests/assets/test_divisible_assets.py b/tests/assets/test_divisible_assets.py index 4daa610..f6a9992 100644 --- a/tests/assets/test_divisible_assets.py +++ b/tests/assets/test_divisible_assets.py @@ -157,14 +157,14 @@ def test_single_in_single_own_multiple_out_single_own_transfer(alice, b, user_pk ) tx_create_signed = tx_create.sign([alice.private_key]) + b.models.store_bulk_transactions([tx_create_signed]) + inputs = tx_create.to_inputs() # TRANSFER tx_transfer = Transfer.generate( - tx_create.to_inputs(), [([alice.public_key], 50), ([alice.public_key], 50)], asset_ids=[tx_create.id] + inputs, [([alice.public_key], 50), ([alice.public_key], 50)], asset_ids=[tx_create.id] ) tx_transfer_signed = tx_transfer.sign([user_sk]) - b.models.store_bulk_transactions([tx_create_signed]) - assert b.validate_transaction(tx_transfer_signed) == tx_transfer_signed assert len(tx_transfer_signed.outputs) == 2 assert tx_transfer_signed.outputs[0].amount == 50 diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index d0f01a8..d2a3d03 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -26,6 +26,103 @@ from planetmint.backend.connection import Connection from tests.utils import generate_election, generate_validators +def mock_get_validators(self, height): + return [ + { + "public_key": { + "value": "zL/DasvKulXZzhSNFwx4cLRXKkSM9GPK7Y0nZ4FEylM=", + "type": "ed25519-base64", + }, + "voting_power": 10, + } + ] + + +@patch("planetmint.commands.utils.start") +def test_main_entrypoint(mock_start): + from planetmint.commands.planetmint import main + from planetmint.model.dataaccessor import DataAccessor + + da = DataAccessor + del da + main() + + assert mock_start.called + + +# @pytest.mark.bdb +def test_chain_migration_election_show_shows_inconclusive(b_flushed, test_abci_rpc): + from tests.utils import flush_db + + b = b_flushed + + validators = generate_validators([1] * 4) + output = b.models.store_validator_set(1, [v["storage"] for v in validators]) + + 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, ChainMigrationElection, public_key, private_key, [{"data": {}}], voter_keys) + + assert not run_election_show(Namespace(election_id=election.id), b) + + b.process_block(1, [election]) + b.models.store_bulk_transactions([election]) + + assert run_election_show(Namespace(election_id=election.id), b) == "status=ongoing" + + b.models.store_block(Block(height=1, transactions=[], app_hash="")._asdict()) + b.models.store_validator_set(2, [v["storage"] for v in validators]) + + assert run_election_show(Namespace(election_id=election.id), b) == "status=ongoing" + + b.models.store_block(Block(height=2, transactions=[], app_hash="")._asdict()) + # TODO insert yet another block here when upgrading to Tendermint 0.22.4. + + assert run_election_show(Namespace(election_id=election.id), b) == "status=inconclusive" + + +@pytest.mark.bdb +def test_chain_migration_election_show_shows_concluded(b_flushed): + b = b_flushed + validators = generate_validators([1] * 4) + b.models.store_validator_set(1, [v["storage"] for v in validators]) + + 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, ChainMigrationElection, public_key, private_key, [{"data": {}}], voter_keys) + + assert not run_election_show(Namespace(election_id=election.id), b) + + b.models.store_bulk_transactions([election]) + b.process_block(1, [election]) + + assert run_election_show(Namespace(election_id=election.id), b) == "status=ongoing" + + b.models.store_abci_chain(1, "chain-X") + b.models.store_block(Block(height=1, transactions=[v.id for v in votes], app_hash="last_app_hash")._asdict()) + b.process_block(2, votes) + + assert ( + run_election_show(Namespace(election_id=election.id), b) + == f'''status=concluded +chain_id=chain-X-migrated-at-height-1 +app_hash=last_app_hash +validators=[{''.join([f""" + {{ + "pub_key": {{ + "type": "tendermint/PubKeyEd25519", + "value": "{v['public_key']}" + }}, + "power": {v['storage']['voting_power']} + }}{',' if i + 1 != len(validators) else ''}""" for i, v in enumerate(validators)])} +]''' + ) + + def test_make_sure_we_dont_remove_any_command(): # thanks to: http://stackoverflow.com/a/18161115/597097 from planetmint.commands.planetmint import create_parser @@ -50,22 +147,44 @@ def test_make_sure_we_dont_remove_any_command(): ] ).command assert parser.parse_args( - ["election", "new", "chain-migration", "--private-key", "TEMP_PATH_TO_PRIVATE_KEY"] + [ + "election", + "new", + "chain-migration", + "--private-key", + "TEMP_PATH_TO_PRIVATE_KEY", + ] ).command assert parser.parse_args( - ["election", "approve", "ELECTION_ID", "--private-key", "TEMP_PATH_TO_PRIVATE_KEY"] + [ + "election", + "approve", + "ELECTION_ID", + "--private-key", + "TEMP_PATH_TO_PRIVATE_KEY", + ] ).command assert parser.parse_args(["election", "show", "ELECTION_ID"]).command assert parser.parse_args(["tendermint-version"]).command -@patch("planetmint.commands.utils.start") -def test_main_entrypoint(mock_start): - from planetmint.commands.planetmint import main +@pytest.mark.bdb +def test_election_approve_called_with_bad_key( + monkeypatch, caplog, b, bad_validator_path, new_validator, node_key, test_abci_rpc +): + from argparse import Namespace - main() + b, election_id = call_election(monkeypatch, b, new_validator, node_key, test_abci_rpc) - assert mock_start.called + # call run_upsert_validator_approve with args that point to the election, but a bad signing key + args = Namespace(action="approve", election_id=election_id, sk=bad_validator_path, config={}) + + with caplog.at_level(logging.ERROR): + assert not run_election_approve(args, b, test_abci_rpc) + assert ( + caplog.records[0].msg == "The key you provided does not match any of " + "the eligible voters in this election." + ) @patch("planetmint.config_utils.setup_logging") @@ -168,7 +287,10 @@ def test_drop_db_does_not_drop_when_interactive_no(mock_db_drop, monkeypatch): # switch with pytest. It will just hang. Seems related to the monkeypatching of # input_on_stderr. def test_run_configure_when_config_does_not_exist( - monkeypatch, mock_write_config, mock_generate_key_pair, mock_planetmint_backup_config + monkeypatch, + mock_write_config, + mock_generate_key_pair, + mock_planetmint_backup_config, ): from planetmint.commands.planetmint import run_configure @@ -180,7 +302,10 @@ def test_run_configure_when_config_does_not_exist( def test_run_configure_when_config_does_exist( - monkeypatch, mock_write_config, mock_generate_key_pair, mock_planetmint_backup_config + monkeypatch, + mock_write_config, + mock_generate_key_pair, + mock_planetmint_backup_config, ): value = {} @@ -329,28 +454,34 @@ def test_election_new_upsert_validator_with_tendermint(b, priv_validator_path, u @pytest.mark.bdb -def test_election_new_upsert_validator_without_tendermint(caplog, b, priv_validator_path, user_sk, test_abci_rpc): - def mock_write(modelist, endpoint, mode_commit, transaction, mode): +def test_election_new_upsert_validator_without_tendermint( + monkeypatch, caplog, b, priv_validator_path, user_sk, test_abci_rpc +): + def mock_write(self, modelist, endpoint, mode_commit, transaction, mode): b.models.store_bulk_transactions([transaction]) return (202, "") - b.models.get_validators = mock_get_validators - test_abci_rpc.write_transaction = mock_write + with monkeypatch.context() as m: + from planetmint.model.dataaccessor import DataAccessor - args = Namespace( - action="new", - election_type="upsert-validator", - public_key="CJxdItf4lz2PwEf4SmYNAu/c/VpmX39JEgC5YpH7fxg=", - power=1, - node_id="fb7140f03a4ffad899fabbbf655b97e0321add66", - sk=priv_validator_path, - config={}, - ) + m.setattr(DataAccessor, "get_validators", mock_get_validators) + m.setattr("planetmint.abci.rpc.ABCI_RPC.write_transaction", mock_write) - with caplog.at_level(logging.INFO): - election_id = run_election_new_upsert_validator(args, b, test_abci_rpc) - assert caplog.records[0].msg == "[SUCCESS] Submitted proposal with id: " + election_id - assert b.models.get_transaction(election_id) + args = Namespace( + action="new", + election_type="upsert-validator", + public_key="CJxdItf4lz2PwEf4SmYNAu/c/VpmX39JEgC5YpH7fxg=", + power=1, + node_id="fb7140f03a4ffad899fabbbf655b97e0321add66", + sk=priv_validator_path, + config={}, + ) + + with caplog.at_level(logging.INFO): + election_id = run_election_new_upsert_validator(args, b, test_abci_rpc) + assert caplog.records[0].msg == "[SUCCESS] Submitted proposal with id: " + election_id + assert b.models.get_transaction(election_id) + m.undo() @pytest.mark.abci @@ -363,20 +494,25 @@ def test_election_new_chain_migration_with_tendermint(b, priv_validator_path, us @pytest.mark.bdb -def test_election_new_chain_migration_without_tendermint(caplog, b, priv_validator_path, user_sk, test_abci_rpc): - def mock_write(modelist, endpoint, mode_commit, transaction, mode): +def test_election_new_chain_migration_without_tendermint( + monkeypatch, caplog, b, priv_validator_path, user_sk, test_abci_rpc +): + def mock_write(self, modelist, endpoint, mode_commit, transaction, mode): b.models.store_bulk_transactions([transaction]) return (202, "") - b.models.get_validators = mock_get_validators - test_abci_rpc.write_transaction = mock_write + with monkeypatch.context() as m: + from planetmint.model.dataaccessor import DataAccessor - args = Namespace(action="new", election_type="migration", sk=priv_validator_path, config={}) + m.setattr(DataAccessor, "get_validators", mock_get_validators) + m.setattr("planetmint.abci.rpc.ABCI_RPC.write_transaction", mock_write) - with caplog.at_level(logging.INFO): - election_id = run_election_new_chain_migration(args, b, test_abci_rpc) - assert caplog.records[0].msg == "[SUCCESS] Submitted proposal with id: " + election_id - assert b.models.get_transaction(election_id) + args = Namespace(action="new", election_type="migration", sk=priv_validator_path, config={}) + + with caplog.at_level(logging.INFO): + election_id = run_election_new_chain_migration(args, b, test_abci_rpc) + assert caplog.records[0].msg == "[SUCCESS] Submitted proposal with id: " + election_id + assert b.models.get_transaction(election_id) @pytest.mark.bdb @@ -397,28 +533,34 @@ def test_election_new_upsert_validator_invalid_election(caplog, b, priv_validato @pytest.mark.bdb -def test_election_new_upsert_validator_invalid_power(caplog, b, priv_validator_path, user_sk, test_abci_rpc): +def test_election_new_upsert_validator_invalid_power( + monkeypatch, caplog, b, priv_validator_path, user_sk, test_abci_rpc +): from transactions.common.exceptions import InvalidPowerChange - def mock_write(modelist, endpoint, mode_commit, transaction, mode): + def mock_write(self, modelist, endpoint, mode_commit, transaction, mode): b.models.store_bulk_transactions([transaction]) return (400, "") - test_abci_rpc.write_transaction = mock_write - b.models.get_validators = mock_get_validators - args = Namespace( - action="new", - election_type="upsert-validator", - public_key="CJxdItf4lz2PwEf4SmYNAu/c/VpmX39JEgC5YpH7fxg=", - power=10, - node_id="fb7140f03a4ffad899fabbbf655b97e0321add66", - sk=priv_validator_path, - config={}, - ) + with monkeypatch.context() as m: + from planetmint.model.dataaccessor import DataAccessor - with caplog.at_level(logging.ERROR): - assert not run_election_new_upsert_validator(args, b, test_abci_rpc) - assert caplog.records[0].msg.__class__ == InvalidPowerChange + m.setattr(DataAccessor, "get_validators", mock_get_validators) + m.setattr("planetmint.abci.rpc.ABCI_RPC.write_transaction", mock_write) + + args = Namespace( + action="new", + election_type="upsert-validator", + public_key="CJxdItf4lz2PwEf4SmYNAu/c/VpmX39JEgC5YpH7fxg=", + power=10, + node_id="fb7140f03a4ffad899fabbbf655b97e0321add66", + sk=priv_validator_path, + config={}, + ) + + with caplog.at_level(logging.ERROR): + assert not run_election_new_upsert_validator(args, b, test_abci_rpc) + assert caplog.records[0].msg.__class__ == InvalidPowerChange @pytest.mark.abci @@ -444,27 +586,43 @@ def test_election_approve_with_tendermint(b, priv_validator_path, user_sk, valid @pytest.mark.bdb -def test_election_approve_without_tendermint(caplog, b, priv_validator_path, new_validator, node_key, test_abci_rpc): +def test_election_approve_without_tendermint( + monkeypatch, caplog, b, priv_validator_path, new_validator, node_key, test_abci_rpc +): + def mock_write(self, modelist, endpoint, mode_commit, transaction, mode): + b.models.store_bulk_transactions([transaction]) + return (202, "") + from planetmint.commands.planetmint import run_election_approve from argparse import Namespace - b, election_id = call_election(b, new_validator, node_key, test_abci_rpc) + with monkeypatch.context() as m: + from planetmint.model.dataaccessor import DataAccessor - # call run_election_approve with args that point to the election - args = Namespace(action="approve", election_id=election_id, sk=priv_validator_path, config={}) + m.setattr(DataAccessor, "get_validators", mock_get_validators) + m.setattr("planetmint.abci.rpc.ABCI_RPC.write_transaction", mock_write) - # assert returned id is in the db - with caplog.at_level(logging.INFO): - approval_id = run_election_approve(args, b, test_abci_rpc) - assert caplog.records[0].msg == "[SUCCESS] Your vote has been submitted" - assert b.models.get_transaction(approval_id) + b, election_id = call_election_internal(b, new_validator, node_key) + + # call run_election_approve with args that point to the election + args = Namespace(action="approve", election_id=election_id, sk=priv_validator_path, config={}) + + # assert returned id is in the db + with caplog.at_level(logging.INFO): + approval_id = run_election_approve(args, b, test_abci_rpc) + assert caplog.records[0].msg == "[SUCCESS] Your vote has been submitted" + assert b.models.get_transaction(approval_id) + m.undo() + + +from unittest import mock @pytest.mark.bdb -def test_election_approve_failure(caplog, b, priv_validator_path, new_validator, node_key, test_abci_rpc): +def test_election_approve_failure(monkeypatch, caplog, b, priv_validator_path, new_validator, node_key, test_abci_rpc): from argparse import Namespace - b, election_id = call_election(b, new_validator, node_key, test_abci_rpc) + b, election_id = call_election(monkeypatch, b, new_validator, node_key, test_abci_rpc) def mock_write(modelist, endpoint, mode_commit, transaction, mode): b.models.store_bulk_transactions([transaction]) @@ -480,91 +638,6 @@ def test_election_approve_failure(caplog, b, priv_validator_path, new_validator, assert caplog.records[0].msg == "Failed to commit vote" -@pytest.mark.bdb -def test_election_approve_called_with_bad_key(caplog, b, bad_validator_path, new_validator, node_key, test_abci_rpc): - from argparse import Namespace - - b, election_id = call_election(b, new_validator, node_key, test_abci_rpc) - - # call run_upsert_validator_approve with args that point to the election, but a bad signing key - args = Namespace(action="approve", election_id=election_id, sk=bad_validator_path, config={}) - - with caplog.at_level(logging.ERROR): - assert not run_election_approve(args, b, test_abci_rpc) - assert ( - caplog.records[0].msg == "The key you provided does not match any of " - "the eligible voters in this election." - ) - - -@pytest.mark.bdb -def test_chain_migration_election_show_shows_inconclusive(b): - validators = generate_validators([1] * 4) - b.models.store_validator_set(1, [v["storage"] for v in validators]) - - 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, ChainMigrationElection, public_key, private_key, [{"data": {}}], voter_keys) - - assert not run_election_show(Namespace(election_id=election.id), b) - - b.process_block(1, [election]) - b.models.store_bulk_transactions([election]) - - assert run_election_show(Namespace(election_id=election.id), b) == "status=ongoing" - - b.models.store_block(Block(height=1, transactions=[], app_hash="")._asdict()) - b.models.store_validator_set(2, [v["storage"] for v in validators]) - - assert run_election_show(Namespace(election_id=election.id), b) == "status=ongoing" - - b.models.store_block(Block(height=2, transactions=[], app_hash="")._asdict()) - # TODO insert yet another block here when upgrading to Tendermint 0.22.4. - - assert run_election_show(Namespace(election_id=election.id), b) == "status=inconclusive" - - -@pytest.mark.bdb -def test_chain_migration_election_show_shows_concluded(b): - validators = generate_validators([1] * 4) - b.models.store_validator_set(1, [v["storage"] for v in validators]) - - 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, ChainMigrationElection, public_key, private_key, [{"data": {}}], voter_keys) - - assert not run_election_show(Namespace(election_id=election.id), b) - - b.models.store_bulk_transactions([election]) - b.process_block(1, [election]) - - assert run_election_show(Namespace(election_id=election.id), b) == "status=ongoing" - - b.models.store_abci_chain(1, "chain-X") - b.models.store_block(Block(height=1, transactions=[v.id for v in votes], app_hash="last_app_hash")._asdict()) - b.process_block(2, votes) - - assert ( - run_election_show(Namespace(election_id=election.id), b) - == f'''status=concluded -chain_id=chain-X-migrated-at-height-1 -app_hash=last_app_hash -validators=[{''.join([f""" - {{ - "pub_key": {{ - "type": "tendermint/PubKeyEd25519", - "value": "{v['public_key']}" - }}, - "power": {v['storage']['voting_power']} - }}{',' if i + 1 != len(validators) else ''}""" for i, v in enumerate(validators)])} -]''' - ) - - def test_bigchain_tendermint_version(capsys): from planetmint.commands.planetmint import run_tendermint_version @@ -578,24 +651,7 @@ def test_bigchain_tendermint_version(capsys): assert sorted(output_config["tendermint"]) == sorted(__tm_supported_versions__) -def mock_get_validators(height): - return [ - { - "public_key": {"value": "zL/DasvKulXZzhSNFwx4cLRXKkSM9GPK7Y0nZ4FEylM=", "type": "ed25519-base64"}, - "voting_power": 10, - } - ] - - -def call_election(b, new_validator, node_key, abci_rpc): - def mock_write(modelist, endpoint, mode_commit, transaction, mode): - b.models.store_bulk_transactions([transaction]) - return (202, "") - - # patch the validator set. We now have one validator with power 10 - b.models.get_validators = mock_get_validators - abci_rpc.write_transaction = mock_write - +def call_election_internal(b, new_validator, node_key): # our voters is a list of length 1, populated from our mocked validator voters = b.get_recipients_list() # and our voter is the public key from the voter list @@ -607,3 +663,18 @@ def call_election(b, new_validator, node_key, abci_rpc): b.models.store_bulk_transactions([valid_election]) return b, election_id + + +def call_election(monkeypatch, b, new_validator, node_key, abci_rpc): + def mock_write(self, modelist, endpoint, mode_commit, transaction, mode): + b.models.store_bulk_transactions([transaction]) + return (202, "") + + with monkeypatch.context() as m: + from planetmint.model.dataaccessor import DataAccessor + + m.setattr(DataAccessor, "get_validators", mock_get_validators) + m.setattr("planetmint.abci.rpc.ABCI_RPC.write_transaction", mock_write) + b, election_id = call_election_internal(b, new_validator, node_key) + m.undo() + return b, election_id diff --git a/tests/conftest.py b/tests/conftest.py index db20c8c..aed8b30 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -27,7 +27,10 @@ from transactions.common import crypto from transactions.common.transaction_mode_types import BROADCAST_TX_COMMIT from planetmint.abci.utils import key_from_base64 from planetmint.backend import schema, query -from transactions.common.crypto import key_pair_from_ed25519_key, public_key_from_ed25519_key +from transactions.common.crypto import ( + key_pair_from_ed25519_key, + public_key_from_ed25519_key, +) from planetmint.abci.block import Block from planetmint.abci.rpc import MODE_LIST from tests.utils import gen_vote @@ -107,7 +110,10 @@ def _configure_planetmint(request): # backend = request.config.getoption('--database-backend') backend = "tarantool_db" - config = {"database": Config().get_db_map(backend), "tendermint": Config()._private_real_config["tendermint"]} + config = { + "database": Config().get_db_map(backend), + "tendermint": Config()._private_real_config["tendermint"], + } config["database"]["name"] = test_db_name config = config_utils.env_config(config) config_utils.set_config(config) @@ -133,6 +139,28 @@ def _setup_database(_configure_planetmint): # TODO Here is located setup databa print("Finished deleting `{}`".format(dbname)) +@pytest.fixture +def da_reset(_setup_database): + from transactions.common.memoize import to_dict, from_dict + from transactions.common.transaction import Transaction + from .utils import flush_db + from planetmint.model.dataaccessor import DataAccessor + + da = DataAccessor() + del da + da = DataAccessor() + da.close_connection() + da.connect() + + yield + dbname = Config().get()["database"]["name"] + flush_db(da.connection, dbname) + + to_dict.cache_clear() + from_dict.cache_clear() + Transaction._input_valid.cache_clear() + + @pytest.fixture def _bdb(_setup_database): from transactions.common.memoize import to_dict, from_dict @@ -273,6 +301,38 @@ def test_abci_rpc(): def b(): from planetmint.application import Validator + old_validator_instance = Validator() + del old_validator_instance.models + del old_validator_instance + validator = Validator() + validator.models.connection.close() + validator.models.connection.connect() + return validator + + +@pytest.fixture +def b_flushed(_setup_database): + from planetmint.application import Validator + from transactions.common.memoize import to_dict, from_dict + from transactions.common.transaction import Transaction + from .utils import flush_db + from planetmint.config import Config + + old_validator_instance = Validator() + del old_validator_instance.models + del old_validator_instance + + conn = Connection() + conn.close() + conn.connect() + + dbname = Config().get()["database"]["name"] + flush_db(conn, dbname) + + to_dict.cache_clear() + from_dict.cache_clear() + Transaction._input_valid.cache_clear() + validator = Validator() validator.models.connection.close() validator.models.connection.connect() @@ -286,22 +346,6 @@ def eventqueue_fixture(): return Queue() -@pytest.fixture -def b_mock(b, network_validators): - b.models.get_validators = mock_get_validators(network_validators) - return b - - -def mock_get_validators(network_validators): - def validator_set(height): - validators = [] - for public_key, power in network_validators.items(): - validators.append({"public_key": {"type": "ed25519-base64", "value": public_key}, "voting_power": power}) - return validators - - return validator_set - - @pytest.fixture def create_tx(alice, user_pk): from transactions.types.assets.create import Create @@ -319,7 +363,10 @@ def signed_create_tx(alice, create_tx): @pytest.fixture def posted_create_tx(b, signed_create_tx, test_abci_rpc): res = test_abci_rpc.post_transaction( - MODE_LIST, test_abci_rpc.tendermint_rpc_endpoint, signed_create_tx, BROADCAST_TX_COMMIT + MODE_LIST, + test_abci_rpc.tendermint_rpc_endpoint, + signed_create_tx, + BROADCAST_TX_COMMIT, ) assert res.status_code == 200 return signed_create_tx @@ -356,7 +403,9 @@ def inputs(user_pk, b, alice): for height in range(1, 4): transactions = [ Create.generate( - [alice.public_key], [([user_pk], 1)], metadata=multihash(marshal({"data": f"{random.random()}"})) + [alice.public_key], + [([user_pk], 1)], + metadata=multihash(marshal({"data": f"{random.random()}"})), ).sign([alice.private_key]) for _ in range(10) ] @@ -428,7 +477,13 @@ def _abci_http(request): @pytest.fixture -def abci_http(_setup_database, _configure_planetmint, abci_server, tendermint_host, tendermint_port): +def abci_http( + _setup_database, + _configure_planetmint, + abci_server, + tendermint_host, + tendermint_port, +): import requests import time @@ -632,19 +687,19 @@ def new_validator(): node_id = "fake_node_id" return [ - {"data": {"public_key": {"value": public_key, "type": "ed25519-base16"}, "power": power, "node_id": node_id}} + { + "data": { + "public_key": {"value": public_key, "type": "ed25519-base16"}, + "power": power, + "node_id": node_id, + } + } ] @pytest.fixture -def valid_upsert_validator_election(b_mock, node_key, new_validator): - voters = b_mock.get_recipients_list() - return ValidatorElection.generate([node_key.public_key], voters, new_validator, None).sign([node_key.private_key]) - - -@pytest.fixture -def valid_upsert_validator_election_2(b_mock, node_key, new_validator): - voters = b_mock.get_recipients_list() +def valid_upsert_validator_election(b, node_key, new_validator): + voters = b.get_recipients_list() return ValidatorElection.generate([node_key.public_key], voters, new_validator, None).sign([node_key.private_key]) @@ -660,40 +715,6 @@ def ongoing_validator_election(b, valid_upsert_validator_election, ed25519_node_ return valid_upsert_validator_election -@pytest.fixture -def ongoing_validator_election_2(b, valid_upsert_validator_election_2, ed25519_node_keys): - validators = b.models.get_validators(height=1) - genesis_validators = {"validators": validators, "height": 0, "election_id": None} - query.store_validator_set(b.models.connection, genesis_validators) - - b.models.store_bulk_transactions([valid_upsert_validator_election_2]) - block_1 = Block(app_hash="hash_2", height=1, transactions=[valid_upsert_validator_election_2.id]) - b.models.store_block(block_1._asdict()) - return valid_upsert_validator_election_2 - - -@pytest.fixture -def validator_election_votes(b_mock, ongoing_validator_election, ed25519_node_keys): - voters = b_mock.get_recipients_list() - votes = generate_votes(ongoing_validator_election, voters, ed25519_node_keys) - return votes - - -@pytest.fixture -def validator_election_votes_2(b_mock, ongoing_validator_election_2, ed25519_node_keys): - voters = b_mock.get_recipients_list() - votes = generate_votes(ongoing_validator_election_2, voters, ed25519_node_keys) - return votes - - -def generate_votes(election, voters, keys): - votes = [] - for voter, _ in enumerate(voters): - v = gen_vote(election, voter, keys) - votes.append(v) - return votes - - @pytest.fixture def signed_2_0_create_tx(): return { diff --git a/tests/elections/test_election.py b/tests/elections/test_election.py index 032ed28..d016668 100644 --- a/tests/elections/test_election.py +++ b/tests/elections/test_election.py @@ -5,10 +5,14 @@ from planetmint.abci.block import Block from transactions.types.elections.election import Election from transactions.types.elections.chain_migration_election import ChainMigrationElection from transactions.types.elections.validator_election import ValidatorElection +from planetmint.model.dataaccessor import DataAccessor @pytest.mark.bdb def test_process_block_concludes_all_elections(b): + del b.models + b.models = DataAccessor() + b.models.connect() validators = generate_validators([1] * 4) b.models.store_validator_set(1, [v["storage"] for v in validators]) diff --git a/tests/migrations/test_migration_election.py b/tests/migrations/test_migration_election.py index efedf7f..e3dde5b 100644 --- a/tests/migrations/test_migration_election.py +++ b/tests/migrations/test_migration_election.py @@ -1,9 +1,28 @@ +import pytest from transactions.types.elections.chain_migration_election import ChainMigrationElection -def test_valid_migration_election(b_mock, node_key): - voters = b_mock.get_recipients_list() - election = ChainMigrationElection.generate([node_key.public_key], voters, [{"data": {}}], None).sign( - [node_key.private_key] - ) - assert b_mock.validate_election(election) +@pytest.mark.bdb +def test_valid_migration_election(monkeypatch, b, node_key, network_validators): + def mock_get_validators(self, height): + validators = [] + for public_key, power in network_validators.items(): + validators.append( + { + "public_key": {"type": "ed25519-base64", "value": public_key}, + "voting_power": power, + } + ) + return validators + + with monkeypatch.context() as m: + from planetmint.model.dataaccessor import DataAccessor + + m.setattr(DataAccessor, "get_validators", mock_get_validators) + + voters = b.get_recipients_list() + election = ChainMigrationElection.generate([node_key.public_key], voters, [{"data": {}}], None).sign( + [node_key.private_key] + ) + assert b.validate_election(election) + m.undo() diff --git a/tests/tendermint/test_core.py b/tests/tendermint/test_core.py index 29771c0..8bd99b0 100644 --- a/tests/tendermint/test_core.py +++ b/tests/tendermint/test_core.py @@ -46,6 +46,7 @@ def generate_init_chain_request(chain_id, vals=None): return types.RequestInitChain(validators=vals, chain_id=chain_id) +@pytest.mark.bdb def test_init_chain_successfully_registers_chain(b): request = generate_init_chain_request("chain-XYZ") res = ApplicationLogic(validator=b).init_chain(request) diff --git a/tests/upsert_validator/conftest.py b/tests/upsert_validator/conftest.py index 3ac8eb8..7787f2f 100644 --- a/tests/upsert_validator/conftest.py +++ b/tests/upsert_validator/conftest.py @@ -11,15 +11,15 @@ from transactions.types.elections.validator_election import ValidatorElection @pytest.fixture -def valid_upsert_validator_election_b(b, node_key, new_validator): +def valid_upsert_validator_election(b, node_key, new_validator): voters = b.get_recipients_list() return ValidatorElection.generate([node_key.public_key], voters, new_validator, None).sign([node_key.private_key]) @pytest.fixture @patch("transactions.types.elections.election.uuid4", lambda: "mock_uuid4") -def fixed_seed_election(b_mock, node_key, new_validator): - voters = b_mock.get_recipients_list() +def fixed_seed_election(b, node_key, new_validator): + voters = b.get_recipients_list() return ValidatorElection.generate([node_key.public_key], voters, new_validator, None).sign([node_key.private_key]) diff --git a/tests/upsert_validator/test_upsert_validator_vote.py b/tests/upsert_validator/test_upsert_validator_vote.py index 6046ef9..cdf4355 100644 --- a/tests/upsert_validator/test_upsert_validator_vote.py +++ b/tests/upsert_validator/test_upsert_validator_vote.py @@ -6,6 +6,7 @@ import pytest import codecs +from planetmint.model.dataaccessor import DataAccessor from planetmint.abci.rpc import MODE_LIST, MODE_COMMIT from planetmint.abci.utils import public_key_to_base64 @@ -22,196 +23,290 @@ from tests.utils import generate_block, gen_vote pytestmark = [pytest.mark.execute] -@pytest.mark.bdb -def test_upsert_validator_valid_election_vote(b_mock, valid_upsert_validator_election, ed25519_node_keys): - b_mock.models.store_bulk_transactions([valid_upsert_validator_election]) +# helper +def get_valid_upsert_election(m, b, mock_get_validators, node_key, new_validator): + m.setattr(DataAccessor, "get_validators", mock_get_validators) + voters = b.get_recipients_list() + valid_upsert_validator_election = ValidatorElection.generate( + [node_key.public_key], voters, new_validator, None + ).sign([node_key.private_key]) + b.models.store_bulk_transactions([valid_upsert_validator_election]) + return valid_upsert_validator_election + + +# helper +def get_voting_set(valid_upsert_validator_election, ed25519_node_keys): 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] - - election_pub_key = election_id_to_public_key(valid_upsert_validator_election.id) - - vote = Vote.generate( - [input0], [([election_pub_key], votes)], election_ids=[valid_upsert_validator_election.id] - ).sign([key0.private_key]) - assert b_mock.validate_transaction(vote) + return input0, votes, key0 @pytest.mark.bdb -def test_upsert_validator_valid_non_election_vote(b_mock, valid_upsert_validator_election, ed25519_node_keys): - b_mock.models.store_bulk_transactions([valid_upsert_validator_election]) +def test_upsert_validator_valid_election_vote( + monkeypatch, b, network_validators, new_validator, node_key, ed25519_node_keys +): + def mock_get_validators(self, height): + validators = [] + for public_key, power in network_validators.items(): + validators.append( + { + "public_key": {"type": "ed25519-base64", "value": public_key}, + "voting_power": power, + } + ) + return validators - 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] + with monkeypatch.context() as m: + valid_upsert_validator_election = get_valid_upsert_election(m, b, mock_get_validators, node_key, new_validator) + input0, votes, key0 = get_voting_set(valid_upsert_validator_election, ed25519_node_keys) - election_pub_key = election_id_to_public_key(valid_upsert_validator_election.id) + election_pub_key = election_id_to_public_key(valid_upsert_validator_election.id) - # Ensure that threshold conditions are now allowed - with pytest.raises(ValidationError): - Vote.generate( - [input0], [([election_pub_key, key0.public_key], votes)], election_ids=[valid_upsert_validator_election.id] + vote = Vote.generate( + [input0], [([election_pub_key], votes)], election_ids=[valid_upsert_validator_election.id] + ).sign([key0.private_key]) + assert b.validate_transaction(vote) + m.undo() + + +@pytest.mark.bdb +def test_upsert_validator_valid_non_election_vote( + monkeypatch, b, network_validators, node_key, new_validator, ed25519_node_keys +): + def mock_get_validators(self, height): + validators = [] + for public_key, power in network_validators.items(): + validators.append( + { + "public_key": {"type": "ed25519-base64", "value": public_key}, + "voting_power": power, + } + ) + return validators + + with monkeypatch.context() as m: + valid_upsert_validator_election = get_valid_upsert_election(m, b, mock_get_validators, node_key, new_validator) + input0, votes, key0 = get_voting_set(valid_upsert_validator_election, ed25519_node_keys) + + election_pub_key = election_id_to_public_key(valid_upsert_validator_election.id) + + # Ensure that threshold conditions are now allowed + with pytest.raises(ValidationError): + Vote.generate( + [input0], + [([election_pub_key, key0.public_key], votes)], + election_ids=[valid_upsert_validator_election.id], + ).sign([key0.private_key]) + m.undo() + + +@pytest.mark.bdb +def test_upsert_validator_delegate_election_vote( + monkeypatch, b, network_validators, node_key, new_validator, ed25519_node_keys +): + def mock_get_validators(self, height): + validators = [] + for public_key, power in network_validators.items(): + validators.append( + { + "public_key": {"type": "ed25519-base64", "value": public_key}, + "voting_power": power, + } + ) + return validators + + with monkeypatch.context() as m: + valid_upsert_validator_election = get_valid_upsert_election(m, b, mock_get_validators, node_key, new_validator) + alice = generate_key_pair() + input0, votes, key0 = get_voting_set(valid_upsert_validator_election, ed25519_node_keys) + + delegate_vote = Vote.generate( + [input0], + [([alice.public_key], 3), ([key0.public_key], votes - 3)], + election_ids=[valid_upsert_validator_election.id], ).sign([key0.private_key]) + assert b.validate_transaction(delegate_vote) -@pytest.mark.bdb -def test_upsert_validator_delegate_election_vote(b_mock, valid_upsert_validator_election, ed25519_node_keys): - alice = generate_key_pair() + b.models.store_bulk_transactions([delegate_vote]) + election_pub_key = election_id_to_public_key(valid_upsert_validator_election.id) - b_mock.models.store_bulk_transactions([valid_upsert_validator_election]) + alice_votes = delegate_vote.to_inputs()[0] + alice_casted_vote = Vote.generate( + [alice_votes], [([election_pub_key], 3)], election_ids=[valid_upsert_validator_election.id] + ).sign([alice.private_key]) + assert b.validate_transaction(alice_casted_vote) - 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_ids=[valid_upsert_validator_election.id], - ).sign([key0.private_key]) - - assert b_mock.validate_transaction(delegate_vote) - - b_mock.models.store_bulk_transactions([delegate_vote]) - election_pub_key = election_id_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_ids=[valid_upsert_validator_election.id] - ).sign([alice.private_key]) - assert b_mock.validate_transaction(alice_casted_vote) - - key0_votes = delegate_vote.to_inputs()[1] - key0_casted_vote = Vote.generate( - [key0_votes], [([election_pub_key], votes - 3)], election_ids=[valid_upsert_validator_election.id] - ).sign([key0.private_key]) - assert b_mock.validate_transaction(key0_casted_vote) + key0_votes = delegate_vote.to_inputs()[1] + key0_casted_vote = Vote.generate( + [key0_votes], [([election_pub_key], votes - 3)], election_ids=[valid_upsert_validator_election.id] + ).sign([key0.private_key]) + assert b.validate_transaction(key0_casted_vote) + m.undo() @pytest.mark.bdb -def test_upsert_validator_invalid_election_vote(b_mock, valid_upsert_validator_election, ed25519_node_keys): - b_mock.models.store_bulk_transactions([valid_upsert_validator_election]) +def test_upsert_validator_invalid_election_vote( + monkeypatch, b, network_validators, node_key, new_validator, ed25519_node_keys +): + def mock_get_validators(self, height): + validators = [] + for public_key, power in network_validators.items(): + validators.append( + { + "public_key": {"type": "ed25519-base64", "value": public_key}, + "voting_power": power, + } + ) + return validators - 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] + with monkeypatch.context() as m: + valid_upsert_validator_election = get_valid_upsert_election(m, b, mock_get_validators, node_key, new_validator) + input0, votes, key0 = get_voting_set(valid_upsert_validator_election, ed25519_node_keys) - election_pub_key = election_id_to_public_key(valid_upsert_validator_election.id) + election_pub_key = election_id_to_public_key(valid_upsert_validator_election.id) - vote = Vote.generate( - [input0], [([election_pub_key], votes + 1)], election_ids=[valid_upsert_validator_election.id] - ).sign([key0.private_key]) + vote = Vote.generate( + [input0], [([election_pub_key], votes + 1)], election_ids=[valid_upsert_validator_election.id] + ).sign([key0.private_key]) - with pytest.raises(AmountError): - assert b_mock.validate_transaction(vote) + with pytest.raises(AmountError): + assert b.validate_transaction(vote) @pytest.mark.bdb -def test_valid_election_votes_received(b_mock, valid_upsert_validator_election, ed25519_node_keys): - alice = generate_key_pair() - b_mock.models.store_bulk_transactions([valid_upsert_validator_election]) - assert b_mock.get_commited_votes(valid_upsert_validator_election) == 0 +def test_valid_election_votes_received(monkeypatch, b, network_validators, node_key, new_validator, ed25519_node_keys): + def mock_get_validators(self, height): + validators = [] + for public_key, power in network_validators.items(): + validators.append( + { + "public_key": {"type": "ed25519-base64", "value": public_key}, + "voting_power": power, + } + ) + return validators - 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] + with monkeypatch.context() as m: + valid_upsert_validator_election = get_valid_upsert_election(m, b, mock_get_validators, node_key, new_validator) + alice = generate_key_pair() - # delegate some votes to alice - delegate_vote = Vote.generate( - [input0], - [([alice.public_key], 4), ([key0.public_key], votes - 4)], - election_ids=[valid_upsert_validator_election.id], - ).sign([key0.private_key]) - b_mock.models.store_bulk_transactions([delegate_vote]) - assert b_mock.get_commited_votes(valid_upsert_validator_election) == 0 + assert b.get_commited_votes(valid_upsert_validator_election) == 0 + input0, votes, key0 = get_voting_set(valid_upsert_validator_election, ed25519_node_keys) - election_public_key = election_id_to_public_key(valid_upsert_validator_election.id) - alice_votes = delegate_vote.to_inputs()[0] - key0_votes = delegate_vote.to_inputs()[1] + # delegate some votes to alice + delegate_vote = Vote.generate( + [input0], + [([alice.public_key], 4), ([key0.public_key], votes - 4)], + election_ids=[valid_upsert_validator_election.id], + ).sign([key0.private_key]) + b.models.store_bulk_transactions([delegate_vote]) + assert b.get_commited_votes(valid_upsert_validator_election) == 0 - alice_casted_vote = Vote.generate( - [alice_votes], - [([election_public_key], 2), ([alice.public_key], 2)], - election_ids=[valid_upsert_validator_election.id], - ).sign([alice.private_key]) + election_public_key = election_id_to_public_key(valid_upsert_validator_election.id) + alice_votes = delegate_vote.to_inputs()[0] + key0_votes = delegate_vote.to_inputs()[1] - assert b_mock.validate_transaction(alice_casted_vote) - b_mock.models.store_bulk_transactions([alice_casted_vote]) + alice_casted_vote = Vote.generate( + [alice_votes], + [([election_public_key], 2), ([alice.public_key], 2)], + election_ids=[valid_upsert_validator_election.id], + ).sign([alice.private_key]) - # Check if the delegated vote is count as valid vote - assert b_mock.get_commited_votes(valid_upsert_validator_election) == 2 + assert b.validate_transaction(alice_casted_vote) + b.models.store_bulk_transactions([alice_casted_vote]) - key0_casted_vote = Vote.generate( - [key0_votes], [([election_public_key], votes - 4)], election_ids=[valid_upsert_validator_election.id] - ).sign([key0.private_key]) + # Check if the delegated vote is count as valid vote + assert b.get_commited_votes(valid_upsert_validator_election) == 2 - assert b_mock.validate_transaction(key0_casted_vote) - b_mock.models.store_bulk_transactions([key0_casted_vote]) - assert b_mock.get_commited_votes(valid_upsert_validator_election) == votes - 2 + key0_casted_vote = Vote.generate( + [key0_votes], [([election_public_key], votes - 4)], election_ids=[valid_upsert_validator_election.id] + ).sign([key0.private_key]) + + assert b.validate_transaction(key0_casted_vote) + b.models.store_bulk_transactions([key0_casted_vote]) + assert b.get_commited_votes(valid_upsert_validator_election) == votes - 2 @pytest.mark.bdb -def test_valid_election_conclude(b_mock, valid_upsert_validator_election, ed25519_node_keys): - # Node 0: cast vote - tx_vote0 = gen_vote(valid_upsert_validator_election, 0, ed25519_node_keys) +def test_valid_election_conclude(monkeypatch, b, network_validators, node_key, new_validator, ed25519_node_keys): + def mock_get_validators(self, height): + validators = [] + for public_key, power in network_validators.items(): + validators.append( + { + "public_key": {"type": "ed25519-base64", "value": public_key}, + "voting_power": power, + } + ) + return validators - # check if the vote is valid even before the election doesn't exist - with pytest.raises(ValidationError): - assert b_mock.validate_transaction(tx_vote0) + with monkeypatch.context() as m: + from planetmint.model.dataaccessor import DataAccessor - # store election - b_mock.models.store_bulk_transactions([valid_upsert_validator_election]) - # cannot conclude election as not votes exist - assert not b_mock.has_election_concluded(valid_upsert_validator_election) + m.setattr(DataAccessor, "get_validators", mock_get_validators) + voters = b.get_recipients_list() + valid_upsert_validator_election = ValidatorElection.generate( + [node_key.public_key], voters, new_validator, None + ).sign([node_key.private_key]) - # validate vote - assert b_mock.validate_transaction(tx_vote0) - assert not b_mock.has_election_concluded(valid_upsert_validator_election, [tx_vote0]) + # Node 0: cast vote + tx_vote0 = gen_vote(valid_upsert_validator_election, 0, ed25519_node_keys) - b_mock.models.store_bulk_transactions([tx_vote0]) - assert not b_mock.has_election_concluded(valid_upsert_validator_election) + # check if the vote is valid even before the election doesn't exist + with pytest.raises(ValidationError): + assert b.validate_transaction(tx_vote0) - # Node 1: cast vote - tx_vote1 = gen_vote(valid_upsert_validator_election, 1, ed25519_node_keys) + # store election + b.models.store_bulk_transactions([valid_upsert_validator_election]) + # cannot conclude election as not votes exist + assert not b.has_election_concluded(valid_upsert_validator_election) - # Node 2: cast vote - tx_vote2 = gen_vote(valid_upsert_validator_election, 2, ed25519_node_keys) + # validate vote + assert b.validate_transaction(tx_vote0) + assert not b.has_election_concluded(valid_upsert_validator_election, [tx_vote0]) - # Node 3: cast vote - tx_vote3 = gen_vote(valid_upsert_validator_election, 3, ed25519_node_keys) + b.models.store_bulk_transactions([tx_vote0]) + assert not b.has_election_concluded(valid_upsert_validator_election) - assert b_mock.validate_transaction(tx_vote1) - assert not b_mock.has_election_concluded(valid_upsert_validator_election, [tx_vote1]) + # Node 1: cast vote + tx_vote1 = gen_vote(valid_upsert_validator_election, 1, ed25519_node_keys) - # 2/3 is achieved in the same block so the election can be.has_concludedd - assert b_mock.has_election_concluded(valid_upsert_validator_election, [tx_vote1, tx_vote2]) + # Node 2: cast vote + tx_vote2 = gen_vote(valid_upsert_validator_election, 2, ed25519_node_keys) - b_mock.models.store_bulk_transactions([tx_vote1]) - assert not b_mock.has_election_concluded(valid_upsert_validator_election) + # Node 3: cast vote + tx_vote3 = gen_vote(valid_upsert_validator_election, 3, ed25519_node_keys) - assert b_mock.validate_transaction(tx_vote2) - assert b_mock.validate_transaction(tx_vote3) + assert b.validate_transaction(tx_vote1) + assert not b.has_election_concluded(valid_upsert_validator_election, [tx_vote1]) - # conclusion can be triggered my different votes in the same block - assert b_mock.has_election_concluded(valid_upsert_validator_election, [tx_vote2]) - assert b_mock.has_election_concluded(valid_upsert_validator_election, [tx_vote2, tx_vote3]) + # 2/3 is achieved in the same block so the election can be.has_concludedd + assert b.has_election_concluded(valid_upsert_validator_election, [tx_vote1, tx_vote2]) - b_mock.models.store_bulk_transactions([tx_vote2]) + b.models.store_bulk_transactions([tx_vote1]) + assert not b.has_election_concluded(valid_upsert_validator_election) - # Once the blockchain records >2/3 of the votes the election is assumed to be.has_concludedd - # so any invocation of `.has_concluded` for that election should return False - assert not b_mock.has_election_concluded(valid_upsert_validator_election) + assert b.validate_transaction(tx_vote2) + assert b.validate_transaction(tx_vote3) - # Vote is still valid but the election cannot be.has_concluded as it it assumed that it has - # been.has_concludedd before - assert b_mock.validate_transaction(tx_vote3) - assert not b_mock.has_election_concluded(valid_upsert_validator_election, [tx_vote3]) + # conclusion can be triggered my different votes in the same block + assert b.has_election_concluded(valid_upsert_validator_election, [tx_vote2]) + assert b.has_election_concluded(valid_upsert_validator_election, [tx_vote2, tx_vote3]) + + b.models.store_bulk_transactions([tx_vote2]) + + # Once the blockchain records >2/3 of the votes the election is assumed to be.has_concludedd + # so any invocation of `.has_concluded` for that election should return False + assert not b.has_election_concluded(valid_upsert_validator_election) + + # Vote is still valid but the election cannot be.has_concluded as it it assumed that it has + # been.has_concludedd before + assert b.validate_transaction(tx_vote3) + assert not b.has_election_concluded(valid_upsert_validator_election, [tx_vote3]) @pytest.mark.abci diff --git a/tests/upsert_validator/test_validator_election.py b/tests/upsert_validator/test_validator_election.py index 57c1bc1..bea3e30 100644 --- a/tests/upsert_validator/test_validator_election.py +++ b/tests/upsert_validator/test_validator_election.py @@ -21,110 +21,280 @@ from transactions.common.exceptions import ( pytestmark = pytest.mark.bdb -def test_upsert_validator_valid_election(b_mock, new_validator, node_key): - voters = b_mock.get_recipients_list() - election = ValidatorElection.generate([node_key.public_key], voters, new_validator, None).sign( - [node_key.private_key] - ) - assert b_mock.validate_election(election) +def test_upsert_validator_valid_election(monkeypatch, b, network_validators, new_validator, node_key): + def mock_get_validators(self, height): + validators = [] + for public_key, power in network_validators.items(): + validators.append( + { + "public_key": {"type": "ed25519-base64", "value": public_key}, + "voting_power": power, + } + ) + return validators + + with monkeypatch.context() as m: + from planetmint.model.dataaccessor import DataAccessor + + m.setattr(DataAccessor, "get_validators", mock_get_validators) + voters = b.get_recipients_list() + election = ValidatorElection.generate([node_key.public_key], voters, new_validator, None).sign( + [node_key.private_key] + ) + assert b.validate_election(election) + m.undo() -def test_upsert_validator_invalid_election_public_key(b_mock, new_validator, node_key): - from transactions.common.exceptions import InvalidPublicKey +def test_upsert_validator_invalid_election_public_key(monkeypatch, b, network_validators, new_validator, node_key): + def mock_get_validators(self, height): + validators = [] + for public_key, power in network_validators.items(): + validators.append( + { + "public_key": {"type": "ed25519-base64", "value": public_key}, + "voting_power": power, + } + ) + return validators - for iv in ["ed25519-base32", "ed25519-base64"]: - new_validator[0]["data"]["public_key"]["type"] = iv - voters = b_mock.get_recipients_list() + with monkeypatch.context() as m: + from planetmint.model.dataaccessor import DataAccessor - with pytest.raises(InvalidPublicKey): - ValidatorElection.generate([node_key.public_key], voters, new_validator, None).sign([node_key.private_key]) + m.setattr(DataAccessor, "get_validators", mock_get_validators) + from transactions.common.exceptions import InvalidPublicKey + + for iv in ["ed25519-base32", "ed25519-base64"]: + new_validator[0]["data"]["public_key"]["type"] = iv + voters = b.get_recipients_list() + + with pytest.raises(InvalidPublicKey): + ValidatorElection.generate([node_key.public_key], voters, new_validator, None).sign( + [node_key.private_key] + ) + m.undo() -def test_upsert_validator_invalid_power_election(b_mock, new_validator, node_key): - voters = b_mock.get_recipients_list() - new_validator[0]["data"]["power"] = 30 +def test_upsert_validator_invalid_power_election(monkeypatch, b, network_validators, new_validator, node_key): + def mock_get_validators(self, height): + validators = [] + for public_key, power in network_validators.items(): + validators.append( + { + "public_key": {"type": "ed25519-base64", "value": public_key}, + "voting_power": power, + } + ) + return validators - election = ValidatorElection.generate([node_key.public_key], voters, new_validator, None).sign( - [node_key.private_key] - ) - with pytest.raises(InvalidPowerChange): - b_mock.validate_election(election) + with monkeypatch.context() as m: + from planetmint.model.dataaccessor import DataAccessor + + m.setattr(DataAccessor, "get_validators", mock_get_validators) + voters = b.get_recipients_list() + new_validator[0]["data"]["power"] = 30 + + election = ValidatorElection.generate([node_key.public_key], voters, new_validator, None).sign( + [node_key.private_key] + ) + with pytest.raises(InvalidPowerChange): + b.validate_election(election) + m.undo() -def test_upsert_validator_invalid_proposed_election(b_mock, new_validator, node_key): +def test_upsert_validator_invalid_proposed_election(monkeypatch, b, network_validators, new_validator, node_key): from transactions.common.crypto import generate_key_pair - alice = generate_key_pair() - voters = b_mock.get_recipients_list() - election = ValidatorElection.generate([alice.public_key], voters, new_validator, None).sign([alice.private_key]) - with pytest.raises(InvalidProposer): - b_mock.validate_election(election) + def mock_get_validators(self, height): + validators = [] + for public_key, power in network_validators.items(): + validators.append( + { + "public_key": {"type": "ed25519-base64", "value": public_key}, + "voting_power": power, + } + ) + return validators + + with monkeypatch.context() as m: + from planetmint.model.dataaccessor import DataAccessor + + m.setattr(DataAccessor, "get_validators", mock_get_validators) + + alice = generate_key_pair() + voters = b.get_recipients_list() + election = ValidatorElection.generate([alice.public_key], voters, new_validator, None).sign( + [alice.private_key] + ) + with pytest.raises(InvalidProposer): + b.validate_election(election) -def test_upsert_validator_invalid_inputs_election(b_mock, new_validator, node_key): +def test_upsert_validator_invalid_inputs_election(monkeypatch, b, network_validators, new_validator, node_key): from transactions.common.crypto import generate_key_pair - alice = generate_key_pair() - voters = b_mock.get_recipients_list() - 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): - b_mock.validate_election(election) + def mock_get_validators(self, height): + validators = [] + for public_key, power in network_validators.items(): + validators.append( + { + "public_key": {"type": "ed25519-base64", "value": public_key}, + "voting_power": power, + } + ) + return validators + + with monkeypatch.context() as m: + from planetmint.model.dataaccessor import DataAccessor + + m.setattr(DataAccessor, "get_validators", mock_get_validators) + alice = generate_key_pair() + voters = b.get_recipients_list() + 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): + b.validate_election(election) + m.undo() @patch("transactions.types.elections.election.uuid4", lambda: "mock_uuid4") -def test_upsert_validator_invalid_election(b_mock, new_validator, node_key, fixed_seed_election): - voters = b_mock.get_recipients_list() - duplicate_election = ValidatorElection.generate([node_key.public_key], voters, new_validator, None).sign( - [node_key.private_key] - ) +def test_upsert_validator_invalid_election(monkeypatch, b, network_validators, new_validator, node_key): + def mock_get_validators(self, height): + validators = [] + for public_key, power in network_validators.items(): + validators.append( + { + "public_key": {"type": "ed25519-base64", "value": public_key}, + "voting_power": power, + } + ) + return validators - with pytest.raises(DuplicateTransaction): - b_mock.validate_election(fixed_seed_election, [duplicate_election]) + with monkeypatch.context() as m: + from planetmint.model.dataaccessor import DataAccessor - b_mock.models.store_bulk_transactions([fixed_seed_election]) + m.setattr(DataAccessor, "get_validators", mock_get_validators) - with pytest.raises(DuplicateTransaction): - b_mock.validate_election(duplicate_election) + voters = b.get_recipients_list() + duplicate_election = ValidatorElection.generate([node_key.public_key], voters, new_validator, None).sign( + [node_key.private_key] + ) + voters = b.get_recipients_list() + fixed_seed_election = ValidatorElection.generate([node_key.public_key], voters, new_validator, None).sign( + [node_key.private_key] + ) - # Try creating an election with incomplete voter set - invalid_election = ValidatorElection.generate([node_key.public_key], voters[1:], new_validator, None).sign( - [node_key.private_key] - ) + with pytest.raises(DuplicateTransaction): + b.validate_election(fixed_seed_election, [duplicate_election]) - with pytest.raises(UnequalValidatorSet): - b_mock.validate_election(invalid_election) + b.models.store_bulk_transactions([fixed_seed_election]) - recipients = b_mock.get_recipients_list() - altered_recipients = [] - for r in recipients: - ([r_public_key], voting_power) = r - altered_recipients.append(([r_public_key], voting_power - 1)) + with pytest.raises(DuplicateTransaction): + b.validate_election(duplicate_election) - # Create a transaction which doesn't enfore the network power - tx_election = ValidatorElection.generate([node_key.public_key], altered_recipients, new_validator, None).sign( - [node_key.private_key] - ) + # Try creating an election with incomplete voter set + invalid_election = ValidatorElection.generate([node_key.public_key], voters[1:], new_validator, None).sign( + [node_key.private_key] + ) - with pytest.raises(UnequalValidatorSet): - b_mock.validate_election(tx_election) + with pytest.raises(UnequalValidatorSet): + b.validate_election(invalid_election) + + recipients = b.get_recipients_list() + altered_recipients = [] + for r in recipients: + ([r_public_key], voting_power) = r + altered_recipients.append(([r_public_key], voting_power - 1)) + + # Create a transaction which doesn't enfore the network power + tx_election = ValidatorElection.generate([node_key.public_key], altered_recipients, new_validator, None).sign( + [node_key.private_key] + ) + + with pytest.raises(UnequalValidatorSet): + b.validate_election(tx_election) + m.undo() -def test_get_status_ongoing(b, ongoing_validator_election, new_validator): - status = ValidatorElection.ONGOING - resp = b.get_election_status(ongoing_validator_election) - assert resp == status +def test_get_status_ongoing(monkeypatch, b, network_validators, node_key, new_validator, ed25519_node_keys): + def mock_get_validators(self, height): + _validators = [] + for public_key, power in network_validators.items(): + _validators.append( + { + "public_key": {"type": "ed25519-base64", "value": public_key}, + "voting_power": power, + } + ) + return _validators + + with monkeypatch.context() as m: + from planetmint.model.dataaccessor import DataAccessor + from planetmint.backend import schema, query + from planetmint.abci.block import Block + + m.setattr(DataAccessor, "get_validators", mock_get_validators) + + voters = b.get_recipients_list() + valid_upsert_validator_election = ValidatorElection.generate( + [node_key.public_key], voters, new_validator, None + ).sign([node_key.private_key]) + + validators = b.models.get_validators(height=1) + genesis_validators = {"validators": validators, "height": 0} + query.store_validator_set(b.models.connection, genesis_validators) + b.models.store_bulk_transactions([valid_upsert_validator_election]) + query.store_election(b.models.connection, valid_upsert_validator_election.id, 1, is_concluded=False) + block_1 = Block(app_hash="hash_1", height=1, transactions=[valid_upsert_validator_election.id]) + b.models.store_block(block_1._asdict()) + + status = ValidatorElection.ONGOING + resp = b.get_election_status(valid_upsert_validator_election) + assert resp == status + m.undo() -def test_get_status_concluded(b, concluded_election, new_validator): - status = ValidatorElection.CONCLUDED - resp = b.get_election_status(concluded_election) - assert resp == status +def test_get_status_concluded(monkeypatch, b, network_validators, node_key, new_validator, ed25519_node_keys): + def mock_get_validators(self, height): + _validators = [] + for public_key, power in network_validators.items(): + _validators.append( + { + "public_key": {"type": "ed25519-base64", "value": public_key}, + "voting_power": power, + } + ) + return _validators + + with monkeypatch.context() as m: + from planetmint.model.dataaccessor import DataAccessor + from planetmint.backend import schema, query + from planetmint.abci.block import Block + + m.setattr(DataAccessor, "get_validators", mock_get_validators) + + voters = b.get_recipients_list() + valid_upsert_validator_election = ValidatorElection.generate( + [node_key.public_key], voters, new_validator, None + ).sign([node_key.private_key]) + + validators = b.models.get_validators(height=1) + genesis_validators = {"validators": validators, "height": 0} + query.store_validator_set(b.models.connection, genesis_validators) + b.models.store_bulk_transactions([valid_upsert_validator_election]) + query.store_election(b.models.connection, valid_upsert_validator_election.id, 1, is_concluded=False) + block_1 = Block(app_hash="hash_1", height=1, transactions=[valid_upsert_validator_election.id]) + b.models.store_block(block_1._asdict()) + query.store_election(b.models.connection, valid_upsert_validator_election.id, 2, is_concluded=True) + + status = ValidatorElection.CONCLUDED + resp = b.get_election_status(valid_upsert_validator_election) + assert resp == status + m.undo() -def test_get_status_inconclusive(b, inconclusive_election, new_validator): - def set_block_height_to_3(): +def test_get_status_inconclusive(monkeypatch, b, network_validators, node_key, new_validator): + def set_block_height_to_3(self): return {"height": 3} def custom_mock_get_validators(height): @@ -167,24 +337,94 @@ def test_get_status_inconclusive(b, inconclusive_election, new_validator): }, ] - b.models.get_validators = custom_mock_get_validators - b.models.get_latest_block = set_block_height_to_3 - status = ValidatorElection.INCONCLUSIVE - resp = b.get_election_status(inconclusive_election) - assert resp == status + def mock_get_validators(self, height): + _validators = [] + for public_key, power in network_validators.items(): + _validators.append( + { + "public_key": {"type": "ed25519-base64", "value": public_key}, + "voting_power": power, + } + ) + return _validators + + with monkeypatch.context() as m: + from planetmint.model.dataaccessor import DataAccessor + from planetmint.backend import schema, query + from planetmint.abci.block import Block + + m.setattr(DataAccessor, "get_validators", mock_get_validators) + + voters = b.get_recipients_list() + valid_upsert_validator_election = ValidatorElection.generate( + [node_key.public_key], voters, new_validator, None + ).sign([node_key.private_key]) + + validators = b.models.get_validators(height=1) + genesis_validators = {"validators": validators, "height": 0} + query.store_validator_set(b.models.connection, genesis_validators) + b.models.store_bulk_transactions([valid_upsert_validator_election]) + query.store_election(b.models.connection, valid_upsert_validator_election.id, 1, is_concluded=False) + block_1 = Block(app_hash="hash_1", height=1, transactions=[valid_upsert_validator_election.id]) + b.models.store_block(block_1._asdict()) + + validators = b.models.get_validators(height=1) + validators[0]["voting_power"] = 15 + validator_update = {"validators": validators, "height": 2, "election_id": "some_other_election"} + + query.store_validator_set(b.models.connection, validator_update) + m.undo() + with monkeypatch.context() as m2: + m2.setattr(DataAccessor, "get_validators", custom_mock_get_validators) + m2.setattr(DataAccessor, "get_latest_block", set_block_height_to_3) + status = ValidatorElection.INCONCLUSIVE + resp = b.get_election_status(valid_upsert_validator_election) + assert resp == status + m2.undo() -def test_upsert_validator_show(caplog, ongoing_validator_election, b): +def test_upsert_validator_show(monkeypatch, caplog, b, node_key, new_validator, network_validators): from planetmint.commands.planetmint import run_election_show - election_id = ongoing_validator_election.id - public_key = public_key_to_base64(ongoing_validator_election.assets[0]["data"]["public_key"]["value"]) - power = ongoing_validator_election.assets[0]["data"]["power"] - node_id = ongoing_validator_election.assets[0]["data"]["node_id"] - status = ValidatorElection.ONGOING + def mock_get_validators(self, height): + _validators = [] + for public_key, power in network_validators.items(): + _validators.append( + { + "public_key": {"type": "ed25519-base64", "value": public_key}, + "voting_power": power, + } + ) + return _validators - show_args = Namespace(action="show", election_id=election_id) + with monkeypatch.context() as m: + from planetmint.model.dataaccessor import DataAccessor + from planetmint.backend import schema, query + from planetmint.abci.block import Block - msg = run_election_show(show_args, b) + m.setattr(DataAccessor, "get_validators", mock_get_validators) - assert msg == f"public_key={public_key}\npower={power}\nnode_id={node_id}\nstatus={status}" + voters = b.get_recipients_list() + valid_upsert_validator_election = ValidatorElection.generate( + [node_key.public_key], voters, new_validator, None + ).sign([node_key.private_key]) + + validators = b.models.get_validators(height=1) + genesis_validators = {"validators": validators, "height": 0} + query.store_validator_set(b.models.connection, genesis_validators) + b.models.store_bulk_transactions([valid_upsert_validator_election]) + query.store_election(b.models.connection, valid_upsert_validator_election.id, 1, is_concluded=False) + block_1 = Block(app_hash="hash_1", height=1, transactions=[valid_upsert_validator_election.id]) + b.models.store_block(block_1._asdict()) + election_id = valid_upsert_validator_election.id + public_key = public_key_to_base64(valid_upsert_validator_election.assets[0]["data"]["public_key"]["value"]) + power = valid_upsert_validator_election.assets[0]["data"]["power"] + node_id = valid_upsert_validator_election.assets[0]["data"]["node_id"] + status = ValidatorElection.ONGOING + + show_args = Namespace(action="show", election_id=election_id) + + msg = run_election_show(show_args, b) + + assert msg == f"public_key={public_key}\npower={power}\nnode_id={node_id}\nstatus={status}" + m.undo() diff --git a/tests/web/test_blocks.py b/tests/web/test_blocks.py index e35e150..c0ca901 100644 --- a/tests/web/test_blocks.py +++ b/tests/web/test_blocks.py @@ -8,6 +8,16 @@ import pytest BLOCKS_ENDPOINT = "/api/v1/blocks/" +@pytest.mark.bdb +@pytest.mark.usefixtures("inputs") +def test_get_latest_block(client): + res = client.get(BLOCKS_ENDPOINT + "latest") + assert res.status_code == 200 + assert len(res.json["transaction_ids"]) == 10 + assert res.json["app_hash"] == "hash3" + assert res.json["height"] == 3 + + @pytest.mark.bdb @pytest.mark.usefixtures("inputs") def test_get_block_returns_404_if_not_found(client): @@ -55,16 +65,6 @@ def test_get_blocks_by_txid_endpoint_returns_400_bad_query_params(client): assert res.json == {"message": "Unknown arguments: status"} -@pytest.mark.bdb -@pytest.mark.usefixtures("inputs") -def test_get_latest_block(client): - res = client.get(BLOCKS_ENDPOINT + "latest") - assert res.status_code == 200 - assert len(res.json["transaction_ids"]) == 10 - assert res.json["app_hash"] == "hash3" - assert res.json["height"] == 3 - - @pytest.mark.bdb @pytest.mark.usefixtures("inputs") def test_get_block_by_height(client):