moved methods used by testing to tests/utils.py

Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
This commit is contained in:
Jürgen Eckel 2023-02-27 13:07:34 +01:00
parent 5f5a610cf1
commit 2cac403a1d
No known key found for this signature in database
3 changed files with 90 additions and 85 deletions

View File

@ -12,15 +12,14 @@ from planetmint.backend.connection import Connection
from planetmint.backend.tarantool.const import TARANT_TABLE_TRANSACTION, TARANT_TABLE_GOVERNANCE from planetmint.backend.tarantool.const import TARANT_TABLE_TRANSACTION, TARANT_TABLE_GOVERNANCE
from planetmint.model.fastquery import FastQuery from planetmint.model.fastquery import FastQuery
from planetmint.abci.tendermint_utils import key_from_base64 from planetmint.abci.tendermint_utils import key_from_base64
from planetmint.abci.tendermint_utils import merkleroot
from hashlib import sha3_256
from planetmint.backend.models.block import Block from planetmint.backend.models.block import Block
from planetmint.backend.models.output import Output from planetmint.backend.models.output import Output
from planetmint.backend.models.asset import Asset from planetmint.backend.models.asset import Asset
from planetmint.backend.models.input import Input
from planetmint.backend.models.metadata import MetaData from planetmint.backend.models.metadata import MetaData
from planetmint.backend.models.dbtransaction import DbTransaction from planetmint.backend.models.dbtransaction import DbTransaction
class Models: class Models:
def __init__(self, database_connection = None): def __init__(self, database_connection = None):
config_utils.autoconfigure() config_utils.autoconfigure()
@ -142,77 +141,6 @@ class Models:
return validators return validators
def tests_update_utxoset(self, transaction):
self.updated__ = """Update the UTXO set given ``transaction``. That is, remove
the outputs that the given ``transaction`` spends, and add the
outputs that the given ``transaction`` creates.
Args:
transaction (:obj:`~planetmint.models.Transaction`): A new
transaction incoming into the system for which the UTXOF
set needs to be updated.
"""
spent_outputs = [spent_output for spent_output in transaction.spent_outputs]
if spent_outputs:
self.tests_delete_unspent_outputs(*spent_outputs)
self.tests_store_unspent_outputs(*[utxo._asdict() for utxo in transaction.unspent_outputs])
def tests_store_unspent_outputs(self, *unspent_outputs):
"""Store the given ``unspent_outputs`` (utxos).
Args:
*unspent_outputs (:obj:`tuple` of :obj:`dict`): Variable
length tuple or list of unspent outputs.
"""
if unspent_outputs:
return backend.query.store_unspent_outputs(self.connection, *unspent_outputs)
def tests_get_utxoset_merkle_root(self):
"""Returns the merkle root of the utxoset. This implies that
the utxoset is first put into a merkle tree.
For now, the merkle tree and its root will be computed each
time. This obviously is not efficient and a better approach
that limits the repetition of the same computation when
unnecesary should be sought. For instance, future optimizations
could simply re-compute the branches of the tree that were
affected by a change.
The transaction hash (id) and output index should be sufficient
to uniquely identify a utxo, and consequently only that
information from a utxo record is needed to compute the merkle
root. Hence, each node of the merkle tree should contain the
tuple (txid, output_index).
.. important:: The leaves of the tree will need to be sorted in
some kind of lexicographical order.
Returns:
str: Merkle root in hexadecimal form.
"""
utxoset = backend.query.get_unspent_outputs(self.connection)
# TODO Once ready, use the already pre-computed utxo_hash field.
# See common/transactions.py for details.
hashes = [
sha3_256("{}{}".format(utxo["transaction_id"], utxo["output_index"]).encode()).digest() for utxo in utxoset
]
# TODO Notice the sorted call!
return merkleroot(sorted(hashes))
def tests_delete_unspent_outputs(self, *unspent_outputs):
"""Deletes the given ``unspent_outputs`` (utxos).
Args:
*unspent_outputs (:obj:`tuple` of :obj:`dict`): Variable
length tuple or list of unspent outputs.
"""
if unspent_outputs:
return backend.query.delete_unspent_outputs(self.connection, *unspent_outputs)
def get_spent(self, txid, output, current_transactions=[]) -> DbTransaction: def get_spent(self, txid, output, current_transactions=[]) -> DbTransaction:
transactions = backend.query.get_spent(self.connection, txid, output) transactions = backend.query.get_spent(self.connection, txid, output)

View File

@ -23,6 +23,9 @@ from uuid import uuid4
from planetmint.abci.rpc import ABCI_RPC from planetmint.abci.rpc import ABCI_RPC
from planetmint.abci.rpc import MODE_COMMIT, MODE_LIST from planetmint.abci.rpc import MODE_COMMIT, MODE_LIST
from tests.utils import delete_unspent_outputs, get_utxoset_merkle_root, store_unspent_outputs, \
update_utxoset
@pytest.mark.bdb @pytest.mark.bdb
def test_asset_is_separated_from_transaciton(b): def test_asset_is_separated_from_transaciton(b):
@ -149,13 +152,13 @@ def test_post_transaction_invalid_mode(b, test_abci_rpc):
@pytest.mark.bdb @pytest.mark.bdb
def test_update_utxoset(b, signed_create_tx, signed_transfer_tx, db_conn): def test_update_utxoset(b, signed_create_tx, signed_transfer_tx, db_conn):
b.models.tests_update_utxoset(signed_create_tx) update_utxoset(b.models.connection, signed_create_tx)
utxoset = db_conn.get_space("utxos") utxoset = db_conn.get_space("utxos")
assert utxoset.select().rowcount == 1 assert utxoset.select().rowcount == 1
utxo = utxoset.select().data utxo = utxoset.select().data
assert utxo[0][1] == signed_create_tx.id assert utxo[0][1] == signed_create_tx.id
assert utxo[0][2] == 0 assert utxo[0][2] == 0
b.models.tests_update_utxoset(signed_transfer_tx) update_utxoset(b.models.connection, signed_transfer_tx)
assert utxoset.select().rowcount == 1 assert utxoset.select().rowcount == 1
utxo = utxoset.select().data utxo = utxoset.select().data
assert utxo[0][1] == signed_transfer_tx.id assert utxo[0][1] == signed_transfer_tx.id
@ -184,7 +187,7 @@ def test_store_bulk_transaction(mocker, b, signed_create_tx, signed_transfer_tx)
def test_delete_zero_unspent_outputs(b, utxoset): def test_delete_zero_unspent_outputs(b, utxoset):
unspent_outputs, utxo_collection = utxoset unspent_outputs, utxo_collection = utxoset
num_rows_before_operation = utxo_collection.select().rowcount num_rows_before_operation = utxo_collection.select().rowcount
delete_res = b.models.tests_delete_unspent_outputs() # noqa: F841 delete_res = delete_unspent_outputs(b.models.connection) # noqa: F841
num_rows_after_operation = utxo_collection.select().rowcount num_rows_after_operation = utxo_collection.select().rowcount
# assert delete_res is None # assert delete_res is None
assert num_rows_before_operation == num_rows_after_operation assert num_rows_before_operation == num_rows_after_operation
@ -197,7 +200,7 @@ def test_delete_one_unspent_outputs(b, dummy_unspent_outputs):
res = utxo_space.insert((uuid4().hex, utxo["transaction_id"], utxo["output_index"], utxo)) res = utxo_space.insert((uuid4().hex, utxo["transaction_id"], utxo["output_index"], utxo))
assert res assert res
b.models.tests_delete_unspent_outputs(dummy_unspent_outputs[0]) delete_unspent_outputs(b.models.connection, dummy_unspent_outputs[0])
res1 = utxo_space.select(["a", 1], index="utxo_by_transaction_id_and_output_index").data res1 = utxo_space.select(["a", 1], index="utxo_by_transaction_id_and_output_index").data
res2 = utxo_space.select(["b", 0], index="utxo_by_transaction_id_and_output_index").data res2 = utxo_space.select(["b", 0], index="utxo_by_transaction_id_and_output_index").data
assert len(res1) + len(res2) == 2 assert len(res1) + len(res2) == 2
@ -212,7 +215,7 @@ def test_delete_many_unspent_outputs(b, dummy_unspent_outputs):
res = utxo_space.insert((uuid4().hex, utxo["transaction_id"], utxo["output_index"], utxo)) res = utxo_space.insert((uuid4().hex, utxo["transaction_id"], utxo["output_index"], utxo))
assert res assert res
b.models.tests_delete_unspent_outputs(*dummy_unspent_outputs[::2]) delete_unspent_outputs(b.models.connection, *dummy_unspent_outputs[::2])
res1 = utxo_space.select(["a", 0], index="utxo_by_transaction_id_and_output_index").data res1 = utxo_space.select(["a", 0], index="utxo_by_transaction_id_and_output_index").data
res2 = utxo_space.select(["b", 0], index="utxo_by_transaction_id_and_output_index").data res2 = utxo_space.select(["b", 0], index="utxo_by_transaction_id_and_output_index").data
assert len(res1) + len(res2) == 0 assert len(res1) + len(res2) == 0
@ -224,7 +227,7 @@ def test_delete_many_unspent_outputs(b, dummy_unspent_outputs):
def test_store_zero_unspent_output(b): def test_store_zero_unspent_output(b):
utxos = b.models.connection.get_space("utxos") utxos = b.models.connection.get_space("utxos")
num_rows_before_operation = utxos.select().rowcount num_rows_before_operation = utxos.select().rowcount
res = b.models.tests_store_unspent_outputs() res = store_unspent_outputs(b.models.connection)
num_rows_after_operation = utxos.select().rowcount num_rows_after_operation = utxos.select().rowcount
assert res is None assert res is None
assert num_rows_before_operation == num_rows_after_operation assert num_rows_before_operation == num_rows_after_operation
@ -234,7 +237,7 @@ def test_store_zero_unspent_output(b):
def test_store_one_unspent_output(b, unspent_output_1, utxo_collection): def test_store_one_unspent_output(b, unspent_output_1, utxo_collection):
from planetmint.backend.tarantool.connection import TarantoolDBConnection from planetmint.backend.tarantool.connection import TarantoolDBConnection
res = b.models.tests_store_unspent_outputs(unspent_output_1) res = store_unspent_outputs(b.models.connection, unspent_output_1)
if not isinstance(b.models.connection, TarantoolDBConnection): if not isinstance(b.models.connection, TarantoolDBConnection):
assert res.acknowledged assert res.acknowledged
assert len(list(res)) == 1 assert len(list(res)) == 1
@ -258,14 +261,14 @@ def test_store_one_unspent_output(b, unspent_output_1, utxo_collection):
@pytest.mark.bdb @pytest.mark.bdb
def test_store_many_unspent_outputs(b, unspent_outputs): def test_store_many_unspent_outputs(b, unspent_outputs):
b.models.tests_store_unspent_outputs(*unspent_outputs) store_unspent_outputs(b.models.connection, *unspent_outputs)
utxo_space = b.models.connection.get_space("utxos") utxo_space = b.models.connection.get_space("utxos")
res = utxo_space.select([unspent_outputs[0]["transaction_id"]], index="utxos_by_transaction_id") res = utxo_space.select([unspent_outputs[0]["transaction_id"]], index="utxos_by_transaction_id")
assert len(res.data) == 3 assert len(res.data) == 3
def test_get_utxoset_merkle_root_when_no_utxo(b): def test_get_utxoset_merkle_root_when_no_utxo(b):
assert b.models.tests_get_utxoset_merkle_root() == sha3_256(b"").hexdigest() assert get_utxoset_merkle_root(b.models.connection) == sha3_256(b"").hexdigest()
@pytest.mark.bdb @pytest.mark.bdb
@ -276,7 +279,7 @@ def test_get_utxoset_merkle_root(b, dummy_unspent_outputs):
assert res assert res
expected_merkle_root = "86d311c03115bf4d287f8449ca5828505432d69b82762d47077b1c00fe426eac" expected_merkle_root = "86d311c03115bf4d287f8449ca5828505432d69b82762d47077b1c00fe426eac"
merkle_root = b.models.tests_get_utxoset_merkle_root() merkle_root = get_utxoset_merkle_root(b.models.connection)
assert merkle_root == expected_merkle_root assert merkle_root == expected_merkle_root

View File

@ -2,6 +2,7 @@
# Planetmint and IPDB software contributors. # Planetmint and IPDB software contributors.
# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0) # SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
# Code is Apache-2.0 and docs are CC-BY-4.0 # Code is Apache-2.0 and docs are CC-BY-4.0
from hashlib import sha3_256
import base58 import base58
import base64 import base64
@ -9,6 +10,7 @@ import random
from functools import singledispatch from functools import singledispatch
from planetmint import backend
from planetmint.abci.rpc import ABCI_RPC from planetmint.abci.rpc import ABCI_RPC
from planetmint.backend.localmongodb.connection import LocalMongoDBConnection from planetmint.backend.localmongodb.connection import LocalMongoDBConnection
from planetmint.backend.tarantool.connection import TarantoolDBConnection from planetmint.backend.tarantool.connection import TarantoolDBConnection
@ -18,7 +20,7 @@ from transactions.common.transaction_mode_types import BROADCAST_TX_COMMIT
from transactions.types.assets.create import Create from transactions.types.assets.create import Create
from transactions.types.elections.vote import Vote from transactions.types.elections.vote import Vote
from transactions.types.elections.validator_utils import election_id_to_public_key from transactions.types.elections.validator_utils import election_id_to_public_key
from planetmint.abci.tendermint_utils import key_to_base64 from planetmint.abci.tendermint_utils import key_to_base64, merkleroot
from planetmint.abci.rpc import MODE_COMMIT, MODE_LIST from planetmint.abci.rpc import MODE_COMMIT, MODE_LIST
@ -123,3 +125,75 @@ def generate_election(b, cls, public_key, private_key, asset_data, voter_keys):
v.sign([key]) v.sign([key])
return election, votes return election, votes
def delete_unspent_outputs(connection, *unspent_outputs):
"""Deletes the given ``unspent_outputs`` (utxos).
Args:
*unspent_outputs (:obj:`tuple` of :obj:`dict`): Variable
length tuple or list of unspent outputs.
"""
if unspent_outputs:
return backend.query.delete_unspent_outputs(connection, *unspent_outputs)
def get_utxoset_merkle_root(connection):
"""Returns the merkle root of the utxoset. This implies that
the utxoset is first put into a merkle tree.
For now, the merkle tree and its root will be computed each
time. This obviously is not efficient and a better approach
that limits the repetition of the same computation when
unnecesary should be sought. For instance, future optimizations
could simply re-compute the branches of the tree that were
affected by a change.
The transaction hash (id) and output index should be sufficient
to uniquely identify a utxo, and consequently only that
information from a utxo record is needed to compute the merkle
root. Hence, each node of the merkle tree should contain the
tuple (txid, output_index).
.. important:: The leaves of the tree will need to be sorted in
some kind of lexicographical order.
Returns:
str: Merkle root in hexadecimal form.
"""
utxoset = backend.query.get_unspent_outputs(connection)
# TODO Once ready, use the already pre-computed utxo_hash field.
# See common/transactions.py for details.
hashes = [
sha3_256("{}{}".format(utxo["transaction_id"], utxo["output_index"]).encode()).digest() for utxo in utxoset
]
# TODO Notice the sorted call!
return merkleroot(sorted(hashes))
def store_unspent_outputs(connection, *unspent_outputs):
"""Store the given ``unspent_outputs`` (utxos).
Args:
*unspent_outputs (:obj:`tuple` of :obj:`dict`): Variable
length tuple or list of unspent outputs.
"""
if unspent_outputs:
return backend.query.store_unspent_outputs(connection, *unspent_outputs)
def update_utxoset(connection, transaction):
"""
Update the UTXO set given ``transaction``. That is, remove
the outputs that the given ``transaction`` spends, and add the
outputs that the given ``transaction`` creates.
Args:
transaction (:obj:`~planetmint.models.Transaction`): A new
transaction incoming into the system for which the UTXOF
set needs to be updated.
"""
spent_outputs = [spent_output for spent_output in transaction.spent_outputs]
if spent_outputs:
delete_unspent_outputs(connection, *spent_outputs)
store_unspent_outputs(connection, *[utxo._asdict() for utxo in transaction.unspent_outputs])