Transaction hierarchy (#254)

* removed Transaction class from models.py, adjusted imports and function calls

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

* removed comments

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

* removed empty lines

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

* resolved linting error

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

* adjusted import path

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

* added missing argument to mock

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

* resolved linting error

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

* adjusted mock func signature

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

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
This commit is contained in:
Lorenz Herzberger 2022-09-13 17:27:51 +02:00 committed by GitHub
parent d971709a79
commit 22ccb26d99
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 65 additions and 78 deletions

View File

@ -4,7 +4,6 @@
# Code is Apache-2.0 and docs are CC-BY-4.0
from planetmint.transactions.common.transaction import Transaction # noqa
from planetmint import models # noqa
from planetmint.upsert_validator import ValidatorElection # noqa
from planetmint.transactions.types.elections.vote import Vote # noqa
from planetmint.transactions.types.elections.chain_migration_election import ChainMigrationElection
@ -12,8 +11,8 @@ from planetmint.lib import Planetmint
from planetmint.core import App
Transaction.register_type(Transaction.CREATE, models.Transaction)
Transaction.register_type(Transaction.TRANSFER, models.Transaction)
Transaction.register_type(Transaction.CREATE, Transaction)
Transaction.register_type(Transaction.TRANSFER, Transaction)
Transaction.register_type(ValidatorElection.OPERATION, ValidatorElection)
Transaction.register_type(ChainMigrationElection.OPERATION, ChainMigrationElection)
Transaction.register_type(Vote.OPERATION, Vote)

View File

@ -24,7 +24,7 @@ import requests
import planetmint
from planetmint.config import Config
from planetmint import backend, config_utils, fastquery
from planetmint.models import Transaction
from planetmint.transactions.common.transaction import Transaction
from planetmint.transactions.common.exceptions import SchemaValidationError, ValidationError, DoubleSpend
from planetmint.transactions.common.transaction_mode_types import (
BROADCAST_TX_COMMIT,
@ -248,7 +248,7 @@ class Planetmint(object):
transaction.update({"metadata": metadata})
transaction = Transaction.from_dict(transaction)
transaction = Transaction.from_dict(transaction, False)
return transaction
@ -301,7 +301,7 @@ class Planetmint(object):
raise DoubleSpend('tx "{}" spends inputs twice'.format(txid))
elif transactions:
transaction = backend.query.get_transactions(self.connection, [transactions[0]["id"]])
transaction = Transaction.from_dict(transaction[0])
transaction = Transaction.from_dict(transaction[0], False)
elif current_spent_transactions:
transaction = current_spent_transactions[0]
@ -368,7 +368,7 @@ class Planetmint(object):
# throught the code base.
if isinstance(transaction, dict):
try:
transaction = Transaction.from_dict(tx)
transaction = Transaction.from_dict(tx, False)
except SchemaValidationError as e:
logger.warning("Invalid transaction schema: %s", e.__cause__.message)
return False

View File

@ -3,56 +3,6 @@
# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
# Code is Apache-2.0 and docs are CC-BY-4.0
from planetmint.backend.schema import validate_language_key
from planetmint.transactions.common.exceptions import InvalidSignature, DuplicateTransaction
from planetmint.transactions.common.schema import validate_transaction_schema
from planetmint.transactions.common.transaction import Transaction
from planetmint.transactions.common.utils import validate_txn_obj, validate_key
class Transaction(Transaction):
ASSET = "asset"
METADATA = "metadata"
DATA = "data"
def validate(self, planet, current_transactions=[]):
"""Validate transaction spend
Args:
planet (Planetmint): an instantiated planetmint.Planetmint object.
Returns:
The transaction (Transaction) if the transaction is valid else it
raises an exception describing the reason why the transaction is
invalid.
Raises:
ValidationError: If the transaction is invalid
"""
input_conditions = []
if self.operation == Transaction.CREATE:
duplicates = any(txn for txn in current_transactions if txn.id == self.id)
if planet.is_committed(self.id) or duplicates:
raise DuplicateTransaction("transaction `{}` already exists".format(self.id))
if not self.inputs_valid(input_conditions):
raise InvalidSignature("Transaction signature is invalid.")
elif self.operation == Transaction.TRANSFER:
self.validate_transfer_inputs(planet, current_transactions)
return self
@classmethod
def from_dict(cls, tx_body):
return super().from_dict(tx_body, False)
@classmethod
def validate_schema(cls, tx_body):
validate_transaction_schema(tx_body)
validate_txn_obj(cls.ASSET, tx_body[cls.ASSET], cls.DATA, validate_key)
validate_txn_obj(cls.METADATA, tx_body, cls.METADATA, validate_key)
validate_language_key(tx_body[cls.ASSET], cls.DATA)
validate_language_key(tx_body, cls.METADATA)
class FastTransaction:
"""A minimal wrapper around a transaction dictionary. This is useful for

View File

@ -34,8 +34,11 @@ from planetmint.transactions.common.exceptions import (
InvalidSignature,
AmountError,
AssetIdMismatch,
DuplicateTransaction,
)
from planetmint.transactions.common.utils import serialize
from planetmint.backend.schema import validate_language_key
from planetmint.transactions.common.schema import validate_transaction_schema
from planetmint.transactions.common.utils import serialize, validate_txn_obj, validate_key
from .memoize import memoize_from_dict, memoize_to_dict
from .input import Input
from .output import Output
@ -81,6 +84,9 @@ class Transaction(object):
CREATE = "CREATE"
TRANSFER = "TRANSFER"
ALLOWED_OPERATIONS = (CREATE, TRANSFER)
ASSET = "asset"
METADATA = "metadata"
DATA = "data"
VERSION = "2.0"
def __init__(
@ -153,6 +159,32 @@ class Transaction(object):
self._id = hash_id
self.tx_dict = tx_dict
def validate(self, planet, current_transactions=[]):
"""Validate transaction spend
Args:
planet (Planetmint): an instantiated planetmint.Planetmint object.
Returns:
The transaction (Transaction) if the transaction is valid else it
raises an exception describing the reason why the transaction is
invalid.
Raises:
ValidationError: If the transaction is invalid
"""
input_conditions = []
if self.operation == Transaction.CREATE:
duplicates = any(txn for txn in current_transactions if txn.id == self.id)
if planet.is_committed(self.id) or duplicates:
raise DuplicateTransaction("transaction `{}` already exists".format(self.id))
if not self.inputs_valid(input_conditions):
raise InvalidSignature("Transaction signature is invalid.")
elif self.operation == Transaction.TRANSFER:
self.validate_transfer_inputs(planet, current_transactions)
return self
@property
def unspent_outputs(self):
"""UnspentOutput: The outputs of this transaction, in a data
@ -802,7 +834,11 @@ class Transaction(object):
@classmethod
def validate_schema(cls, tx):
pass
validate_transaction_schema(tx)
validate_txn_obj(cls.ASSET, tx[cls.ASSET], cls.DATA, validate_key)
validate_txn_obj(cls.METADATA, tx, cls.METADATA, validate_key)
validate_language_key(tx[cls.ASSET], cls.DATA)
validate_language_key(tx, cls.METADATA)
def validate_transfer_inputs(self, planet, current_transactions=[]):
# store the inputs so that we can check if the asset ids match

View File

@ -3,7 +3,7 @@
# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
# Code is Apache-2.0 and docs are CC-BY-4.0
from planetmint.models import Transaction
from planetmint.transactions.common.transaction import Transaction
from planetmint.transactions.common.input import Input
from planetmint.transactions.common.output import Output

View File

@ -3,7 +3,7 @@
# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
# Code is Apache-2.0 and docs are CC-BY-4.0
from planetmint.models import Transaction
from planetmint.transactions.common.transaction import Transaction
from planetmint.transactions.common.output import Output
from copy import deepcopy

View File

@ -19,7 +19,7 @@ from planetmint.transactions.common.exceptions import (
)
from planetmint.web.views.base import make_error
from planetmint.web.views import parameters
from planetmint.models import Transaction
from planetmint.transactions.common.transaction import Transaction
logger = logging.getLogger(__name__)
@ -76,7 +76,7 @@ class TransactionListApi(Resource):
tx = request.get_json(force=True)
try:
tx_obj = Transaction.from_dict(tx)
tx_obj = Transaction.from_dict(tx, False)
except SchemaValidationError as e:
return make_error(
400,

View File

@ -33,14 +33,14 @@ def test_validate_transfer_asset_id_mismatch(b, signed_create_tx, user_pk, user_
def test_get_asset_id_create_transaction(alice, user_pk):
from planetmint.models import Transaction
from planetmint.transactions.common.transaction import Transaction
tx_create = Create.generate([alice.public_key], [([user_pk], 1)])
assert Transaction.get_asset_id(tx_create) == tx_create.id
def test_get_asset_id_transfer_transaction(b, signed_create_tx, user_pk):
from planetmint.models import Transaction
from planetmint.transactions.common.transaction import Transaction
tx_transfer = Transfer.generate(signed_create_tx.to_inputs(), [([user_pk], 1)], signed_create_tx.id)
asset_id = Transaction.get_asset_id(tx_transfer)
@ -48,7 +48,7 @@ def test_get_asset_id_transfer_transaction(b, signed_create_tx, user_pk):
def test_asset_id_mismatch(alice, user_pk):
from planetmint.models import Transaction
from planetmint.transactions.common.transaction import Transaction
from planetmint.transactions.common.exceptions import AssetIdMismatch
tx1 = Create.generate([alice.public_key], [([user_pk], 1)], metadata={"msg": random.random()})

View File

@ -149,7 +149,7 @@ def test_zenroom_signing():
shared_creation_txid = sha3_256(json_str_tx.encode()).hexdigest()
tx["id"] = shared_creation_txid
from planetmint.models import Transaction
from planetmint.transactions.common.transaction import Transaction
from planetmint.lib import Planetmint
from planetmint.transactions.common.exceptions import (
SchemaValidationError,
@ -158,7 +158,7 @@ def test_zenroom_signing():
try:
print(f"TX\n{tx}")
tx_obj = Transaction.from_dict(tx)
tx_obj = Transaction.from_dict(tx, False)
except SchemaValidationError as e:
print(e)
assert ()

View File

@ -7,6 +7,7 @@ from copy import deepcopy
import pytest
import json
from planetmint.transactions.common.transaction import Transaction
from planetmint.transactions.types.assets.create import Create
from planetmint.transactions.types.assets.transfer import Transfer
@ -15,7 +16,6 @@ pytestmark = pytest.mark.bdb
def test_get_txids_filtered(signed_create_tx, signed_transfer_tx, db_conn):
from planetmint.backend.tarantool import query
from planetmint.models import Transaction
# create and insert two blocks, one for the create and one for the
# transfer transaction

View File

@ -6,7 +6,7 @@
import pytest
from copy import deepcopy
from planetmint.models import Transaction
from planetmint.transactions.common.transaction import Transaction
from planetmint.transactions.types.assets.create import Create
from planetmint.transactions.common.crypto import generate_key_pair
from planetmint.transactions.common.memoize import to_dict, from_dict

View File

@ -141,7 +141,7 @@ def _setup_database(_configure_planetmint): # TODO Here is located setup databa
@pytest.fixture
def _bdb(_setup_database, _configure_planetmint):
from planetmint.transactions.common.memoize import to_dict, from_dict
from planetmint.models import Transaction
from planetmint.transactions.common.transaction import Transaction
from .utils import flush_db
from planetmint.config import Config

View File

@ -18,7 +18,7 @@ except ImportError:
from unittest.mock import MagicMock
from planetmint.transactions.common.exceptions import AmountError, SchemaValidationError, ThresholdTooDeep
from planetmint.models import Transaction
from planetmint.transactions.common.transaction import Transaction
from planetmint.transactions.common.utils import _fulfillment_to_details, _fulfillment_from_details
################################################################################
@ -28,7 +28,7 @@ from planetmint.transactions.common.utils import _fulfillment_to_details, _fulfi
def validate(tx):
if isinstance(tx, Transaction):
tx = tx.to_dict()
Transaction.from_dict(tx)
Transaction.from_dict(tx, False)
def validate_raises(tx, exc=SchemaValidationError):
@ -38,7 +38,7 @@ def validate_raises(tx, exc=SchemaValidationError):
# We should test that validation works when we expect it to
def test_validation_passes(signed_create_tx):
Transaction.from_dict(signed_create_tx.to_dict())
Transaction.from_dict(signed_create_tx.to_dict(), False)
################################################################################
@ -53,7 +53,6 @@ def test_tx_serialization_hash_function(signed_create_tx):
def test_tx_serialization_with_incorrect_hash(signed_create_tx):
from planetmint.transactions.common.transaction import Transaction
from planetmint.transactions.common.exceptions import InvalidHash
tx = signed_create_tx.to_dict()
@ -68,7 +67,7 @@ def test_tx_serialization_with_no_hash(signed_create_tx):
tx = signed_create_tx.to_dict()
del tx["id"]
with pytest.raises(InvalidHash):
Transaction.from_dict(tx)
Transaction.from_dict(tx, False)
################################################################################

View File

@ -306,12 +306,15 @@ def test_post_invalid_transaction(
exc_cls = getattr(exceptions, exc)
def mock_validation(self_, tx):
def mock_validation(self_, tx, skip_schema_validation=True):
raise exc_cls(msg)
TransactionMock = Mock(validate=mock_validation)
monkeypatch.setattr("planetmint.models.Transaction.from_dict", lambda tx: TransactionMock)
monkeypatch.setattr(
"planetmint.transactions.common.transaction.Transaction.from_dict",
lambda tx, skip_schema_validation: TransactionMock,
)
res = client.post(TX_ENDPOINT, data=json.dumps({}))
expected_status_code = 400
expected_error_message = "Invalid transaction ({}): {}".format(exc, msg)