planetmint/tests/test_parallel_validation.py
Lorenz Herzberger 8abbef00fe
GitHub actions ()
* creating first github action

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* fix syntax error

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* renamed action, using black stable

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* updated checkout action on workflow black

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* formatted code with black

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* replaced lint with black service

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* removed black service added black check to makefile

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* replaced flake8 with black

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added pull_request to black actions trigger

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* replaced flake8 with black style checker ()

* updated version number to 1.0.0

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* creating first github action

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* fix syntax error

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* renamed action, using black stable

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* updated checkout action on workflow black

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* formatted code with black

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* version bumpt

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* removed some comments and unsused import

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* replaced lint with black service

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* removed black service added black check to makefile

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* replaced flake8 with black

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added pull_request to black actions trigger

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* started on unit test workflow

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* removed run step

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* fixed typo

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* testing docker-compose

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* check docker-compose

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* try running pytest

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* check out -f

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* changed path

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* increased health check retries, added job dependency

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added path to docker-compose.yml to test action

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* moved container startup to test step

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added checkout step to test job

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* different kind of execution

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* checking build step

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* fixed missing keyword

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added checkout to build step

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* storing artifacts

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added needs

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* changed Dockerfile-dev to python-slim

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added job matrix back in

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added abci to build job matrix

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* updated test job steps

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* fixed typo

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* replaced docker exec with docker-compose exec for abci test

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added first version of acceptance and integration test action

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added runs-on

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* fixed syntax error

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* reverted to docker exec

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added copyright notice and env to start container step

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* separated abci from non abci test job

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* renamed pytest workflow to unit-test

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added codecov workflow

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added pytest install to codecov step

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added pip install

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* moved codecov to unit-test

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* show files

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* changed paths

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* removed debug job steps

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* renamed black to lint, added audit workflow

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* checking if dc down is necessary

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* removed dc down step from acceptance and integration

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* fixed lint error

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added tox documentation to github acitons ()

* added documentation job

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added docs dependency install to docs workflow

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* add more dependencies

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* install rapidjson manually

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added python-rapidjson to docs requirements text

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* changed gh config on tox.ini

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added base58 to docs require

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* changed docs require to dev

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* reversed changes to docs require

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* changed gh to gh-actions

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* increased verbosity for debugging

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* added -e docsroot manually

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* removed verbosity

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* removed travis ci files

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

* changed audit step to trigger on schedule

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
Co-authored-by: enesturk <enes.m.turk@gmail.com>
2022-08-18 09:45:51 +02:00

130 lines
5.4 KiB
Python

# 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
import pytest
from planetmint.transactions.common.crypto import generate_key_pair
from planetmint.transactions.types.assets.create import Create
from planetmint.transactions.types.assets.transfer import Transfer
pytestmark = pytest.mark.tendermint
def generate_create_and_transfer(keypair=None):
if not keypair:
keypair = generate_key_pair()
priv_key, pub_key = keypair
create_tx = Create.generate([pub_key], [([pub_key], 10)]).sign([priv_key])
transfer_tx = Transfer.generate(create_tx.to_inputs(), [([pub_key], 10)], asset_id=create_tx.id).sign([priv_key])
return create_tx, transfer_tx
def test_validation_worker_process_multiple_transactions(b):
import multiprocessing as mp
from planetmint.parallel_validation import ValidationWorker, RESET, EXIT
keypair = generate_key_pair()
create_tx, transfer_tx = generate_create_and_transfer(keypair)
double_spend = Transfer.generate(create_tx.to_inputs(), [([keypair.public_key], 10)], asset_id=create_tx.id).sign(
[keypair.private_key]
)
in_queue, results_queue = mp.Queue(), mp.Queue()
vw = ValidationWorker(in_queue, results_queue)
# Note: in the following instructions, the worker will encounter two
# `RESET` messages, and an `EXIT` message. When a worker processes a
# `RESET` message, it forgets all transactions it has validated. This allow
# us to re-validate the same transactions. This won't happen in real life,
# but it's quite handy to check if the worker actually forgot about the
# past transactions (if not, it will return `False` because the
# transactions look like a double spend).
# `EXIT` makes the worker to stop the infinite loop.
in_queue.put((0, create_tx.to_dict()))
in_queue.put((10, transfer_tx.to_dict()))
in_queue.put((20, double_spend.to_dict()))
in_queue.put(RESET)
in_queue.put((0, create_tx.to_dict()))
in_queue.put((5, transfer_tx.to_dict()))
in_queue.put(RESET)
in_queue.put((20, create_tx.to_dict()))
in_queue.put((25, double_spend.to_dict()))
in_queue.put((30, transfer_tx.to_dict()))
in_queue.put(EXIT)
vw.run()
assert results_queue.get() == (0, create_tx)
assert results_queue.get() == (10, transfer_tx)
assert results_queue.get() == (20, False)
assert results_queue.get() == (0, create_tx)
assert results_queue.get() == (5, transfer_tx)
assert results_queue.get() == (20, create_tx)
assert results_queue.get() == (25, double_spend)
assert results_queue.get() == (30, False)
def test_parallel_validator_routes_transactions_correctly(b, monkeypatch):
import os
from collections import defaultdict
import multiprocessing as mp
from json import dumps
from planetmint.parallel_validation import ParallelValidator
# We want to make sure that the load is distributed across all workers.
# Since introspection on an object running on a different process is
# difficult, we create an additional queue where every worker can emit its
# PID every time validation is called.
validation_called_by = mp.Queue()
# Validate is now a passthrough, and every time it is called it will emit
# the PID of its worker to the designated queue.
def validate(self, dict_transaction):
validation_called_by.put((os.getpid(), dict_transaction["id"]))
return dict_transaction
monkeypatch.setattr("planetmint.parallel_validation.ValidationWorker.validate", validate)
# Transaction routing uses the `id` of the transaction. This test strips
# down a transaction to just its `id`. We have two workers, so even ids
# will be processed by one worker, odd ids by the other.
transactions = [{"id": "0"}, {"id": "1"}, {"id": "2"}, {"id": "3"}]
pv = ParallelValidator(number_of_workers=2)
pv.start()
# ParallelValidator is instantiated once, and then used several times.
# Here we simulate this scenario by running it an arbitrary number of
# times.
# Note that the `ParallelValidator.result` call resets the object, and
# makes it ready to validate a new set of transactions.
for _ in range(2):
# First, we push the transactions to the parallel validator instance
for transaction in transactions:
pv.validate(dumps(transaction).encode("utf8"))
assert pv.result(timeout=1) == transactions
# Now we analize the transaction processed by the workers
worker_to_transactions = defaultdict(list)
for _ in transactions:
worker_pid, transaction_id = validation_called_by.get()
worker_to_transactions[worker_pid].append(transaction_id)
# The transactions are stored in two buckets.
for _, transaction_ids in worker_to_transactions.items():
assert len(transaction_ids) == 2
# We have two workers, hence we have two different routes for
# transactions. We have the route for even transactions, and the
# route for odd transactions. Since we don't know which worker
# processed what, we test that the transactions processed by a
# worker are all even or all odd.
assert all(filter(lambda x: int(x) % 2 == 0, transaction_ids)) or all(
filter(lambda x: int(x) % 2 == 1, transaction_ids)
)
pv.stop()