mirror of
https://github.com/planetmint/planetmint.git
synced 2025-03-30 15:08:31 +00:00
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:
parent
d971709a79
commit
22ccb26d99
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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()})
|
||||
|
@ -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 ()
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
||||
################################################################################
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user