Use planetmint-transaction pypi package

Signed-off-by: cybnon <stefan.weber93@googlemail.com>
This commit is contained in:
cybnon 2022-10-11 15:16:26 +02:00
parent db7d9663f1
commit ab58eec5af
91 changed files with 240 additions and 3574 deletions

View File

@ -3,10 +3,10 @@
# 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.transactions.common.transaction import Transaction # noqa
from planetmint.transactions.types.elections.validator_election import ValidatorElection # noqa
from planetmint.transactions.types.elections.vote import Vote # noqa
from planetmint.transactions.types.elections.chain_migration_election import ChainMigrationElection
from transactions.common.transaction import Transaction # noqa
from transactions.types.elections.validator_election import ValidatorElection # noqa
from transactions.types.elections.vote import Vote # noqa
from transactions.types.elections.chain_migration_election import ChainMigrationElection
from planetmint.lib import Planetmint
from planetmint.core import App

View File

@ -8,10 +8,10 @@ import logging
from importlib import import_module
import tarantool
from transactions.common.exceptions import ConfigurationError
from planetmint.config import Config
from planetmint.backend.exceptions import ConnectionError
from planetmint.transactions.common.exceptions import ConfigurationError
BACKENDS = {
"tarantool_db": "planetmint.backend.tarantool.connection.TarantoolDBConnection",

View File

@ -9,7 +9,7 @@ import pymongo
from planetmint.config import Config
from planetmint.backend.exceptions import DuplicateKeyError, OperationError, ConnectionError
from planetmint.transactions.common.exceptions import ConfigurationError
from transactions.common.exceptions import ConfigurationError
from planetmint.utils import Lazy
from planetmint.backend.connection import Connection

View File

@ -13,7 +13,7 @@ from planetmint import backend
from planetmint.backend.exceptions import DuplicateKeyError
from planetmint.backend.utils import module_dispatch_registrar
from planetmint.backend.localmongodb.connection import LocalMongoDBConnection
from planetmint.transactions.common.transaction import Transaction
from transactions.common.transaction import Transaction
register_query = module_dispatch_registrar(backend.query)

View File

@ -10,8 +10,8 @@ import logging
from planetmint.config import Config
from planetmint.backend.connection import connect
from planetmint.transactions.common.exceptions import ValidationError
from planetmint.transactions.common.utils import (
from transactions.common.exceptions import ValidationError
from transactions.common.utils import (
validate_all_values_for_key_in_obj,
validate_all_values_for_key_in_list,
)

View File

@ -7,7 +7,7 @@ import logging
import tarantool
from planetmint.config import Config
from planetmint.transactions.common.exceptions import ConfigurationError
from transactions.common.exceptions import ConfigurationError
from planetmint.utils import Lazy
from planetmint.backend.connection import Connection

View File

@ -1,7 +1,7 @@
from secrets import token_hex
import copy
import json
from planetmint.transactions.common.memoize import HDict
from transactions.common.memoize import HDict
def get_items(_list):

View File

@ -15,11 +15,11 @@ import sys
from planetmint.core import rollback
from planetmint.utils import load_node_key
from planetmint.transactions.common.transaction_mode_types import BROADCAST_TX_COMMIT
from planetmint.transactions.common.exceptions import DatabaseDoesNotExist, ValidationError
from planetmint.transactions.types.elections.vote import Vote
from planetmint.transactions.types.elections.chain_migration_election import ChainMigrationElection
from planetmint.transactions.types.elections.validator_utils import election_id_to_public_key
from transactions.common.transaction_mode_types import BROADCAST_TX_COMMIT
from transactions.common.exceptions import DatabaseDoesNotExist, ValidationError
from transactions.types.elections.vote import Vote
from transactions.types.elections.chain_migration_election import ChainMigrationElection
from transactions.types.elections.validator_utils import election_id_to_public_key
import planetmint
from planetmint import ValidatorElection, Planetmint
from planetmint.backend import schema

View File

@ -25,7 +25,7 @@ from functools import lru_cache
from pkg_resources import iter_entry_points, ResolutionError
from planetmint.config import Config
from planetmint.transactions.common import exceptions
from transactions.common import exceptions
from planetmint.validation import BaseValidationRules
# TODO: move this to a proper configuration file for logging

View File

@ -5,7 +5,7 @@
from planetmint.utils import condition_details_has_owner
from planetmint.backend import query
from planetmint.transactions.common.transaction import TransactionLink
from transactions.common.transaction import TransactionLink
class FastQuery:

View File

@ -17,37 +17,21 @@ import rapidjson
from hashlib import sha3_256
import requests
from transactions import Transaction, Vote
from transactions.common.crypto import public_key_from_ed25519_key
from transactions.common.exceptions import SchemaValidationError, ValidationError, DuplicateTransaction, \
InvalidSignature, DoubleSpend, InputDoesNotExist, AssetIdMismatch, AmountError, MultipleInputsError, \
InvalidProposer, UnequalValidatorSet, InvalidPowerChange
from transactions.common.transaction import VALIDATOR_ELECTION, CHAIN_MIGRATION_ELECTION
from transactions.common.transaction_mode_types import BROADCAST_TX_COMMIT, BROADCAST_TX_ASYNC, BROADCAST_TX_SYNC
from transactions.types.elections.election import Election
from transactions.types.elections.validator_utils import election_id_to_public_key
import planetmint
from planetmint.config import Config
from planetmint import backend, config_utils, fastquery
from planetmint.transactions.common.transaction import CHAIN_MIGRATION_ELECTION, VALIDATOR_ELECTION, Transaction
from planetmint.transactions.common.exceptions import (
DuplicateTransaction,
InvalidSignature,
SchemaValidationError,
ValidationError,
DoubleSpend,
AmountError,
InputDoesNotExist,
AssetIdMismatch,
InvalidProposer,
UnequalValidatorSet,
DuplicateTransaction,
MultipleInputsError,
InvalidPowerChange
)
from planetmint.transactions.common.crypto import public_key_from_ed25519_key
from planetmint.transactions.common.transaction_mode_types import (
BROADCAST_TX_COMMIT,
BROADCAST_TX_ASYNC,
BROADCAST_TX_SYNC,
)
from planetmint.tendermint_utils import encode_transaction, merkleroot, key_from_base64, public_key_to_base64, encode_validator, new_validator_set
from planetmint import exceptions as core_exceptions
from planetmint.transactions.types.elections.election import Election
from planetmint.transactions.types.elections.vote import Vote
from planetmint.transactions.types.elections.validator_utils import election_id_to_public_key
from planetmint.validation import BaseValidationRules
logger = logging.getLogger(__name__)

View File

@ -6,7 +6,7 @@
import planetmint
import logging
from planetmint.transactions.common.exceptions import ConfigurationError
from transactions.common.exceptions import ConfigurationError
from logging.config import dictConfig as set_logging_config
from planetmint.config import Config, DEFAULT_LOGGING_CONFIG
import os

View File

@ -14,7 +14,7 @@ from tendermint.crypto import keys_pb2
from hashlib import sha3_256
from planetmint.transactions.common.exceptions import InvalidPublicKey
from transactions.common.exceptions import InvalidPublicKey
def encode_validator(v):
ed25519_public_key = v["public_key"]["value"]

View File

@ -1,51 +0,0 @@
# 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
# Separate all crypto code so that we can easily test several implementations
from collections import namedtuple
from hashlib import sha3_256
from cryptoconditions import crypto
CryptoKeypair = namedtuple("CryptoKeypair", ("private_key", "public_key"))
def hash_data(data):
"""Hash the provided data using SHA3-256"""
return sha3_256(data.encode()).hexdigest()
def generate_key_pair() -> CryptoKeypair:
"""Generates a cryptographic key pair.
Returns:
:class:`~planetmint.transactions.common.crypto.CryptoKeypair`: A
:obj:`collections.namedtuple` with named fields
:attr:`~planetmint.transactions.common.crypto.CryptoKeypair.private_key` and
:attr:`~planetmint.transactions.common.crypto.CryptoKeypair.public_key`.
"""
# TODO FOR CC: Adjust interface so that this function becomes unnecessary
return CryptoKeypair(*(k.decode() for k in crypto.ed25519_generate_key_pair()))
PrivateKey = crypto.Ed25519SigningKey
PublicKey = crypto.Ed25519VerifyingKey
def key_pair_from_ed25519_key(hex_private_key):
"""Generate base58 encode public-private key pair from a hex encoded private key"""
priv_key = crypto.Ed25519SigningKey(bytes.fromhex(hex_private_key)[:32], encoding="bytes")
public_key = priv_key.get_verifying_key()
return CryptoKeypair(
private_key=priv_key.encode(encoding="base58").decode("utf-8"),
public_key=public_key.encode(encoding="base58").decode("utf-8"),
)
def public_key_from_ed25519_key(hex_public_key):
"""Generate base58 public key from hex encoded public key"""
public_key = crypto.Ed25519VerifyingKey(bytes.fromhex(hex_public_key), encoding="bytes")
return public_key.encode(encoding="base58").decode("utf-8")

View File

@ -1,115 +0,0 @@
# 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
"""Custom exceptions used in the `planetmint` package.
"""
from planetmint.exceptions import PlanetmintError
class ConfigurationError(PlanetmintError):
"""Raised when there is a problem with server configuration"""
class DatabaseDoesNotExist(PlanetmintError):
"""Raised when trying to delete the database but the db is not there"""
class StartupError(PlanetmintError):
"""Raised when there is an error starting up the system"""
class CyclicBlockchainError(PlanetmintError):
"""Raised when there is a cycle in the blockchain"""
class KeypairMismatchException(PlanetmintError):
"""Raised if the private key(s) provided for signing don't match any of the
current owner(s)
"""
class OperationError(PlanetmintError):
"""Raised when an operation cannot go through"""
################################################################################
# Validation errors
#
# All validation errors (which are handleable errors, not faults) should
# subclass ValidationError. However, where possible they should also have their
# own distinct type to differentiate them from other validation errors,
# especially for the purposes of testing.
class ValidationError(PlanetmintError):
"""Raised if there was an error in validation"""
class DoubleSpend(ValidationError):
"""Raised if a double spend is found"""
class InvalidHash(ValidationError):
"""Raised if there was an error checking the hash for a particular
operation
"""
class SchemaValidationError(ValidationError):
"""Raised if there was any error validating an object's schema"""
class InvalidSignature(ValidationError):
"""Raised if there was an error checking the signature for a particular
operation
"""
class AssetIdMismatch(ValidationError):
"""Raised when multiple transaction inputs related to different assets"""
class AmountError(ValidationError):
"""Raised when there is a problem with a transaction's output amounts"""
class InputDoesNotExist(ValidationError):
"""Raised if a transaction input does not exist"""
class TransactionOwnerError(ValidationError):
"""Raised if a user tries to transfer a transaction they don't own"""
class DuplicateTransaction(ValidationError):
"""Raised if a duplicated transaction is found"""
class ThresholdTooDeep(ValidationError):
"""Raised if threshold condition is too deep"""
class MultipleValidatorOperationError(ValidationError):
"""Raised when a validator update pending but new request is submited"""
class MultipleInputsError(ValidationError):
"""Raised if there were multiple inputs when only one was expected"""
class InvalidProposer(ValidationError):
"""Raised if the public key is not a part of the validator set"""
class UnequalValidatorSet(ValidationError):
"""Raised if the validator sets differ"""
class InvalidPowerChange(ValidationError):
"""Raised if proposed power change in validator set is >=1/3 total power"""
class InvalidPublicKey(ValidationError):
"""Raised if public key doesn't match the encoding type"""

View File

@ -1,127 +0,0 @@
# 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
from typing import Optional
from cryptoconditions import Fulfillment
from cryptoconditions.exceptions import ASN1DecodeError, ASN1EncodeError
from planetmint.transactions.common.exceptions import InvalidSignature
from .utils import _fulfillment_to_details, _fulfillment_from_details
from .output import Output
from .transaction_link import TransactionLink
class Input(object):
"""A Input is used to spend assets locked by an Output.
Wraps around a Crypto-condition Fulfillment.
Attributes:
fulfillment (:class:`cryptoconditions.Fulfillment`): A Fulfillment
to be signed with a private key.
owners_before (:obj:`list` of :obj:`str`): A list of owners after a
Transaction was confirmed.
fulfills (:class:`~planetmint.transactions.common.transaction. TransactionLink`,
optional): A link representing the input of a `TRANSFER`
Transaction.
"""
def __init__(self, fulfillment: Fulfillment, owners_before: list[str], fulfills: Optional[TransactionLink] = None):
"""Create an instance of an :class:`~.Input`.
Args:
fulfillment (:class:`cryptoconditions.Fulfillment`): A
Fulfillment to be signed with a private key.
owners_before (:obj:`list` of :obj:`str`): A list of owners
after a Transaction was confirmed.
fulfills (:class:`~planetmint.transactions.common.transaction.
TransactionLink`, optional): A link representing the input
of a `TRANSFER` Transaction.
"""
if fulfills is not None and not isinstance(fulfills, TransactionLink):
raise TypeError("`fulfills` must be a TransactionLink instance")
if not isinstance(owners_before, list):
raise TypeError("`owners_before` must be a list instance")
self.fulfillment = fulfillment
self.fulfills = fulfills
self.owners_before = owners_before
def __eq__(self, other):
# TODO: If `other !== Fulfillment` return `False`
return self.to_dict() == other.to_dict()
# NOTE: This function is used to provide a unique key for a given
# Input to suppliment memoization
def __hash__(self):
return hash((self.fulfillment, self.fulfills))
def to_dict(self):
"""Transforms the object to a Python dictionary.
Note:
If an Input hasn't been signed yet, this method returns a
dictionary representation.
Returns:
dict: The Input as an alternative serialization format.
"""
try:
fulfillment = self.fulfillment.serialize_uri()
except (TypeError, AttributeError, ASN1EncodeError, ASN1DecodeError):
fulfillment = _fulfillment_to_details(self.fulfillment)
try:
# NOTE: `self.fulfills` can be `None` and that's fine
fulfills = self.fulfills.to_dict()
except AttributeError:
fulfills = None
input_ = {
"owners_before": self.owners_before,
"fulfills": fulfills,
"fulfillment": fulfillment,
}
return input_
@classmethod
def generate(cls, public_keys: list[str]):
# TODO: write docstring
# The amount here does not really matter. It is only use on the
# output data model but here we only care about the fulfillment
output = Output.generate(public_keys, 1)
return cls(output.fulfillment, public_keys)
@classmethod
def from_dict(cls, data: dict):
"""Transforms a Python dictionary to an Input object.
Note:
Optionally, this method can also serialize a Cryptoconditions-
Fulfillment that is not yet signed.
Args:
data (dict): The Input to be transformed.
Returns:
:class:`~planetmint.transactions.common.transaction.Input`
Raises:
InvalidSignature: If an Input's URI couldn't be parsed.
"""
fulfillment = data["fulfillment"]
if not isinstance(fulfillment, (Fulfillment, type(None))):
try:
fulfillment = Fulfillment.from_uri(data["fulfillment"])
except ASN1DecodeError:
# TODO Remove as it is legacy code, and simply fall back on
# ASN1DecodeError
raise InvalidSignature("Fulfillment URI couldn't been parsed")
except TypeError:
# NOTE: See comment about this special case in
# `Input.to_dict`
fulfillment = _fulfillment_from_details(data["fulfillment"])
fulfills = TransactionLink.from_dict(data["fulfills"])
return cls(fulfillment, data["owners_before"], fulfills)

View File

@ -1,57 +0,0 @@
import functools
import codecs
from functools import lru_cache
from typing import Callable
class HDict(dict):
def __hash__(self):
return hash(codecs.decode(self["id"], "hex"))
@lru_cache(maxsize=16384)
def from_dict(func: Callable, *args, **kwargs):
return func(*args, **kwargs)
def memoize_from_dict(func: Callable):
@functools.wraps(func)
def memoized_func(*args, **kwargs):
if args[1] is None:
return None
elif args[1].get("id", None):
args = list(args)
args[1] = HDict(args[1])
new_args = tuple(args)
return from_dict(func, *new_args, **kwargs)
else:
return func(*args, **kwargs)
return memoized_func
class ToDictWrapper:
def __init__(self, tx):
self.tx = tx
def __eq__(self, other):
return self.tx.id == other.tx.id
def __hash__(self):
return hash(self.tx.id)
@lru_cache(maxsize=16384)
def to_dict(func, tx_wrapped):
return func(tx_wrapped.tx)
def memoize_to_dict(func: Callable):
@functools.wraps(func)
def memoized_func(*args, **kwargs):
if args[0].id:
return to_dict(func, ToDictWrapper(args[0]))
else:
return func(*args, **kwargs)
return memoized_func

View File

@ -1,211 +0,0 @@
# 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
from functools import reduce
from typing import Union, Optional
import base58
from cryptoconditions import ThresholdSha256, Ed25519Sha256, ZenroomSha256
from cryptoconditions import Fulfillment
from planetmint.transactions.common.exceptions import AmountError
from .utils import _fulfillment_to_details, _fulfillment_from_details
class Output(object):
"""An Output is used to lock an asset.
Wraps around a Crypto-condition Condition.
Attributes:
fulfillment (:class:`cryptoconditions.Fulfillment`): A Fulfillment
to extract a Condition from.
public_keys (:obj:`list` of :obj:`str`, optional): A list of
owners before a Transaction was confirmed.
"""
MAX_AMOUNT = 9 * 10**18
def __init__(self, fulfillment: type[Fulfillment], public_keys: Optional[list[str]] = None, amount: int = 1):
"""Create an instance of a :class:`~.Output`.
Args:
fulfillment (:class:`cryptoconditions.Fulfillment`): A
Fulfillment to extract a Condition from.
public_keys (:obj:`list` of :obj:`str`, optional): A list of
owners before a Transaction was confirmed.
amount (int): The amount of Assets to be locked with this
Output.
Raises:
TypeError: if `public_keys` is not instance of `list`.
"""
if not isinstance(public_keys, list) and public_keys is not None:
raise TypeError("`public_keys` must be a list instance or None")
if not isinstance(amount, int):
raise TypeError("`amount` must be an int")
if amount < 1:
raise AmountError("`amount` must be greater than 0")
if amount > self.MAX_AMOUNT:
raise AmountError("`amount` must be <= %s" % self.MAX_AMOUNT)
self.fulfillment = fulfillment
self.amount = amount
self.public_keys = public_keys
def __eq__(self, other):
# TODO: If `other !== Condition` return `False`
return self.to_dict() == other.to_dict()
def to_dict(self):
"""Transforms the object to a Python dictionary.
Note:
A dictionary serialization of the Input the Output was
derived from is always provided.
Returns:
dict: The Output as an alternative serialization format.
"""
# TODO FOR CC: It must be able to recognize a hashlock condition
# and fulfillment!
condition = {}
try:
# TODO verify if a script is returned in case of zenroom fulfillments
condition["details"] = _fulfillment_to_details(self.fulfillment)
except AttributeError:
pass
try:
condition["uri"] = self.fulfillment.condition_uri
except AttributeError:
condition["uri"] = self.fulfillment
output = {
"public_keys": self.public_keys,
"condition": condition,
"amount": str(self.amount),
}
return output
@classmethod
def generate(cls, public_keys: list[str], amount: int):
"""Generates a Output from a specifically formed tuple or list.
Note:
If a ThresholdCondition has to be generated where the threshold
is always the number of subconditions it is split between, a
list of the following structure is sufficient:
[(address|condition)*, [(address|condition)*, ...], ...]
Args:
public_keys (:obj:`list` of :obj:`str`): The public key of
the users that should be able to fulfill the Condition
that is being created.
amount (:obj:`int`): The amount locked by the Output.
Returns:
An Output that can be used in a Transaction.
Raises:
TypeError: If `public_keys` is not an instance of `list`.
ValueError: If `public_keys` is an empty list.
"""
threshold = len(public_keys)
if not isinstance(amount, int):
raise TypeError("`amount` must be a int")
if amount < 1:
raise AmountError("`amount` needs to be greater than zero")
if not isinstance(public_keys, list):
raise TypeError("`public_keys` must be an instance of list")
if len(public_keys) == 0:
raise ValueError("`public_keys` needs to contain at least one" "owner")
elif len(public_keys) == 1 and not isinstance(public_keys[0], list):
if isinstance(public_keys[0], Fulfillment):
ffill = public_keys[0]
elif isinstance(public_keys[0], ZenroomSha256):
ffill = ZenroomSha256(public_key=base58.b58decode(public_keys[0]))
else:
ffill = Ed25519Sha256(public_key=base58.b58decode(public_keys[0]))
return cls(ffill, public_keys, amount=amount)
else:
initial_cond = ThresholdSha256(threshold=threshold)
threshold_cond = reduce(cls._gen_condition, public_keys, initial_cond)
return cls(threshold_cond, public_keys, amount=amount)
@classmethod
def _gen_condition(cls, initial: type[ThresholdSha256], new_public_keys: Union[list[str],str]) -> type[ThresholdSha256]:
"""Generates ThresholdSha256 conditions from a list of new owners.
Note:
This method is intended only to be used with a reduce function.
For a description on how to use this method, see
:meth:`~.Output.generate`.
Args:
initial (:class:`cryptoconditions.ThresholdSha256`):
A Condition representing the overall root.
new_public_keys (:obj:`list` of :obj:`str`|str): A list of new
owners or a single new owner.
Returns:
:class:`cryptoconditions.ThresholdSha256`:
"""
try:
threshold = len(new_public_keys)
except TypeError:
threshold = None
if isinstance(new_public_keys, list) and len(new_public_keys) > 1:
ffill = ThresholdSha256(threshold=threshold)
reduce(cls._gen_condition, new_public_keys, ffill)
elif isinstance(new_public_keys, list) and len(new_public_keys) <= 1:
raise ValueError("Sublist cannot contain single owner")
else:
try:
if isinstance(new_public_keys, list):
new_public_keys = new_public_keys.pop()
except AttributeError:
pass
# NOTE: Instead of submitting base58 encoded addresses, a user
# of this class can also submit fully instantiated
# Cryptoconditions. In the case of casting
# `new_public_keys` to a Ed25519Fulfillment with the
# result of a `TypeError`, we're assuming that
# `new_public_keys` is a Cryptocondition then.
if isinstance(new_public_keys, Fulfillment):
ffill = new_public_keys
else:
ffill = Ed25519Sha256(public_key=base58.b58decode(new_public_keys))
initial.add_subfulfillment(ffill)
return initial
@classmethod
def from_dict(cls, data: dict):
"""Transforms a Python dictionary to an Output object.
Note:
To pass a serialization cycle multiple times, a
Cryptoconditions Fulfillment needs to be present in the
passed-in dictionary, as Condition URIs are not serializable
anymore.
Args:
data (dict): The dict to be transformed.
Returns:
:class:`~planetmint.transactions.common.transaction.Output`
"""
try:
fulfillment = _fulfillment_from_details(data["condition"]["details"])
except KeyError:
# NOTE: Hashlock condition case
fulfillment = data["condition"]["uri"]
try:
amount = int(data["amount"])
except ValueError:
raise AmountError("Invalid amount: %s" % data["amount"])
return cls(fulfillment, data["public_keys"], amount)

View File

@ -1,54 +0,0 @@
<!---
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
--->
# Introduction
This directory contains the schemas for the different JSON documents Planetmint uses.
The aim is to provide:
- a strict definition of the data structures used in Planetmint,
- a language-independent tool to validate the structure of incoming/outcoming
data. (There are several ready to use
[implementations](http://json-schema.org/implementations.html) written in
different languages.)
## Sources
The files defining the JSON Schema for transactions (`transaction_*.yaml`)
are based on the [Planetmint Transactions Specs](https://github.com/planetmint/BEPs/tree/master/tx-specs).
If you want to add a new transaction version,
you must write a spec for it first.
(You can't change the JSON Schema files for old versions.
Those were used to validate old transactions
and are needed to re-check those transactions.)
There used to be a file defining the JSON Schema for votes, named `vote.yaml`.
It was used by Planetmint version 1.3.0 and earlier.
If you want a copy of the latest `vote.yaml` file,
then you can get it from the version 1.3.0 release on GitHub, at
[https://github.com/planetmint/planetmint/blob/v1.3.0/planetmint/common/schema/vote.yaml](https://github.com/planetmint/planetmint/blob/v1.3.0/planetmint/common/schema/vote.yaml).
## Learn about JSON Schema
A good resource is [Understanding JSON Schema](http://spacetelescope.github.io/understanding-json-schema/index.html).
It provides a *more accessible documentation for JSON schema* than the [specs](http://json-schema.org/documentation.html).
## If it's supposed to be JSON, why's everything in YAML D:?
YAML is great for its conciseness and friendliness towards human-editing in comparision to JSON.
Although YAML is a superset of JSON, at the end of the day, JSON Schema processors, like
[json-schema](http://python-jsonschema.readthedocs.io/en/latest/), take in a native object (e.g.
Python dicts or JavaScript objects) as the schema used for validation. As long as we can serialize
the YAML into what the JSON Schema processor expects (almost always as simple as loading the YAML
like you would with a JSON file), it's the same as using JSON.
Specific advantages of using YAML:
- Legibility, especially when nesting
- Multi-line string literals, that make it easy to include descriptions that can be [auto-generated
into Sphinx documentation](/docs/server/generate_schema_documentation.py)

View File

@ -1,78 +0,0 @@
# 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
"""Schema validation related functions and data"""
import os.path
import logging
import jsonschema
import yaml
import rapidjson
from planetmint.transactions.common.exceptions import SchemaValidationError
logger = logging.getLogger(__name__)
def _load_schema(name, version, path=__file__):
"""Load a schema from disk"""
path = os.path.join(os.path.dirname(path), version, name + ".yaml")
with open(path) as handle:
schema = yaml.safe_load(handle)
fast_schema = rapidjson.Validator(rapidjson.dumps(schema))
return path, (schema, fast_schema)
# TODO: make this an env var from a config file
TX_SCHEMA_VERSION = "v2.0"
TX_SCHEMA_PATH, TX_SCHEMA_COMMON = _load_schema("transaction", TX_SCHEMA_VERSION)
_, TX_SCHEMA_CREATE = _load_schema("transaction_create", TX_SCHEMA_VERSION)
_, TX_SCHEMA_TRANSFER = _load_schema("transaction_transfer", TX_SCHEMA_VERSION)
_, TX_SCHEMA_VALIDATOR_ELECTION = _load_schema("transaction_validator_election", TX_SCHEMA_VERSION)
_, TX_SCHEMA_CHAIN_MIGRATION_ELECTION = _load_schema("transaction_chain_migration_election", TX_SCHEMA_VERSION)
_, TX_SCHEMA_VOTE = _load_schema("transaction_vote", TX_SCHEMA_VERSION)
def _validate_schema(schema, body):
"""Validate data against a schema"""
# Note
#
# Schema validation is currently the major CPU bottleneck of
# Planetmint. the `jsonschema` library validates python data structures
# directly and produces nice error messages, but validation takes 4+ ms
# per transaction which is pretty slow. The rapidjson library validates
# much faster at 1.5ms, however it produces _very_ poor error messages.
# For this reason we use both, rapidjson as an optimistic pathway and
# jsonschema as a fallback in case there is a failure, so we can produce
# a helpful error message.
try:
schema[1](rapidjson.dumps(body))
except ValueError as exc:
try:
jsonschema.validate(body, schema[0])
except jsonschema.ValidationError as exc2:
raise SchemaValidationError(str(exc2)) from exc2
logger.warning("code problem: jsonschema did not raise an exception, wheras rapidjson raised %s", exc)
raise SchemaValidationError(str(exc)) from exc
def validate_transaction_schema(tx):
"""Validate a transaction dict.
TX_SCHEMA_COMMON contains properties that are common to all types of
transaction. TX_SCHEMA_[TRANSFER|CREATE] add additional constraints on top.
"""
_validate_schema(TX_SCHEMA_COMMON, tx)
if tx["operation"] == "TRANSFER":
_validate_schema(TX_SCHEMA_TRANSFER, tx)
else:
_validate_schema(TX_SCHEMA_CREATE, tx)

View File

@ -1,168 +0,0 @@
# 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
---
"$schema": "http://json-schema.org/draft-04/schema#"
type: object
additionalProperties: false
title: Transaction Schema
required:
- id
- inputs
- outputs
- operation
- metadata
- asset
- version
properties:
id:
anyOf:
- "$ref": "#/definitions/sha3_hexdigest"
- type: 'null'
operation:
"$ref": "#/definitions/operation"
asset:
"$ref": "#/definitions/asset"
inputs:
type: array
title: "Transaction inputs"
items:
"$ref": "#/definitions/input"
outputs:
type: array
items:
"$ref": "#/definitions/output"
metadata:
"$ref": "#/definitions/metadata"
version:
type: string
pattern: "^1\\.0$"
definitions:
offset:
type: integer
minimum: 0
base58:
pattern: "[1-9a-zA-Z^OIl]{43,44}"
type: string
public_keys:
anyOf:
- type: array
items:
"$ref": "#/definitions/base58"
- type: 'null'
sha3_hexdigest:
pattern: "[0-9a-f]{64}"
type: string
uuid4:
pattern: "[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}"
type: string
operation:
type: string
enum:
- CREATE
- TRANSFER
- GENESIS
asset:
type: object
additionalProperties: false
properties:
id:
"$ref": "#/definitions/sha3_hexdigest"
data:
anyOf:
- type: object
additionalProperties: true
- type: 'null'
output:
type: object
additionalProperties: false
required:
- amount
- condition
- public_keys
properties:
amount:
type: string
pattern: "^[0-9]{1,20}$"
condition:
type: object
additionalProperties: false
required:
- details
- uri
properties:
details:
"$ref": "#/definitions/condition_details"
uri:
type: string
pattern: "^ni:///sha-256;([a-zA-Z0-9_-]{0,86})[?]\
(fpt=(ed25519|threshold)-sha-256(&)?|cost=[0-9]+(&)?|\
subtypes=ed25519-sha-256(&)?){2,3}$"
public_keys:
"$ref": "#/definitions/public_keys"
input:
type: "object"
additionalProperties: false
required:
- owners_before
- fulfillment
properties:
owners_before:
"$ref": "#/definitions/public_keys"
fulfillment:
anyOf:
- type: string
pattern: "^[a-zA-Z0-9_-]*$"
- "$ref": "#/definitions/condition_details"
fulfills:
anyOf:
- type: 'object'
additionalProperties: false
required:
- output_index
- transaction_id
properties:
output_index:
"$ref": "#/definitions/offset"
transaction_id:
"$ref": "#/definitions/sha3_hexdigest"
- type: 'null'
metadata:
anyOf:
- type: object
additionalProperties: true
minProperties: 1
- type: 'null'
condition_details:
anyOf:
- type: object
additionalProperties: false
required:
- type
- public_key
properties:
type:
type: string
pattern: "^ed25519-sha-256$"
public_key:
"$ref": "#/definitions/base58"
- type: object
additionalProperties: false
required:
- type
- threshold
- subconditions
properties:
type:
type: "string"
pattern: "^threshold-sha-256$"
threshold:
type: integer
minimum: 1
maximum: 100
subconditions:
type: array
items:
"$ref": "#/definitions/condition_details"

View File

@ -1,35 +0,0 @@
# 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
---
"$schema": "http://json-schema.org/draft-04/schema#"
type: object
title: Transaction Schema - CREATE/GENESIS specific constraints
required:
- asset
- inputs
properties:
asset:
additionalProperties: false
properties:
data:
anyOf:
- type: object
additionalProperties: true
- type: 'null'
required:
- data
inputs:
type: array
title: "Transaction inputs"
maxItems: 1
minItems: 1
items:
type: "object"
required:
- fulfills
properties:
fulfills:
type: "null"

View File

@ -1,34 +0,0 @@
# 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
---
"$schema": "http://json-schema.org/draft-04/schema#"
type: object
title: Transaction Schema - TRANSFER specific properties
required:
- asset
properties:
asset:
additionalProperties: false
properties:
id:
"$ref": "#/definitions/sha3_hexdigest"
required:
- id
inputs:
type: array
title: "Transaction inputs"
minItems: 1
items:
type: "object"
required:
- fulfills
properties:
fulfills:
type: "object"
definitions:
sha3_hexdigest:
pattern: "[0-9a-f]{64}"
type: string

View File

@ -1,215 +0,0 @@
# 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
---
"$schema": "http://json-schema.org/draft-04/schema#"
type: object
additionalProperties: false
title: Transaction Schema
required:
- id
- inputs
- outputs
- operation
- metadata
- asset
- version
properties:
id:
anyOf:
- "$ref": "#/definitions/sha3_hexdigest"
- type: 'null'
operation:
"$ref": "#/definitions/operation"
asset:
"$ref": "#/definitions/asset"
inputs:
type: array
title: "Transaction inputs"
items:
"$ref": "#/definitions/input"
outputs:
type: array
items:
"$ref": "#/definitions/output"
metadata:
"$ref": "#/definitions/metadata"
version:
type: string
pattern: "^2\\.0$"
script:
"$ref": "#/definitions/script"
definitions:
offset:
type: integer
minimum: 0
base58:
pattern: "[1-9a-zA-Z^OIl]{43,44}"
type: string
public_keys:
anyOf:
- type: array
items:
"$ref": "#/definitions/base58"
- type: 'null'
sha3_hexdigest:
pattern: "[0-9a-f]{64}"
type: string
uuid4:
pattern: "[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}"
type: string
operation:
type: string
enum:
- CREATE
- TRANSFER
- VALIDATOR_ELECTION
- CHAIN_MIGRATION_ELECTION
- VOTE
asset:
anyOf:
- type: 'null'
- type: object
output:
type: object
additionalProperties: false
required:
- amount
- condition
- public_keys
properties:
amount:
type: string
pattern: "^[0-9]{1,20}$"
condition:
type: object
additionalProperties: false
required:
- details
- uri
properties:
details:
"$ref": "#/definitions/condition_details"
uri:
type: string
pattern: "^ni:///sha-256;([a-zA-Z0-9_-]{0,86})[?]\
(fpt=(ed25519|threshold|zenroom)-sha-256(&)?|cost=[0-9]+(&)?|\
subtypes=(ed25519|zenroom)-sha-256(&)?){2,3}$"
public_keys:
"$ref": "#/definitions/public_keys"
input:
type: "object"
additionalProperties: false
required:
- owners_before
- fulfillment
properties:
owners_before:
"$ref": "#/definitions/public_keys"
fulfillment:
anyOf:
- type: string
pattern: "^[a-zA-Z0-9_-]*$"
- "$ref": "#/definitions/condition_details"
fulfills:
anyOf:
- type: 'object'
additionalProperties: false
required:
- output_index
- transaction_id
properties:
output_index:
"$ref": "#/definitions/offset"
transaction_id:
"$ref": "#/definitions/sha3_hexdigest"
- type: 'null'
metadata:
anyOf:
- type: string
- type: 'null'
condition_details:
anyOf:
- type: object
additionalProperties: false
required:
- type
- public_key
properties:
type:
type: string
pattern: "^(ed25519|zenroom)-sha-256$"
public_key:
"$ref": "#/definitions/base58"
- type: object
additionalProperties: false
required:
- type
- threshold
- subconditions
properties:
type:
type: "string"
pattern: "^threshold-sha-256$"
threshold:
type: integer
minimum: 1
maximum: 100
subconditions:
type: array
items:
"$ref": "#/definitions/condition_details"
script:
type: object
additionalProperties: false
required:
- code
- state
- input
- output
properties:
code:
anyOf:
- type: object
additionalProperties: false
required:
- type
- raw
- parameters
properties:
type:
type: string
enum:
- zenroom
raw:
type: string
parameters:
type: array
items:
type: object
- type: object
additionalProperties: false
required:
- transaction_id
properties:
transaction_id:
"$ref": "#/definitions/sha3_hexdigest"
state:
anyOf:
- type: object
"$ref": "#/definitions/sha3_hexdigest"
input:
type: object
output:
anyOf:
- type: object
- type: array
policies:
type: object
properties:
raw:
type: object
txids:
type: object

View File

@ -1,45 +0,0 @@
# 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
---
"$schema": "http://json-schema.org/draft-04/schema#"
type: object
title: Chain Migration Election Schema - Propose a halt in block production to allow for a version change
required:
- operation
- asset
- outputs
properties:
operation:
type: string
value: "CHAIN_MIGRATION_ELECTION"
asset:
additionalProperties: false
properties:
data:
additionalProperties: false
properties:
seed:
type: string
required:
- data
outputs:
type: array
items:
"$ref": "#/definitions/output"
definitions:
output:
type: object
properties:
condition:
type: object
required:
- uri
properties:
uri:
type: string
pattern: "^ni:///sha-256;([a-zA-Z0-9_-]{0,86})[?]\
(fpt=ed25519-sha-256(&)?|cost=[0-9]+(&)?|\
subtypes=ed25519-sha-256(&)?){2,3}$"

View File

@ -1,34 +0,0 @@
# 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
---
"$schema": "http://json-schema.org/draft-04/schema#"
type: object
title: Transaction Schema - CREATE specific constraints
required:
- asset
- inputs
properties:
asset:
additionalProperties: false
properties:
data:
anyOf:
- type: string
- type: 'null'
required:
- data
inputs:
type: array
title: "Transaction inputs"
maxItems: 1
minItems: 1
items:
type: "object"
required:
- fulfills
properties:
fulfills:
type: "null"

View File

@ -1,34 +0,0 @@
# 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
---
"$schema": "http://json-schema.org/draft-04/schema#"
type: object
title: Transaction Schema - TRANSFER specific properties
required:
- asset
properties:
asset:
additionalProperties: false
properties:
id:
"$ref": "#/definitions/sha3_hexdigest"
required:
- id
inputs:
type: array
title: "Transaction inputs"
minItems: 1
items:
type: "object"
required:
- fulfills
properties:
fulfills:
type: "object"
definitions:
sha3_hexdigest:
pattern: "[0-9a-f]{64}"
type: string

View File

@ -1,68 +0,0 @@
# 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
---
"$schema": "http://json-schema.org/draft-04/schema#"
type: object
title: Validator Election Schema - Propose a change to validator set
required:
- operation
- asset
- outputs
properties:
operation:
type: string
value: "VALIDATOR_ELECTION"
asset:
additionalProperties: false
properties:
data:
additionalProperties: false
properties:
node_id:
type: string
seed:
type: string
public_key:
type: object
additionalProperties: false
required:
- value
- type
properties:
value:
type: string
type:
type: string
enum:
- ed25519-base16
- ed25519-base32
- ed25519-base64
power:
"$ref": "#/definitions/positiveInteger"
required:
- node_id
- public_key
- power
required:
- data
outputs:
type: array
items:
"$ref": "#/definitions/output"
definitions:
output:
type: object
properties:
condition:
type: object
required:
- uri
properties:
uri:
type: string
pattern: "^ni:///sha-256;([a-zA-Z0-9_-]{0,86})[?]\
(fpt=ed25519-sha-256(&)?|cost=[0-9]+(&)?|\
subtypes=ed25519-sha-256(&)?){2,3}$"

View File

@ -1,34 +0,0 @@
# 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
---
"$schema": "http://json-schema.org/draft-04/schema#"
type: object
title: Vote Schema - Vote on an election
required:
- operation
- outputs
properties:
operation:
type: string
value: "VOTE"
outputs:
type: array
items:
"$ref": "#/definitions/output"
definitions:
output:
type: object
properties:
condition:
type: object
required:
- uri
properties:
uri:
type: string
pattern: "^ni:///sha-256;([a-zA-Z0-9_-]{0,86})[?]\
(fpt=ed25519-sha-256(&)?|cost=[0-9]+(&)?|\
subtypes=ed25519-sha-256(&)?){2,3}$"

View File

@ -1,219 +0,0 @@
# 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
---
"$schema": "http://json-schema.org/draft-04/schema#"
type: object
additionalProperties: false
title: Transaction Schema
required:
- id
- inputs
- outputs
- operation
- metadata
- assets
- version
properties:
id:
anyOf:
- "$ref": "#/definitions/sha3_hexdigest"
- type: 'null'
operation:
"$ref": "#/definitions/operation"
assets:
type: array
items:
"$ref": "#/definitions/asset"
inputs:
type: array
title: "Transaction inputs"
items:
"$ref": "#/definitions/input"
outputs:
type: array
items:
"$ref": "#/definitions/output"
metadata:
"$ref": "#/definitions/metadata"
version:
type: string
pattern: "^3\\.0$"
script:
"$ref": "#/definitions/script"
definitions:
offset:
type: integer
minimum: 0
base58:
pattern: "[1-9a-zA-Z^OIl]{43,44}"
type: string
public_keys:
anyOf:
- type: array
items:
"$ref": "#/definitions/base58"
- type: 'null'
sha3_hexdigest:
pattern: "[0-9a-f]{64}"
type: string
uuid4:
pattern: "[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}"
type: string
operation:
type: string
enum:
- CREATE
- TRANSFER
- VALIDATOR_ELECTION
- CHAIN_MIGRATION_ELECTION
- VOTE
- COMPOSE
- DECOMPOSE
asset:
anyOf:
- type: 'null'
- type: object
output:
type: object
additionalProperties: false
required:
- amount
- condition
- public_keys
properties:
amount:
type: string
pattern: "^[0-9]{1,20}$"
condition:
type: object
additionalProperties: false
required:
- details
- uri
properties:
details:
"$ref": "#/definitions/condition_details"
uri:
type: string
pattern: "^ni:///sha-256;([a-zA-Z0-9_-]{0,86})[?]\
(fpt=(ed25519|threshold)-sha-256(&)?|cost=[0-9]+(&)?|\
subtypes=ed25519-sha-256(&)?){2,3}$"
public_keys:
"$ref": "#/definitions/public_keys"
input:
type: "object"
additionalProperties: false
required:
- owners_before
- fulfillment
properties:
owners_before:
"$ref": "#/definitions/public_keys"
fulfillment:
anyOf:
- type: string
pattern: "^[a-zA-Z0-9_-]*$"
- "$ref": "#/definitions/condition_details"
fulfills:
anyOf:
- type: 'object'
additionalProperties: false
required:
- output_index
- transaction_id
properties:
output_index:
"$ref": "#/definitions/offset"
transaction_id:
"$ref": "#/definitions/sha3_hexdigest"
- type: 'null'
metadata:
anyOf:
- type: string
- type: 'null'
condition_details:
anyOf:
- type: object
additionalProperties: false
required:
- type
- public_key
properties:
type:
type: string
pattern: "^ed25519-sha-256$"
public_key:
"$ref": "#/definitions/base58"
- type: object
additionalProperties: false
required:
- type
- threshold
- subconditions
properties:
type:
type: "string"
pattern: "^threshold-sha-256$"
threshold:
type: integer
minimum: 1
maximum: 100
subconditions:
type: array
items:
"$ref": "#/definitions/condition_details"
script:
type: object
additionalProperties: false
required:
- code
- state
- input
- output
properties:
code:
anyOf:
- type: object
additionalProperties: false
required:
- type
- raw
- parameters
properties:
type:
type: string
enum:
- zenroom
raw:
type: string
parameters:
type: array
items:
type: object
- type: object
additionalProperties: false
required:
- transaction_id
properties:
transaction_id:
"$ref": "#/definitions/sha3_hexdigest"
state:
anyOf:
- type: object
"$ref": "#/definitions/sha3_hexdigest"
input:
type: object
output:
anyOf:
- type: object
- type: array
policies:
type: object
properties:
raw:
type: object
txids:
type: object

View File

@ -1,51 +0,0 @@
# 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
---
"$schema": "http://json-schema.org/draft-04/schema#"
type: object
title: Chain Migration Election Schema - Propose a halt in block production to allow for a version change
required:
- operation
- assets
- outputs
properties:
operation:
type: string
value: "CHAIN_MIGRATION_ELECTION"
assets:
type: array
minItems: 1
maxItems: 1
items:
"$ref": "#/definitions/asset"
outputs:
type: array
items:
"$ref": "#/definitions/output"
definitions:
asset:
additionalProperties: false
properties:
data:
additionalProperties: false
properties:
seed:
type: string
required:
- data
output:
type: object
properties:
condition:
type: object
required:
- uri
properties:
uri:
type: string
pattern: "^ni:///sha-256;([a-zA-Z0-9_-]{0,86})[?]\
(fpt=ed25519-sha-256(&)?|cost=[0-9]+(&)?|\
subtypes=ed25519-sha-256(&)?){2,3}$"

View File

@ -1,41 +0,0 @@
# 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
---
"$schema": "http://json-schema.org/draft-04/schema#"
type: object
title: Transaction Schema - CREATE specific constraints
required:
- assets
- inputs
properties:
assets:
type: array
minItems: 1
maxItems: 1
items:
"$ref": "#/definitions/asset"
inputs:
type: array
title: "Transaction inputs"
maxItems: 1
minItems: 1
items:
type: "object"
required:
- fulfills
properties:
fulfills:
type: "null"
definitions:
asset:
additionalProperties: false
properties:
data:
anyOf:
- type: string
- type: 'null'
required:
- data

View File

@ -1,39 +0,0 @@
# 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
---
"$schema": "http://json-schema.org/draft-04/schema#"
type: object
title: Transaction Schema - TRANSFER specific properties
required:
- assets
properties:
assets:
type: array
minItems: 1
items:
"$ref": "#/definitions/asset"
inputs:
type: array
title: "Transaction inputs"
minItems: 1
items:
type: "object"
required:
- fulfills
properties:
fulfills:
type: "object"
definitions:
sha3_hexdigest:
pattern: "[0-9a-f]{64}"
type: string
asset:
additionalProperties: false
properties:
id:
"$ref": "#/definitions/sha3_hexdigest"
required:
- id

View File

@ -1,74 +0,0 @@
# 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
---
"$schema": "http://json-schema.org/draft-04/schema#"
type: object
title: Validator Election Schema - Propose a change to validator set
required:
- operation
- assets
- outputs
properties:
operation:
type: string
value: "VALIDATOR_ELECTION"
assets:
type: array
minItems: 1
maxItems: 1
items:
"$ref": "#/definitions/asset"
outputs:
type: array
items:
"$ref": "#/definitions/output"
definitions:
output:
type: object
properties:
condition:
type: object
required:
- uri
properties:
uri:
type: string
pattern: "^ni:///sha-256;([a-zA-Z0-9_-]{0,86})[?]\
(fpt=ed25519-sha-256(&)?|cost=[0-9]+(&)?|\
subtypes=ed25519-sha-256(&)?){2,3}$"
asset:
additionalProperties: false
properties:
data:
additionalProperties: false
properties:
node_id:
type: string
seed:
type: string
public_key:
type: object
additionalProperties: false
required:
- value
- type
properties:
value:
type: string
type:
type: string
enum:
- ed25519-base16
- ed25519-base32
- ed25519-base64
power:
"$ref": "#/definitions/positiveInteger"
required:
- node_id
- public_key
- power
required:
- data

View File

@ -1,34 +0,0 @@
# 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
---
"$schema": "http://json-schema.org/draft-04/schema#"
type: object
title: Vote Schema - Vote on an election
required:
- operation
- outputs
properties:
operation:
type: string
value: "VOTE"
outputs:
type: array
items:
"$ref": "#/definitions/output"
definitions:
output:
type: object
properties:
condition:
type: object
required:
- uri
properties:
uri:
type: string
pattern: "^ni:///sha-256;([a-zA-Z0-9_-]{0,86})[?]\
(fpt=ed25519-sha-256(&)?|cost=[0-9]+(&)?|\
subtypes=ed25519-sha-256(&)?){2,3}$"

View File

@ -1,813 +0,0 @@
# 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
"""Transaction related models to parse and construct transaction
payloads.
Attributes:
UnspentOutput (namedtuple): Object holding the information
representing an unspent output.
"""
from collections import namedtuple
from copy import deepcopy
from functools import lru_cache
from typing import Optional
import rapidjson
import base58
from cryptoconditions import Fulfillment, ThresholdSha256, Ed25519Sha256, ZenroomSha256
from cryptoconditions.exceptions import ParsingError, ASN1DecodeError, ASN1EncodeError
from cid import is_cid
from hashlib import sha3_256
from planetmint.transactions.common.crypto import PrivateKey, hash_data
from planetmint.transactions.common.exceptions import (
KeypairMismatchException,
InputDoesNotExist,
DoubleSpend,
InvalidHash,
InvalidSignature,
AmountError,
AssetIdMismatch,
DuplicateTransaction,
)
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
from .transaction_link import TransactionLink
UnspentOutput = namedtuple(
"UnspentOutput",
(
# TODO 'utxo_hash': sha3_256(f'{txid}{output_index}'.encode())
# 'utxo_hash', # noqa
"transaction_id",
"output_index",
"amount",
"asset_id",
"condition_uri",
),
)
VALIDATOR_ELECTION = "VALIDATOR_ELECTION"
CHAIN_MIGRATION_ELECTION = "CHAIN_MIGRATION_ELECTION"
VOTE = "VOTE"
class Transaction(object):
"""A Transaction is used to create and transfer assets.
Note:
For adding Inputs and Outputs, this class provides methods
to do so.
Attributes:
operation (str): Defines the operation of the Transaction.
inputs (:obj:`list` of :class:`~planetmint.transactions.common.
transaction.Input`, optional): Define the assets to
spend.
outputs (:obj:`list` of :class:`~planetmint.transactions.common.
transaction.Output`, optional): Define the assets to lock.
asset (dict): Asset payload for this Transaction. ``CREATE``
Transactions require a dict with a ``data``
property while ``TRANSFER`` Transactions require a dict with a
``id`` property.
metadata (dict):
Metadata to be stored along with the Transaction.
version (string): Defines the version number of a Transaction.
"""
CREATE: str = "CREATE"
TRANSFER: str = "TRANSFER"
VALIDATOR_ELECTION: str = VALIDATOR_ELECTION
CHAIN_MIGRATION_ELECTION: str = CHAIN_MIGRATION_ELECTION
VOTE: str = VOTE
ALLOWED_OPERATIONS: tuple[str, ...] = (CREATE, TRANSFER)
ASSET: str = "asset"
METADATA: str = "metadata"
DATA: str = "data"
VERSION: str = "2.0"
def __init__(
self,
operation,
asset,
inputs=None,
outputs=None,
metadata=None,
version=None,
hash_id=None,
tx_dict=None,
script=None,
):
"""The constructor allows to create a customizable Transaction.
Note:
When no `version` is provided, one is being
generated by this method.
Args:
operation (str): Defines the operation of the Transaction.
asset (dict): Asset payload for this Transaction.
inputs (:obj:`list` of :class:`~planetmint.transactions.common.
transaction.Input`, optional): Define the assets to
outputs (:obj:`list` of :class:`~planetmint.transactions.common.
transaction.Output`, optional): Define the assets to
lock.
metadata (dict): Metadata to be stored along with the
Transaction.
version (string): Defines the version number of a Transaction.
hash_id (string): Hash id of the transaction.
"""
if operation not in self.ALLOWED_OPERATIONS:
allowed_ops = ", ".join(self.__class__.ALLOWED_OPERATIONS)
raise ValueError("`operation` must be one of {}".format(allowed_ops))
# Asset payloads for 'CREATE' operations must be None or
# dicts holding a `data` property. Asset payloads for 'TRANSFER'
# operations must be dicts holding an `id` property.
if operation == self.CREATE and asset is not None:
if not isinstance(asset, dict):
raise TypeError(
(
"`asset` must be None or a dict holding a `data` "
" property instance for '{}' Transactions".format(operation)
)
)
if "data" in asset:
if asset["data"] is not None and not isinstance(asset["data"], str):
if is_cid(asset["data"]) == False:
raise TypeError("`asset.data` not valid CID")
raise TypeError(
(
"`asset` must be None or a dict holding a `data` "
" property instance for '{}' Transactions".format(operation)
)
)
elif operation == self.TRANSFER and not (isinstance(asset, dict) and "id" in asset):
raise TypeError(("`asset` must be a dict holding an `id` property " "for 'TRANSFER' Transactions"))
if outputs and not isinstance(outputs, list):
raise TypeError("`outputs` must be a list instance or None")
if inputs and not isinstance(inputs, list):
raise TypeError("`inputs` must be a list instance or None")
if metadata is not None and not isinstance(metadata, str):
if is_cid(metadata) == False:
raise TypeError("`metadata` not valid CID")
raise TypeError("`metadata` must be a CID string or None")
if script is not None and not isinstance(script, dict):
raise TypeError("`script` must be a dict or None")
self.version = version if version is not None else self.VERSION
self.operation = operation
self.asset = asset
self.inputs = inputs or []
self.outputs = outputs or []
self.metadata = metadata
self.script = script
self._id = hash_id
self.tx_dict = tx_dict
@property
def unspent_outputs(self):
"""UnspentOutput: The outputs of this transaction, in a data
structure containing relevant information for storing them in
a UTXO set, and performing validation.
"""
if self.operation == self.CREATE:
self._asset_id = self._id
elif self.operation == self.TRANSFER:
self._asset_id = self.asset["id"]
return (
UnspentOutput(
transaction_id=self._id,
output_index=output_index,
amount=output.amount,
asset_id=self._asset_id,
condition_uri=output.fulfillment.condition_uri,
)
for output_index, output in enumerate(self.outputs)
)
@property
def spent_outputs(self):
"""Tuple of :obj:`dict`: Inputs of this transaction. Each input
is represented as a dictionary containing a transaction id and
output index.
"""
return (input_.fulfills.to_dict() for input_ in self.inputs if input_.fulfills)
@property
def serialized(self):
return Transaction._to_str(self.to_dict())
def _hash(self):
self._id = hash_data(self.serialized)
def __eq__(self, other):
try:
other = other.to_dict()
except AttributeError:
return False
return self.to_dict() == other
def to_inputs(self, indices: Optional[list[int]] = None) -> list[Input]:
"""Converts a Transaction's outputs to spendable inputs.
Note:
Takes the Transaction's outputs and derives inputs
from that can then be passed into `Transaction.transfer` as
`inputs`.
A list of integers can be passed to `indices` that
defines which outputs should be returned as inputs.
If no `indices` are passed (empty list or None) all
outputs of the Transaction are returned.
Args:
indices (:obj:`list` of int): Defines which
outputs should be returned as inputs.
Returns:
:obj:`list` of :class:`~planetmint.transactions.common.transaction.
Input`
"""
# NOTE: If no indices are passed, we just assume to take all outputs
# as inputs.
iterable_indices = indices or range(len(self.outputs))
return [
Input(
self.outputs[idx].fulfillment,
self.outputs[idx].public_keys,
TransactionLink(self.id, idx),
)
for idx in iterable_indices
]
def add_input(self, input_: Input) -> None:
"""Adds an input to a Transaction's list of inputs.
Args:
input_ (:class:`~planetmint.transactions.common.transaction.
Input`): An Input to be added to the Transaction.
"""
if not isinstance(input_, Input):
raise TypeError("`input_` must be a Input instance")
self.inputs.append(input_)
def add_output(self, output: Output) -> None:
"""Adds an output to a Transaction's list of outputs.
Args:
output (:class:`~planetmint.transactions.common.transaction.
Output`): An Output to be added to the
Transaction.
"""
if not isinstance(output, Output):
raise TypeError("`output` must be an Output instance or None")
self.outputs.append(output)
def sign(self, private_keys: list[str]):
"""Fulfills a previous Transaction's Output by signing Inputs.
Note:
This method works only for the following Cryptoconditions
currently:
- Ed25519Fulfillment
- ThresholdSha256
- ZenroomSha256
Furthermore, note that all keys required to fully sign the
Transaction have to be passed to this method. A subset of all
will cause this method to fail.
Args:
private_keys (:obj:`list` of :obj:`str`): A complete list of
all private keys needed to sign all Fulfillments of this
Transaction.
Returns:
:class:`~planetmint.transactions.common.transaction.Transaction`
"""
# TODO: Singing should be possible with at least one of all private
# keys supplied to this method.
if private_keys is None or not isinstance(private_keys, list):
raise TypeError("`private_keys` must be a list instance")
# NOTE: Generate public keys from private keys and match them in a
# dictionary:
# key: public_key
# value: private_key
def gen_public_key(private_key):
# TODO FOR CC: Adjust interface so that this function becomes
# unnecessary
# cc now provides a single method `encode` to return the key
# in several different encodings.
public_key = private_key.get_verifying_key().encode()
# Returned values from cc are always bytestrings so here we need
# to decode to convert the bytestring into a python str
return public_key.decode()
key_pairs = {gen_public_key(PrivateKey(private_key)): PrivateKey(private_key) for private_key in private_keys}
tx_dict = self.to_dict()
tx_dict = Transaction._remove_signatures(tx_dict)
tx_serialized = Transaction._to_str(tx_dict)
for i, input_ in enumerate(self.inputs):
self.inputs[i] = self._sign_input(input_, tx_serialized, key_pairs)
self._hash()
return self
@classmethod
def _sign_input(cls, input_: Input, message: str, key_pairs: dict) -> Input:
"""Signs a single Input.
Note:
This method works only for the following Cryptoconditions
currently:
- Ed25519Fulfillment
- ThresholdSha256.
- ZenroomSha256
Args:
input_ (:class:`~planetmint.transactions.common.transaction.
Input`) The Input to be signed.
message (str): The message to be signed
key_pairs (dict): The keys to sign the Transaction with.
"""
if isinstance(input_.fulfillment, Ed25519Sha256):
return cls._sign_simple_signature_fulfillment(input_, message, key_pairs)
elif isinstance(input_.fulfillment, ThresholdSha256):
return cls._sign_threshold_signature_fulfillment(input_, message, key_pairs)
elif isinstance(input_.fulfillment, ZenroomSha256):
return cls._sign_zenroom_fulfillment(input_, message, key_pairs)
else:
raise ValueError("Fulfillment couldn't be matched to " "Cryptocondition fulfillment type.")
@classmethod
def _sign_zenroom_fulfillment(cls, input_: Input, message: str, key_pairs: dict) -> Input:
"""Signs a Zenroomful.
Args:
input_ (:class:`~planetmint.transactions.common.transaction.
Input`) The input to be signed.
message (str): The message to be signed
key_pairs (dict): The keys to sign the Transaction with.
"""
# NOTE: To eliminate the dangers of accidentally signing a condition by
# reference, we remove the reference of input_ here
# intentionally. If the user of this class knows how to use it,
# this should never happen, but then again, never say never.
input_ = deepcopy(input_)
public_key = input_.owners_before[0]
sha3_message = sha3_256(message.encode())
if input_.fulfills:
sha3_message.update("{}{}".format(input_.fulfills.txid, input_.fulfills.output).encode())
try:
# cryptoconditions makes no assumptions of the encoding of the
# message to sign or verify. It only accepts bytestrings
input_.fulfillment.sign(sha3_message.digest(), base58.b58decode(key_pairs[public_key].encode()))
except KeyError:
raise KeypairMismatchException(
"Public key {} is not a pair to " "any of the private keys".format(public_key)
)
return input_
@classmethod
def _sign_simple_signature_fulfillment(cls, input_: Input, message: str, key_pairs: dict) -> Input:
"""Signs a Ed25519Fulfillment.
Args:
input_ (:class:`~planetmint.transactions.common.transaction.
Input`) The input to be signed.
message (str): The message to be signed
key_pairs (dict): The keys to sign the Transaction with.
"""
# NOTE: To eliminate the dangers of accidentally signing a condition by
# reference, we remove the reference of input_ here
# intentionally. If the user of this class knows how to use it,
# this should never happen, but then again, never say never.
input_ = deepcopy(input_)
public_key = input_.owners_before[0]
sha3_message = sha3_256(message.encode())
if input_.fulfills:
sha3_message.update("{}{}".format(input_.fulfills.txid, input_.fulfills.output).encode())
try:
# cryptoconditions makes no assumptions of the encoding of the
# message to sign or verify. It only accepts bytestrings
input_.fulfillment.sign(sha3_message.digest(), base58.b58decode(key_pairs[public_key].encode()))
except KeyError:
raise KeypairMismatchException(
"Public key {} is not a pair to " "any of the private keys".format(public_key)
)
return input_
@classmethod
def _sign_threshold_signature_fulfillment(cls, input_: Input, message: str, key_pairs: dict) -> Input:
"""Signs a ThresholdSha256.
Args:
input_ (:class:`~planetmint.transactions.common.transaction.
Input`) The Input to be signed.
message (str): The message to be signed
key_pairs (dict): The keys to sign the Transaction with.
"""
input_ = deepcopy(input_)
sha3_message = sha3_256(message.encode())
if input_.fulfills:
sha3_message.update("{}{}".format(input_.fulfills.txid, input_.fulfills.output).encode())
for owner_before in set(input_.owners_before):
# TODO: CC should throw a KeypairMismatchException, instead of
# our manual mapping here
# TODO FOR CC: Naming wise this is not so smart,
# `get_subcondition` in fact doesn't return a
# condition but a fulfillment
# TODO FOR CC: `get_subcondition` is singular. One would not
# expect to get a list back.
ccffill = input_.fulfillment
subffills = ccffill.get_subcondition_from_vk(base58.b58decode(owner_before))
if not subffills:
raise KeypairMismatchException(
"Public key {} cannot be found " "in the fulfillment".format(owner_before)
)
try:
private_key = key_pairs[owner_before]
except KeyError:
raise KeypairMismatchException(
"Public key {} is not a pair " "to any of the private keys".format(owner_before)
)
# cryptoconditions makes no assumptions of the encoding of the
# message to sign or verify. It only accepts bytestrings
for subffill in subffills:
subffill.sign(sha3_message.digest(), base58.b58decode(private_key.encode()))
return input_
def inputs_valid(self, outputs=None) -> bool:
"""Validates the Inputs in the Transaction against given
Outputs.
Note:
Given a `CREATE` Transaction is passed,
dummy values for Outputs are submitted for validation that
evaluate parts of the validation-checks to `True`.
Args:
outputs (:obj:`list` of :class:`~planetmint.transactions.common.
transaction.Output`): A list of Outputs to check the
Inputs against.
Returns:
bool: If all Inputs are valid.
"""
if self.operation == self.CREATE:
# NOTE: Since in the case of a `CREATE`-transaction we do not have
# to check for outputs, we're just submitting dummy
# values to the actual method. This simplifies it's logic
# greatly, as we do not have to check against `None` values.
return self._inputs_valid(["dummyvalue" for _ in self.inputs])
elif self.operation == self.TRANSFER:
return self._inputs_valid([output.fulfillment.condition_uri for output in outputs])
elif self.operation == self.VALIDATOR_ELECTION:
return self._inputs_valid(["dummyvalue" for _ in self.inputs])
elif self.operation == self.CHAIN_MIGRATION_ELECTION:
return self._inputs_valid(["dummyvalue" for _ in self.inputs])
else:
allowed_ops = ", ".join(self.__class__.ALLOWED_OPERATIONS)
raise TypeError("`operation` must be one of {}".format(allowed_ops))
def _inputs_valid(self, output_condition_uris: list[str]) -> bool:
"""Validates an Input against a given set of Outputs.
Note:
The number of `output_condition_uris` must be equal to the
number of Inputs a Transaction has.
Args:
output_condition_uris (:obj:`list` of :obj:`str`): A list of
Outputs to check the Inputs against.
Returns:
bool: If all Outputs are valid.
"""
if len(self.inputs) != len(output_condition_uris):
raise ValueError("Inputs and " "output_condition_uris must have the same count")
tx_dict = self.tx_dict if self.tx_dict else self.to_dict()
tx_dict = Transaction._remove_signatures(tx_dict)
tx_dict["id"] = None
tx_serialized = Transaction._to_str(tx_dict)
def validate(i, output_condition_uri=None):
"""Validate input against output condition URI"""
return self._input_valid(self.inputs[i], self.operation, tx_serialized, output_condition_uri)
return all(validate(i, cond) for i, cond in enumerate(output_condition_uris))
@lru_cache(maxsize=16384)
def _input_valid(self, input_: Input, operation: str, message: str, output_condition_uri: Optional[str] = None) -> bool:
"""Validates a single Input against a single Output.
Note:
In case of a `CREATE` Transaction, this method
does not validate against `output_condition_uri`.
Args:
input_ (:class:`~planetmint.transactions.common.transaction.
Input`) The Input to be signed.
operation (str): The type of Transaction.
message (str): The fulfillment message.
output_condition_uri (str, optional): An Output to check the
Input against.
Returns:
bool: If the Input is valid.
"""
ccffill = input_.fulfillment
try:
parsed_ffill = Fulfillment.from_uri(ccffill.serialize_uri())
except TypeError as e:
print(f"Exception TypeError : {e}")
return False
except ValueError as e:
print(f"Exception ValueError : {e}")
return False
except ParsingError as e:
print(f"Exception ParsingError : {e}")
return False
except ASN1DecodeError as e:
print(f"Exception ASN1DecodeError : {e}")
return False
except ASN1EncodeError as e:
print(f"Exception ASN1EncodeError : {e}")
return False
if operation in [self.CREATE, self.CHAIN_MIGRATION_ELECTION, self.VALIDATOR_ELECTION]:
# NOTE: In the case of a `CREATE` transaction, the
# output is always valid.
output_valid = True
else:
output_valid = output_condition_uri == ccffill.condition_uri
ffill_valid = False
if isinstance(parsed_ffill, ZenroomSha256):
import json
msg = json.loads(message)
ffill_valid = parsed_ffill.validate(message=json.dumps(msg["script"]))
else:
sha3_message = sha3_256(message.encode())
if input_.fulfills:
sha3_message.update("{}{}".format(input_.fulfills.txid, input_.fulfills.output).encode())
# NOTE: We pass a timestamp to `.validate`, as in case of a timeout
# condition we'll have to validate against it
# cryptoconditions makes no assumptions of the encoding of the
# message to sign or verify. It only accepts bytestrings
ffill_valid = parsed_ffill.validate(message=sha3_message.digest())
return output_valid and ffill_valid
# This function is required by `lru_cache` to create a key for memoization
def __hash__(self):
return hash(self.id)
@memoize_to_dict
def to_dict(self) -> dict:
"""Transforms the object to a Python dictionary.
Returns:
dict: The Transaction as an alternative serialization format.
"""
tx_dict = {
"inputs": [input_.to_dict() for input_ in self.inputs],
"outputs": [output.to_dict() for output in self.outputs],
"operation": str(self.operation),
"metadata": self.metadata,
"asset": self.asset,
"version": self.version,
"id": self._id,
}
if self.script:
tx_dict["script"] = self.script
return tx_dict
@staticmethod
# TODO: Remove `_dict` prefix of variable.
def _remove_signatures(tx_dict: dict) -> dict:
"""Takes a Transaction dictionary and removes all signatures.
Args:
tx_dict (dict): The Transaction to remove all signatures from.
Returns:
dict
"""
# NOTE: We remove the reference since we need `tx_dict` only for the
# transaction's hash
tx_dict = deepcopy(tx_dict)
for input_ in tx_dict["inputs"]:
# NOTE: Not all Cryptoconditions return a `signature` key (e.g.
# ThresholdSha256), so setting it to `None` in any
# case could yield incorrect signatures. This is why we only
# set it to `None` if it's set in the dict.
input_["fulfillment"] = None
return tx_dict
@staticmethod
def _to_hash(value):
return hash_data(value)
@property
def id(self):
return self._id
def to_hash(self):
return self.to_dict()["id"]
@staticmethod
def _to_str(value):
return serialize(value)
# TODO: This method shouldn't call `_remove_signatures`
def __str__(self):
_tx = self.to_dict()
tx = Transaction._remove_signatures(_tx)
return Transaction._to_str(tx)
@classmethod
def get_asset_id(cls, transactions):
"""Get the asset id from a list of :class:`~.Transactions`.
This is useful when we want to check if the multiple inputs of a
transaction are related to the same asset id.
Args:
transactions (:obj:`list` of :class:`~planetmint.transactions.common.
transaction.Transaction`): A list of Transactions.
Usually input Transactions that should have a matching
asset ID.
Returns:
str: ID of the asset.
Raises:
:exc:`AssetIdMismatch`: If the inputs are related to different
assets.
"""
if not isinstance(transactions, list):
transactions = [transactions]
# create a set of the transactions' asset ids
asset_ids = {
tx.id if tx.operation in [tx.CREATE, tx.VALIDATOR_ELECTION] else tx.asset["id"] for tx in transactions
}
# check that all the transasctions have the same asset id
if len(asset_ids) > 1:
raise AssetIdMismatch(("All inputs of all transactions passed" " need to have the same asset id"))
return asset_ids.pop()
@staticmethod
def validate_id(tx_body: dict):
"""Validate the transaction ID of a transaction
Args:
tx_body (dict): The Transaction to be transformed.
"""
# NOTE: Remove reference to avoid side effects
tx_body = deepcopy(tx_body)
tx_body = rapidjson.loads(rapidjson.dumps(tx_body))
try:
proposed_tx_id = tx_body["id"]
except KeyError:
raise InvalidHash("No transaction id found!")
tx_body["id"] = None
tx_body_serialized = Transaction._to_str(tx_body)
valid_tx_id = Transaction._to_hash(tx_body_serialized)
if proposed_tx_id != valid_tx_id:
err_msg = "The transaction's id '{}' isn't equal to " "the hash of its body, i.e. it's not valid."
raise InvalidHash(err_msg.format(proposed_tx_id))
@classmethod
@memoize_from_dict
def from_dict(cls, tx: dict, skip_schema_validation=True):
"""Transforms a Python dictionary to a Transaction object.
Args:
tx_body (dict): The Transaction to be transformed.
Returns:
:class:`~planetmint.transactions.common.transaction.Transaction`
"""
operation = tx.get("operation", Transaction.CREATE) if isinstance(tx, dict) else Transaction.CREATE
cls = Transaction.resolve_class(operation)
id = None
try:
id = tx["id"]
except KeyError:
id = None
# tx['asset'] = tx['asset'][0] if isinstance( tx['asset'], list) or isinstance( tx['asset'], tuple) else tx['asset'], # noqa: E501
local_dict = {
"inputs": tx["inputs"],
"outputs": tx["outputs"],
"operation": operation,
"metadata": tx["metadata"],
"asset": tx[
"asset"
], # [0] if isinstance( tx['asset'], list) or isinstance( tx['asset'], tuple) else tx['asset'], # noqa: E501
"version": tx["version"],
"id": id,
}
try:
script_ = tx["script"]
script_dict = {"script": script_}
except KeyError:
script_ = None
pass
else:
local_dict = {**local_dict, **script_dict}
if not skip_schema_validation:
cls.validate_id(local_dict)
cls.validate_schema(local_dict)
inputs = [Input.from_dict(input_) for input_ in tx["inputs"]]
outputs = [Output.from_dict(output) for output in tx["outputs"]]
return cls(
tx["operation"],
tx["asset"],
inputs,
outputs,
tx["metadata"],
tx["version"],
hash_id=tx["id"],
tx_dict=tx,
script=script_,
)
type_registry: dict[type, type] = {}
@staticmethod
def register_type(tx_type, tx_class):
Transaction.type_registry[tx_type] = tx_class
def resolve_class(operation):
"""For the given `tx` based on the `operation` key return its implementation class"""
create_txn_class = Transaction.type_registry.get(Transaction.CREATE)
return Transaction.type_registry.get(operation, create_txn_class)
@classmethod
def validate_schema(cls, tx):
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)
@classmethod
def complete_tx_i_o(self, tx_signers, recipients):
inputs = []
outputs = []
# generate_outputs
for recipient in recipients:
if not isinstance(recipient, tuple) or len(recipient) != 2:
raise ValueError(
("Each `recipient` in the list must be a" " tuple of `([<list of public keys>]," " <amount>)`")
)
pub_keys, amount = recipient
outputs.append(Output.generate(pub_keys, amount))
# generate inputs
inputs.append(Input.generate(tx_signers))
return (inputs, outputs)

View File

@ -1,79 +0,0 @@
# 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
from typing import Optional, Union
class TransactionLink(object):
"""An object for unidirectional linking to a Transaction's Output.
Attributes:
txid (str, optional): A Transaction to link to.
output (int, optional): An output's index in a Transaction with id
`txid`.
"""
def __init__(self, txid: Optional[str] = None, output: Optional[int] = None):
"""Create an instance of a :class:`~.TransactionLink`.
Note:
In an IPLD implementation, this class is not necessary anymore,
as an IPLD link can simply point to an object, as well as an
objects properties. So instead of having a (de)serializable
class, we can have a simple IPLD link of the form:
`/<tx_id>/transaction/outputs/<output>/`.
Args:
txid (str, optional): A Transaction to link to.
output (int, optional): An Outputs's index in a Transaction with
id `txid`.
"""
self.txid = txid
self.output = output
def __bool__(self):
return self.txid is not None and self.output is not None
def __eq__(self, other):
# TODO: If `other !== TransactionLink` return `False`
return self.to_dict() == other.to_dict()
def __hash__(self):
return hash((self.txid, self.output))
@classmethod
def from_dict(cls, link: dict):
"""Transforms a Python dictionary to a TransactionLink object.
Args:
link (dict): The link to be transformed.
Returns:
:class:`~planetmint.transactions.common.transaction.TransactionLink`
"""
try:
return cls(link["transaction_id"], link["output_index"])
except TypeError:
return cls()
def to_dict(self) -> Union[dict,None]:
"""Transforms the object to a Python dictionary.
Returns:
(dict|None): The link as an alternative serialization format.
"""
if self.txid is None and self.output is None:
return None
else:
return {
"transaction_id": self.txid,
"output_index": self.output,
}
def to_uri(self, path: str = "") -> Union[str,None]:
if self.txid is None and self.output is None:
return None
return "{}/transactions/{}/outputs/{}".format(path, self.txid, self.output)

View File

@ -1,8 +0,0 @@
# 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
BROADCAST_TX_COMMIT = "broadcast_tx_commit"
BROADCAST_TX_ASYNC = "broadcast_tx_async"
BROADCAST_TX_SYNC = "broadcast_tx_sync"

View File

@ -1,232 +0,0 @@
# 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 base58
import time
import re
import rapidjson
from typing import Callable
from planetmint.config import Config
from planetmint.transactions.common.exceptions import ValidationError
from cryptoconditions import ThresholdSha256, Ed25519Sha256, ZenroomSha256, Fulfillment
from planetmint.transactions.common.exceptions import ThresholdTooDeep
from cryptoconditions.exceptions import UnsupportedTypeError
def gen_timestamp() -> str:
"""The Unix time, rounded to the nearest second.
See https://en.wikipedia.org/wiki/Unix_time
Returns:
str: the Unix time
"""
return str(round(time.time()))
def serialize(data: dict) -> str:
"""Serialize a dict into a JSON formatted string.
This function enforces rules like the separator and order of keys.
This ensures that all dicts are serialized in the same way.
This is specially important for hashing data. We need to make sure that
everyone serializes their data in the same way so that we do not have
hash mismatches for the same structure due to serialization
differences.
Args:
data (dict): dict to serialize
Returns:
str: JSON formatted string
"""
return rapidjson.dumps(data, skipkeys=False, ensure_ascii=False, sort_keys=True)
def deserialize(data: str) -> dict:
"""Deserialize a JSON formatted string into a dict.
Args:
data (str): JSON formatted string.
Returns:
dict: dict resulting from the serialization of a JSON formatted
string.
"""
return rapidjson.loads(data)
def validate_txn_obj(obj_name: str, obj: dict, key: str, validation_fun: Callable) -> None:
"""Validate value of `key` in `obj` using `validation_fun`.
Args:
obj_name (str): name for `obj` being validated.
obj (dict): dictionary object.
key (str): key to be validated in `obj`.
validation_fun (function): function used to validate the value
of `key`.
Returns:
None: indicates validation successful
Raises:
ValidationError: `validation_fun` will raise exception on failure
"""
backend = Config().get()["database"]["backend"]
if backend == "localmongodb":
data = obj.get(key, {})
if isinstance(data, dict):
validate_all_keys_in_obj(obj_name, data, validation_fun)
elif isinstance(data, list):
validate_all_items_in_list(obj_name, data, validation_fun)
def validate_all_items_in_list(obj_name: str, data: list, validation_fun: Callable) -> None:
for item in data:
if isinstance(item, dict):
validate_all_keys_in_obj(obj_name, item, validation_fun)
elif isinstance(item, list):
validate_all_items_in_list(obj_name, item, validation_fun)
def validate_all_keys_in_obj(obj_name: str, obj: dict, validation_fun: Callable) -> None:
"""Validate all (nested) keys in `obj` by using `validation_fun`.
Args:
obj_name (str): name for `obj` being validated.
obj (dict): dictionary object.
validation_fun (function): function used to validate the value
of `key`.
Returns:
None: indicates validation successful
Raises:
ValidationError: `validation_fun` will raise this error on failure
"""
for key, value in obj.items():
validation_fun(obj_name, key)
if isinstance(value, dict):
validate_all_keys_in_obj(obj_name, value, validation_fun)
elif isinstance(value, list):
validate_all_items_in_list(obj_name, value, validation_fun)
def validate_all_values_for_key_in_obj(obj: dict, key: str, validation_fun: Callable) -> None:
"""Validate value for all (nested) occurrence of `key` in `obj`
using `validation_fun`.
Args:
obj (dict): dictionary object.
key (str): key whose value is to be validated.
validation_fun (function): function used to validate the value
of `key`.
Raises:
ValidationError: `validation_fun` will raise this error on failure
"""
for vkey, value in obj.items():
if vkey == key:
validation_fun(value)
elif isinstance(value, dict):
validate_all_values_for_key_in_obj(value, key, validation_fun)
elif isinstance(value, list):
validate_all_values_for_key_in_list(value, key, validation_fun)
def validate_all_values_for_key_in_list(input_list: list, key: str, validation_fun: Callable) -> None:
for item in input_list:
if isinstance(item, dict):
validate_all_values_for_key_in_obj(item, key, validation_fun)
elif isinstance(item, list):
validate_all_values_for_key_in_list(item, key, validation_fun)
def validate_key(obj_name: str, key: str) -> None:
"""Check if `key` contains ".", "$" or null characters.
https://docs.mongodb.com/manual/reference/limits/#Restrictions-on-Field-Names
Args:
obj_name (str): object name to use when raising exception
key (str): key to validated
Returns:
None: validation successful
Raises:
ValidationError: will raise exception in case of regex match.
"""
if re.search(r"^[$]|\.|\x00", key):
error_str = (
'Invalid key name "{}" in {} object. The '
"key name cannot contain characters "
'".", "$" or null characters'
).format(key, obj_name)
raise ValidationError(error_str)
def _fulfillment_to_details(fulfillment: type[Fulfillment]) -> dict:
"""Encode a fulfillment as a details dictionary
Args:
fulfillment: Crypto-conditions Fulfillment object
"""
if fulfillment.type_name == "ed25519-sha-256":
return {
"type": "ed25519-sha-256",
"public_key": base58.b58encode(fulfillment.public_key).decode(),
}
if fulfillment.type_name == "threshold-sha-256":
subconditions = [_fulfillment_to_details(cond["body"]) for cond in fulfillment.subconditions]
return {
"type": "threshold-sha-256",
"threshold": fulfillment.threshold,
"subconditions": subconditions,
}
if fulfillment.type_name == "zenroom-sha-256":
return {
"type": "zenroom-sha-256",
"public_key": base58.b58encode(fulfillment.public_key).decode(),
"script": base58.b58encode(fulfillment.script).decode(),
"data": base58.b58encode(fulfillment.data).decode(),
}
raise UnsupportedTypeError(fulfillment.type_name)
def _fulfillment_from_details(data: dict, _depth: int = 0):
"""Load a fulfillment for a signing spec dictionary
Args:
data: tx.output[].condition.details dictionary
"""
if _depth == 100:
raise ThresholdTooDeep()
if data["type"] == "ed25519-sha-256":
public_key = base58.b58decode(data["public_key"])
return Ed25519Sha256(public_key=public_key)
if data["type"] == "threshold-sha-256":
threshold = ThresholdSha256(data["threshold"])
for cond in data["subconditions"]:
cond = _fulfillment_from_details(cond, _depth + 1)
threshold.add_subfulfillment(cond)
return threshold
if data["type"] == "zenroom-sha-256":
public_key_ = base58.b58decode(data["public_key"])
script_ = base58.b58decode(data["script"])
data_ = base58.b58decode(data["data"])
# TODO: assign to zenroom and evaluate the outcome
ZenroomSha256(script=script_, data=data_, keys={public_key_})
raise UnsupportedTypeError(data.get("type"))

View File

@ -1,68 +0,0 @@
# 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
from typing import Optional
from cid import is_cid
from planetmint.transactions.common.transaction import Transaction
class Create(Transaction):
OPERATION = "CREATE"
ALLOWED_OPERATIONS = (OPERATION,)
@classmethod
def validate_create(self, tx_signers: list[str], recipients: list[tuple[list[str],int]], asset: Optional[dict], metadata: Optional[dict]):
if not isinstance(tx_signers, list):
raise TypeError("`tx_signers` must be a list instance")
if not isinstance(recipients, list):
raise TypeError("`recipients` must be a list instance")
if len(tx_signers) == 0:
raise ValueError("`tx_signers` list cannot be empty")
if len(recipients) == 0:
raise ValueError("`recipients` list cannot be empty")
if not asset is None:
if not isinstance(asset, dict):
raise TypeError("`asset` must be a CID string or None")
if "data" in asset and not is_cid(asset["data"]):
raise TypeError("`asset` must be a CID string or None")
if not (metadata is None or is_cid(metadata)):
raise TypeError("`metadata` must be a CID string or None")
return True
@classmethod
def generate(cls, tx_signers: list[str], recipients: list[tuple[list[str],int]], metadata: Optional[dict] = None, asset: Optional[dict] = None):
"""A simple way to generate a `CREATE` transaction.
Note:
This method currently supports the following Cryptoconditions
use cases:
- Ed25519
- ThresholdSha256
Additionally, it provides support for the following Planetmint
use cases:
- Multiple inputs and outputs.
Args:
tx_signers (:obj:`list` of :obj:`str`): A list of keys that
represent the signers of the CREATE Transaction.
recipients (:obj:`list` of :obj:`tuple`): A list of
([keys],amount) that represent the recipients of this
Transaction.
metadata (dict): The metadata to be stored along with the
Transaction.
asset (dict): The metadata associated with the asset that will
be created in this Transaction.
Returns:
:class:`~planetmint.common.transaction.Transaction`
"""
Create.validate_create(tx_signers, recipients, asset, metadata)
(inputs, outputs) = Transaction.complete_tx_i_o(tx_signers, recipients)
return cls(cls.OPERATION, asset, inputs, outputs, metadata)

View File

@ -1,84 +0,0 @@
# 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
from typing import Optional
from planetmint.transactions.common.transaction import Transaction
from planetmint.transactions.common.input import Input
from planetmint.transactions.common.output import Output
from copy import deepcopy
class Transfer(Transaction):
OPERATION = "TRANSFER"
ALLOWED_OPERATIONS = (OPERATION,)
@classmethod
def validate_transfer(cls, inputs: list[Input], recipients: list[tuple[list[str],int]], asset_id: str, metadata: Optional[dict]):
if not isinstance(inputs, list):
raise TypeError("`inputs` must be a list instance")
if len(inputs) == 0:
raise ValueError("`inputs` must contain at least one item")
if not isinstance(recipients, list):
raise TypeError("`recipients` must be a list instance")
if len(recipients) == 0:
raise ValueError("`recipients` list cannot be empty")
outputs = []
for recipient in recipients:
if not isinstance(recipient, tuple) or len(recipient) != 2:
raise ValueError(
("Each `recipient` in the list must be a" " tuple of `([<list of public keys>]," " <amount>)`")
)
pub_keys, amount = recipient
outputs.append(Output.generate(pub_keys, amount))
if not isinstance(asset_id, str):
raise TypeError("`asset_id` must be a string")
return (deepcopy(inputs), outputs)
@classmethod
def generate(cls, inputs: list[Input], recipients: list[tuple[list[str],int]], asset_id: str, metadata: Optional[dict] = None):
"""A simple way to generate a `TRANSFER` transaction.
Note:
Different cases for threshold conditions:
Combining multiple `inputs` with an arbitrary number of
`recipients` can yield interesting cases for the creation of
threshold conditions we'd like to support. The following
notation is proposed:
1. The index of a `recipient` corresponds to the index of
an input:
e.g. `transfer([input1], [a])`, means `input1` would now be
owned by user `a`.
2. `recipients` can (almost) get arbitrary deeply nested,
creating various complex threshold conditions:
e.g. `transfer([inp1, inp2], [[a, [b, c]], d])`, means
`a`'s signature would have a 50% weight on `inp1`
compared to `b` and `c` that share 25% of the leftover
weight respectively. `inp2` is owned completely by `d`.
Args:
inputs (:obj:`list` of :class:`~planetmint.common.transaction.
Input`): Converted `Output`s, intended to
be used as inputs in the transfer to generate.
recipients (:obj:`list` of :obj:`tuple`): A list of
([keys],amount) that represent the recipients of this
Transaction.
asset_id (str): The asset ID of the asset to be transferred in
this Transaction.
metadata (dict): Python dictionary to be stored along with the
Transaction.
Returns:
:class:`~planetmint.common.transaction.Transaction`
"""
(inputs, outputs) = cls.validate_transfer(inputs, recipients, asset_id, metadata)
return cls(cls.OPERATION, {"id": asset_id}, inputs, outputs, metadata)

View File

@ -1,15 +0,0 @@
# 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
from planetmint.transactions.common.schema import TX_SCHEMA_CHAIN_MIGRATION_ELECTION
from planetmint.transactions.common.transaction import CHAIN_MIGRATION_ELECTION
from planetmint.transactions.types.elections.election import Election
class ChainMigrationElection(Election):
OPERATION = CHAIN_MIGRATION_ELECTION
ALLOWED_OPERATIONS = (OPERATION,)
TX_SCHEMA_CUSTOM = TX_SCHEMA_CHAIN_MIGRATION_ELECTION

View File

@ -1,68 +0,0 @@
# 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
from uuid import uuid4
from typing import Optional
from planetmint.transactions.common.transaction import Transaction
from planetmint.transactions.common.schema import _validate_schema, TX_SCHEMA_COMMON
class Election(Transaction):
"""Represents election transactions.
To implement a custom election, create a class deriving from this one
with OPERATION set to the election operation, ALLOWED_OPERATIONS
set to (OPERATION,), CREATE set to OPERATION.
"""
OPERATION: Optional[str] = None
# Custom validation schema
TX_SCHEMA_CUSTOM = None
# Election Statuses:
ONGOING: str = "ongoing"
CONCLUDED: str = "concluded"
INCONCLUSIVE: str = "inconclusive"
# Vote ratio to approve an election
ELECTION_THRESHOLD = 2 / 3
@classmethod
def validate_election(self, tx_signers, recipients, asset, metadata):
if not isinstance(tx_signers, list):
raise TypeError("`tx_signers` must be a list instance")
if not isinstance(recipients, list):
raise TypeError("`recipients` must be a list instance")
if len(tx_signers) == 0:
raise ValueError("`tx_signers` list cannot be empty")
if len(recipients) == 0:
raise ValueError("`recipients` list cannot be empty")
if not asset is None:
if not isinstance(asset, dict):
raise TypeError("`asset` must be a CID string or None")
if not (metadata is None or isinstance(metadata, str)):
# add check if metadata is ipld marshalled CID string
raise TypeError("`metadata` must be a CID string or None")
return True
@classmethod
def generate(cls, initiator, voters, election_data, metadata=None):
# Break symmetry in case we need to call an election with the same properties twice
uuid = uuid4()
election_data["seed"] = str(uuid)
Election.validate_election(initiator, voters, election_data, metadata)
(inputs, outputs) = Transaction.complete_tx_i_o(initiator, voters)
election = cls(cls.OPERATION, {"data": election_data}, inputs, outputs, metadata)
cls.validate_schema(election.to_dict())
return election
@classmethod
def validate_schema(cls, tx):
"""Validate the election transaction. Since `ELECTION` extends `CREATE` transaction, all the validations for
`CREATE` transaction should be inherited
"""
_validate_schema(TX_SCHEMA_COMMON, tx)
if cls.TX_SCHEMA_CUSTOM:
_validate_schema(cls.TX_SCHEMA_CUSTOM, tx)

View File

@ -1,22 +0,0 @@
# 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
from planetmint.transactions.types.elections.election import Election
from planetmint.transactions.common.schema import TX_SCHEMA_VALIDATOR_ELECTION
from planetmint.transactions.common.transaction import VALIDATOR_ELECTION
from .validator_utils import validate_asset_public_key
class ValidatorElection(Election):
OPERATION = VALIDATOR_ELECTION
ALLOWED_OPERATIONS = (OPERATION,)
TX_SCHEMA_CUSTOM = TX_SCHEMA_VALIDATOR_ELECTION
@classmethod
def validate_schema(cls, tx):
super(ValidatorElection, cls).validate_schema(tx)
validate_asset_public_key(tx["asset"]["data"]["public_key"])

View File

@ -1,51 +0,0 @@
# 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 base58
import base64
import binascii
from planetmint.transactions.common.exceptions import InvalidPublicKey
def encode_pk_to_base16(validator):
pk = validator["public_key"]
decoder = get_public_key_decoder(pk)
public_key16 = base64.b16encode(decoder(pk["value"])).decode("utf-8")
validator["public_key"]["value"] = public_key16
return validator
def validate_asset_public_key(pk):
pk_binary = pk["value"].encode("utf-8")
decoder = get_public_key_decoder(pk)
try:
pk_decoded = decoder(pk_binary)
if len(pk_decoded) != 32:
raise InvalidPublicKey("Public key should be of size 32 bytes")
except binascii.Error:
raise InvalidPublicKey("Invalid `type` specified for public key `value`")
def get_public_key_decoder(pk):
encoding = pk["type"]
decoder = base64.b64decode
if encoding == "ed25519-base16":
decoder = base64.b16decode
elif encoding == "ed25519-base32":
decoder = base64.b32decode
elif encoding == "ed25519-base64":
decoder = base64.b64decode
else:
raise InvalidPublicKey("Invalid `type` specified for public key `value`")
return decoder
def election_id_to_public_key(election_id):
return base58.b58encode(bytes.fromhex(election_id)).decode()

View File

@ -1,39 +0,0 @@
# 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
from planetmint.transactions.types.assets.transfer import Transfer
from planetmint.transactions.common.transaction import VOTE
from planetmint.transactions.common.schema import (
_validate_schema,
TX_SCHEMA_COMMON,
TX_SCHEMA_TRANSFER,
TX_SCHEMA_VOTE,
)
class Vote(Transfer):
OPERATION = VOTE
# NOTE: This class inherits TRANSFER txn type. The `TRANSFER` property is
# overriden to re-use methods from parent class
TRANSFER = OPERATION
ALLOWED_OPERATIONS = (OPERATION,)
# Custom validation schema
TX_SCHEMA_CUSTOM = TX_SCHEMA_VOTE
@classmethod
def generate(cls, inputs, recipients, election_id, metadata=None):
(inputs, outputs) = cls.validate_transfer(inputs, recipients, election_id, metadata)
election_vote = cls(cls.OPERATION, {"id": election_id}, inputs, outputs, metadata)
cls.validate_schema(election_vote.to_dict())
return election_vote
@classmethod
def validate_schema(cls, tx):
"""Validate the validator election vote transaction. Since `VOTE` extends `TRANSFER`
transaction, all the validations for `CREATE` transaction should be inherited
"""
_validate_schema(TX_SCHEMA_COMMON, tx)
_validate_schema(TX_SCHEMA_TRANSFER, tx)
_validate_schema(cls.TX_SCHEMA_CUSTOM, tx)

View File

@ -13,7 +13,7 @@ import setproctitle
from packaging import version
from planetmint.version import __tm_supported_versions__
from planetmint.tendermint_utils import key_from_base64
from planetmint.transactions.common.crypto import key_pair_from_ed25519_key
from transactions.common.crypto import key_pair_from_ed25519_key
class ProcessGroup(object):

View File

@ -5,7 +5,7 @@
import re
from planetmint.transactions.common.transaction_mode_types import (
from transactions.common.transaction_mode_types import (
BROADCAST_TX_COMMIT,
BROADCAST_TX_ASYNC,
BROADCAST_TX_SYNC,

View File

@ -12,14 +12,14 @@ import logging
from flask import current_app, request, jsonify
from flask_restful import Resource, reqparse
from planetmint.transactions.common.transaction_mode_types import BROADCAST_TX_ASYNC
from planetmint.transactions.common.exceptions import (
from transactions.common.transaction_mode_types import BROADCAST_TX_ASYNC
from transactions.common.exceptions import (
SchemaValidationError,
ValidationError,
)
from planetmint.web.views.base import make_error
from planetmint.web.views import parameters
from planetmint.transactions.common.transaction import Transaction
from transactions.common.transaction import Transaction
logger = logging.getLogger(__name__)

View File

@ -116,14 +116,14 @@ install_requires = [
"flask-restful==0.3.9",
"flask==2.1.2",
"gunicorn==20.1.0",
"jsonschema==3.2.0",
"jsonschema==4.16.0",
"logstats==0.3.0",
"packaging>=20.9",
# TODO Consider not installing the db drivers, or putting them in extras.
"pymongo==3.11.4",
"tarantool==0.7.1",
"python-rapidjson>=1.0",
"pyyaml==5.4.1",
"pyyaml==6.0.0",
"requests==2.25.1",
"setproctitle==1.2.2",
"werkzeug==2.0.3",
@ -136,6 +136,7 @@ install_requires = [
"PyNaCl==1.4.0",
"pyasn1>=0.4.8",
"cryptography==3.4.7",
"planetmint-transactions==0.1.0",
]
setup(
@ -176,7 +177,7 @@ setup(
"docs": docs_require,
},
package_data={
"planetmint.transactions.common.schema": [
"transactions.common.schema": [
"v1.0/*.yaml",
"v2.0/*.yaml",
"v3.0/*.yaml",

View File

@ -4,8 +4,8 @@
# Code is Apache-2.0 and docs are CC-BY-4.0
import pytest
from planetmint.transactions.types.assets.create import Create
from planetmint.transactions.types.assets.transfer import Transfer
from transactions.types.assets.create import Create
from transactions.types.assets.transfer import Transfer
def test_asset_transfer(b, signed_create_tx, user_pk, user_sk):
@ -19,7 +19,7 @@ def test_asset_transfer(b, signed_create_tx, user_pk, user_sk):
def test_validate_transfer_asset_id_mismatch(b, signed_create_tx, user_pk, user_sk):
from planetmint.transactions.common.exceptions import AssetIdMismatch
from transactions.common.exceptions import AssetIdMismatch
tx_transfer = Transfer.generate(signed_create_tx.to_inputs(), [([user_pk], 1)], signed_create_tx.id)
tx_transfer.asset["id"] = "a" * 64
@ -32,14 +32,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.transactions.common.transaction import Transaction
from 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.transactions.common.transaction import Transaction
from 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)
@ -47,8 +47,8 @@ def test_get_asset_id_transfer_transaction(b, signed_create_tx, user_pk):
def test_asset_id_mismatch(alice, user_pk):
from planetmint.transactions.common.transaction import Transaction
from planetmint.transactions.common.exceptions import AssetIdMismatch
from transactions.common.transaction import Transaction
from transactions.common.exceptions import AssetIdMismatch
tx1 = Create.generate(
[alice.public_key], [([user_pk], 1)], metadata="QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4"

View File

@ -7,9 +7,9 @@
import pytest
import random
from planetmint.transactions.types.assets.create import Create
from planetmint.transactions.types.assets.transfer import Transfer
from planetmint.transactions.common.exceptions import DoubleSpend
from transactions.types.assets.create import Create
from transactions.types.assets.transfer import Transfer
from transactions.common.exceptions import DoubleSpend
# CREATE divisible asset
@ -107,7 +107,7 @@ def test_single_in_single_own_multiple_out_mix_own_create(alice, user_pk, b):
# Multiple owners_before
# Output combinations already tested above
def test_single_in_multiple_own_single_out_single_own_create(alice, b, user_pk, user_sk):
from planetmint.transactions.common.utils import _fulfillment_to_details
from transactions.common.utils import _fulfillment_to_details
tx = Create.generate(
[alice.public_key, user_pk],
@ -259,7 +259,7 @@ def test_single_in_single_own_multiple_out_mix_own_transfer(alice, b, user_pk, u
# Single output
# Single owners_after
def test_single_in_multiple_own_single_out_single_own_transfer(alice, b, user_pk, user_sk):
from planetmint.transactions.common.utils import _fulfillment_to_details
from transactions.common.utils import _fulfillment_to_details
# CREATE divisible asset
tx_create = Create.generate(
@ -325,7 +325,7 @@ def test_multiple_in_single_own_single_out_single_own_transfer(alice, b, user_pk
# Single output
# Single owners_after
def test_multiple_in_multiple_own_single_out_single_own_transfer(alice, b, user_pk, user_sk):
from planetmint.transactions.common.utils import _fulfillment_to_details
from transactions.common.utils import _fulfillment_to_details
# CREATE divisible asset
tx_create = Create.generate(
@ -365,7 +365,7 @@ def test_multiple_in_multiple_own_single_out_single_own_transfer(alice, b, user_
# Single output
# Single owners_after
def test_muiltiple_in_mix_own_multiple_out_single_own_transfer(alice, b, user_pk, user_sk):
from planetmint.transactions.common.utils import _fulfillment_to_details
from transactions.common.utils import _fulfillment_to_details
# CREATE divisible asset
tx_create = Create.generate(
@ -404,7 +404,7 @@ def test_muiltiple_in_mix_own_multiple_out_single_own_transfer(alice, b, user_pk
# Mix: one output with a single owners_after, one output with multiple
# owners_after
def test_muiltiple_in_mix_own_multiple_out_mix_own_transfer(alice, b, user_pk, user_sk):
from planetmint.transactions.common.utils import _fulfillment_to_details
from transactions.common.utils import _fulfillment_to_details
# CREATE divisible asset
tx_create = Create.generate(
@ -492,7 +492,7 @@ def test_multiple_in_different_transactions(alice, b, user_pk, user_sk):
# inputs needs to match the amount being sent in the outputs.
# In other words `amount_in_inputs - amount_in_outputs == 0`
def test_amount_error_transfer(alice, b, user_pk, user_sk):
from planetmint.transactions.common.exceptions import AmountError
from transactions.common.exceptions import AmountError
# CREATE divisible asset
tx_create = Create.generate(

View File

@ -1,11 +1,9 @@
import pytest
import json
import base58
from hashlib import sha3_256
from zenroom import zencode_exec
from cryptoconditions.types.ed25519 import Ed25519Sha256
from cryptoconditions.types.zenroom import ZenroomSha256
from planetmint.transactions.common.crypto import generate_key_pair
from transactions.common.crypto import generate_key_pair
from ipld import multihash, marshal
CONDITION_SCRIPT = """Scenario 'ecdh': create the signature of an object
@ -150,9 +148,9 @@ def test_zenroom_signing():
shared_creation_txid = sha3_256(json_str_tx.encode()).hexdigest()
tx["id"] = shared_creation_txid
from planetmint.transactions.common.transaction import Transaction
from transactions.common.transaction import Transaction
from planetmint.lib import Planetmint
from planetmint.transactions.common.exceptions import (
from transactions.common.exceptions import (
SchemaValidationError,
ValidationError,
)

View File

@ -4,8 +4,8 @@
# # # Code is Apache-2.0 and docs are CC-BY-4.0
#
# from copy import deepcopy
# from planetmint.transactions.types.assets.create import Create
# from planetmint.transactions.types.assets.transfer import Transfer
# from transactions.types.assets.create import Create
# from transactions.types.assets.transfer import Transfer
#
# # import pytest
# # import pymongo
@ -238,7 +238,7 @@
# @pytest.mark.skip
# def test_get_spending_transactions_multiple_inputs():
# from planetmint.backend import connect, query
# from planetmint.transactions.common.crypto import generate_key_pair
# from transactions.common.crypto import generate_key_pair
# conn = connect()
# (alice_sk, alice_pk) = generate_key_pair()
# (bob_sk, bob_pk) = generate_key_pair()

View File

@ -7,9 +7,9 @@ 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
from transactions.common.transaction import Transaction
from transactions.types.assets.create import Create
from transactions.types.assets.transfer import Transfer
pytestmark = pytest.mark.bdb
@ -228,7 +228,7 @@ def test_get_spending_transactions(user_pk, user_sk, db_conn):
def test_get_spending_transactions_multiple_inputs(db_conn):
from planetmint.transactions.common.crypto import generate_key_pair
from transactions.common.crypto import generate_key_pair
from planetmint.backend.tarantool import query
(alice_sk, alice_pk) = generate_key_pair()

View File

@ -7,7 +7,7 @@ import pytest
def test_get_connection_raises_a_configuration_error(monkeypatch):
from planetmint.transactions.common.exceptions import ConfigurationError
from transactions.common.exceptions import ConfigurationError
from planetmint.backend.connection import connect
with pytest.raises(ConfigurationError):

View File

@ -39,7 +39,7 @@ def mock_processes_start(monkeypatch):
@pytest.fixture
def mock_generate_key_pair(monkeypatch):
monkeypatch.setattr("planetmint.transactions.common.crypto.generate_key_pair", lambda: ("privkey", "pubkey"))
monkeypatch.setattr("transactions.common.crypto.generate_key_pair", lambda: ("privkey", "pubkey"))
@pytest.fixture

View File

@ -14,9 +14,8 @@ import pytest
from planetmint.config import Config
from planetmint import ValidatorElection
from planetmint.commands.planetmint import run_election_show
from planetmint.transactions.types.elections.election import Election
from planetmint.lib import Block
from planetmint.transactions.types.elections.chain_migration_election import ChainMigrationElection
from transactions.types.elections.chain_migration_election import ChainMigrationElection
from tests.utils import generate_election, generate_validators
@ -137,7 +136,7 @@ def test_drop_db_when_interactive_yes(mock_db_drop, monkeypatch):
@patch("planetmint.backend.schema.drop_database")
def test_drop_db_when_db_does_not_exist(mock_db_drop, capsys):
from planetmint.transactions.common.exceptions import DatabaseDoesNotExist
from transactions.common.exceptions import DatabaseDoesNotExist
from planetmint.commands.planetmint import run_drop
args = Namespace(config=None, yes=True)
@ -263,7 +262,7 @@ def test_recover_db_on_start(mock_run_recover, mock_start, mocked_setup_logging)
@pytest.mark.bdb
def test_run_recover(b, alice, bob):
from planetmint.commands.planetmint import run_recover
from planetmint.transactions.types.assets.create import Create
from transactions.types.assets.create import Create
from planetmint.lib import Block
from planetmint.backend import query
@ -408,7 +407,7 @@ 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):
from planetmint.commands.planetmint import run_election_new_upsert_validator
from planetmint.transactions.common.exceptions import InvalidPowerChange
from transactions.common.exceptions import InvalidPowerChange
def mock_write(tx, mode):
b.store_bulk_transactions([tx])

View File

@ -89,35 +89,35 @@ def user2_Ed25519(user2_pub):
@pytest.fixture
def user_input(user_Ed25519, user_pub):
from planetmint.transactions.common.transaction import Input
from transactions.common.transaction import Input
return Input(user_Ed25519, [user_pub])
@pytest.fixture
def user_user2_threshold_output(user_user2_threshold, user_pub, user2_pub):
from planetmint.transactions.common.transaction import Output
from transactions.common.transaction import Output
return Output(user_user2_threshold, [user_pub, user2_pub])
@pytest.fixture
def user_user2_threshold_input(user_user2_threshold, user_pub, user2_pub):
from planetmint.transactions.common.transaction import Input
from transactions.common.transaction import Input
return Input(user_user2_threshold, [user_pub, user2_pub])
@pytest.fixture
def user_output(user_Ed25519, user_pub):
from planetmint.transactions.common.transaction import Output
from transactions.common.transaction import Output
return Output(user_Ed25519, [user_pub])
@pytest.fixture
def user2_output(user2_Ed25519, user2_pub):
from planetmint.transactions.common.transaction import Output
from transactions.common.transaction import Output
return Output(user2_Ed25519, [user2_pub])
@ -134,7 +134,7 @@ def data():
@pytest.fixture
def utx(user_input, user_output):
from planetmint.transactions.common.transaction import Transaction
from transactions.common.transaction import Transaction
return Transaction(Transaction.CREATE, {"data": None}, [user_input], [user_output])
@ -146,7 +146,7 @@ def tx(utx, user_priv):
@pytest.fixture
def transfer_utx(user_output, user2_output, utx):
from planetmint.transactions.common.transaction import Input, TransactionLink, Transaction
from transactions.common.transaction import Input, TransactionLink, Transaction
user_output = user_output.to_dict()
input = Input(utx.outputs[0].fulfillment, user_output["public_keys"], TransactionLink(utx.id, 0))

View File

@ -6,10 +6,10 @@
import pytest
from copy import deepcopy
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
from transactions.common.transaction import Transaction
from transactions.types.assets.create import Create
from transactions.common.crypto import generate_key_pair
from transactions.common.memoize import to_dict, from_dict
pytestmark = pytest.mark.bdb

View File

@ -13,8 +13,8 @@ from hypothesis import given
from hypothesis.strategies import from_regex as regex
from pytest import raises
from planetmint.transactions.common.exceptions import SchemaValidationError
from planetmint.transactions.common.schema import (
from transactions.common.exceptions import SchemaValidationError
from transactions.common.schema import (
TX_SCHEMA_COMMON,
validate_transaction_schema,
)

View File

@ -10,13 +10,13 @@ import json
from copy import deepcopy
from base58 import b58encode, b58decode
from planetmint.transactions.types.assets.create import Create
from planetmint.transactions.types.assets.transfer import Transfer
from planetmint.transactions.common.transaction import Output
from planetmint.transactions.common.transaction import Input
from planetmint.transactions.common.exceptions import AmountError
from planetmint.transactions.common.transaction import Transaction
from planetmint.transactions.common.transaction import TransactionLink
from transactions.types.assets.create import Create
from transactions.types.assets.transfer import Transfer
from transactions.common.transaction import Output
from transactions.common.transaction import Input
from transactions.common.exceptions import AmountError
from transactions.common.transaction import Transaction
from transactions.common.transaction import TransactionLink
from cryptoconditions import ThresholdSha256
from cryptoconditions import Fulfillment
from cryptoconditions import PreimageSha256
@ -52,7 +52,7 @@ def test_input_deserialization_with_uri(ffill_uri, user_pub):
@mark.skip(reason="None is tolerated because it is None before fulfilling.")
def test_input_deserialization_with_invalid_input(user_pub):
from planetmint.transactions.common.transaction import Input
from transactions.common.transaction import Input
ffill = {
"owners_before": [user_pub],
@ -64,8 +64,8 @@ def test_input_deserialization_with_invalid_input(user_pub):
def test_input_deserialization_with_invalid_fulfillment_uri(user_pub):
from planetmint.transactions.common.exceptions import InvalidSignature
from planetmint.transactions.common.transaction import Input
from transactions.common.exceptions import InvalidSignature
from transactions.common.transaction import Input
ffill = {
"owners_before": [user_pub],
@ -89,7 +89,7 @@ def test_input_deserialization_with_unsigned_fulfillment(ffill_uri, user_pub):
def test_output_serialization(user_Ed25519, user_pub):
from planetmint.transactions.common.transaction import Output
from transactions.common.transaction import Output
expected = {
"condition": {
@ -109,7 +109,7 @@ def test_output_serialization(user_Ed25519, user_pub):
def test_output_deserialization(user_Ed25519, user_pub):
from planetmint.transactions.common.transaction import Output
from transactions.common.transaction import Output
expected = Output(user_Ed25519, [user_pub], 1)
cond = {
@ -229,8 +229,8 @@ def test_generate_output_single_owner_with_output(user_pub):
def test_generate_output_invalid_parameters(user_pub, user2_pub, user3_pub):
from planetmint.transactions.common.transaction import Output
from planetmint.transactions.common.exceptions import AmountError
from transactions.common.transaction import Output
from transactions.common.exceptions import AmountError
with raises(ValueError):
Output.generate([], 1)
@ -298,7 +298,7 @@ def test_transaction_deserialization(tri_state_transaction):
def test_invalid_input_initialization(user_input, user_pub):
from planetmint.transactions.common.transaction import Input
from transactions.common.transaction import Input
with raises(TypeError):
Input(user_input, user_pub)
@ -435,7 +435,7 @@ def test_validate_tx_simple_create_signature(user_input, user_output, user_priv,
def test_invoke_simple_signature_fulfillment_with_invalid_params(utx, user_input):
from planetmint.transactions.common.exceptions import KeypairMismatchException
from transactions.common.exceptions import KeypairMismatchException
with raises(KeypairMismatchException):
invalid_key_pair = {"wrong_pub_key": "wrong_priv_key"}
@ -443,7 +443,7 @@ def test_invoke_simple_signature_fulfillment_with_invalid_params(utx, user_input
def test_sign_threshold_with_invalid_params(utx, user_user2_threshold_input, user3_pub, user3_priv):
from planetmint.transactions.common.exceptions import KeypairMismatchException
from transactions.common.exceptions import KeypairMismatchException
with raises(KeypairMismatchException):
utx._sign_threshold_signature_fulfillment(user_user2_threshold_input, "somemessage", {user3_pub: user3_priv})
@ -845,8 +845,8 @@ def test_transaction_hash(fulfilled_transaction):
def test_output_from_dict_invalid_amount(user_output):
from planetmint.transactions.common.transaction import Output
from planetmint.transactions.common.exceptions import AmountError
from transactions.common.transaction import Output
from transactions.common.exceptions import AmountError
out = user_output.to_dict()
out["amount"] = "a"

View File

@ -5,8 +5,8 @@
def validate_transaction_model(tx):
from planetmint.transactions.common.transaction import Transaction
from planetmint.transactions.common.schema import validate_transaction_schema
from transactions.common.transaction import Transaction
from transactions.common.schema import validate_transaction_schema
tx_dict = tx.to_dict()
# Check that a transaction is valid by re-serializing it

View File

@ -11,7 +11,6 @@ Tasks:
"""
import json
import os
import copy
import random
import tempfile
import codecs
@ -26,16 +25,16 @@ import pytest
# from pymongo import MongoClient
from planetmint.transactions.common import crypto
from planetmint.transactions.common.transaction_mode_types import BROADCAST_TX_COMMIT
from transactions.common import crypto
from transactions.common.transaction_mode_types import BROADCAST_TX_COMMIT
from planetmint.tendermint_utils import key_from_base64
from planetmint.backend import schema, query
from planetmint.transactions.common.crypto import key_pair_from_ed25519_key, public_key_from_ed25519_key
from planetmint.transactions.common.exceptions import DatabaseDoesNotExist
from transactions.common.crypto import key_pair_from_ed25519_key, public_key_from_ed25519_key
from transactions.common.exceptions import DatabaseDoesNotExist
from planetmint.lib import Block
from tests.utils import gen_vote
from planetmint.config import Config
from planetmint.transactions.types.elections.validator_election import ValidatorElection # noqa
from transactions.types.elections.validator_election import ValidatorElection # noqa
from tendermint.abci import types_pb2 as types
from tendermint.crypto import keys_pb2
@ -140,8 +139,8 @@ 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.transactions.common.transaction import Transaction
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
@ -198,14 +197,14 @@ def user2_pk():
@pytest.fixture
def alice():
from planetmint.transactions.common.crypto import generate_key_pair
from transactions.common.crypto import generate_key_pair
return generate_key_pair()
@pytest.fixture
def bob():
from planetmint.transactions.common.crypto import generate_key_pair
from transactions.common.crypto import generate_key_pair
return generate_key_pair()
@ -222,7 +221,7 @@ def bob_pubkey(carol):
@pytest.fixture
def carol():
from planetmint.transactions.common.crypto import generate_key_pair
from transactions.common.crypto import generate_key_pair
return generate_key_pair()
@ -239,7 +238,7 @@ def carol_pubkey(carol):
@pytest.fixture
def merlin():
from planetmint.transactions.common.crypto import generate_key_pair
from transactions.common.crypto import generate_key_pair
return generate_key_pair()
@ -284,7 +283,7 @@ def mock_get_validators(network_validators):
@pytest.fixture
def create_tx(alice, user_pk):
from planetmint.transactions.types.assets.create import Create
from transactions.types.assets.create import Create
name = f"I am created by the create_tx fixture. My random identifier is {random.random()}."
asset = {"data": multihash(marshal({"name": name}))}
@ -305,7 +304,7 @@ def posted_create_tx(b, signed_create_tx):
@pytest.fixture
def signed_transfer_tx(signed_create_tx, user_pk, user_sk):
from planetmint.transactions.types.assets.transfer import Transfer
from transactions.types.assets.transfer import Transfer
inputs = signed_create_tx.to_inputs()
tx = Transfer.generate(inputs, [([user_pk], 1)], asset_id=signed_create_tx.id)
@ -314,7 +313,7 @@ def signed_transfer_tx(signed_create_tx, user_pk, user_sk):
@pytest.fixture
def double_spend_tx(signed_create_tx, carol_pubkey, user_sk):
from planetmint.transactions.types.assets.transfer import Transfer
from transactions.types.assets.transfer import Transfer
inputs = signed_create_tx.to_inputs()
tx = Transfer.generate(inputs, [([carol_pubkey], 1)], asset_id=signed_create_tx.id)
@ -328,7 +327,7 @@ def _get_height(b):
@pytest.fixture
def inputs(user_pk, b, alice):
from planetmint.transactions.types.assets.create import Create
from transactions.types.assets.create import Create
# create blocks with transactions for `USER` to spend
for height in range(1, 4):

View File

@ -4,8 +4,8 @@
# Code is Apache-2.0 and docs are CC-BY-4.0
import warnings
from unittest.mock import patch
from planetmint.transactions.types.assets.create import Create
from planetmint.transactions.types.assets.transfer import Transfer
from transactions.types.assets.create import Create
from transactions.types.assets.transfer import Transfer
from ipld import marshal, multihash
import pytest
from base58 import b58decode
@ -16,7 +16,7 @@ pytestmark = pytest.mark.bdb
class TestBigchainApi(object):
def test_get_spent_with_double_spend_detected(self, b, alice):
from planetmint.transactions.common.exceptions import DoubleSpend
from transactions.common.exceptions import DoubleSpend
from planetmint.exceptions import CriticalDoubleSpend
tx = Create.generate([alice.public_key], [([alice.public_key], 1)])
@ -85,8 +85,8 @@ class TestBigchainApi(object):
@pytest.mark.usefixtures("inputs")
def test_non_create_input_not_found(self, b, user_pk):
from cryptoconditions import Ed25519Sha256
from planetmint.transactions.common.exceptions import InputDoesNotExist
from planetmint.transactions.common.transaction import Input, TransactionLink
from transactions.common.exceptions import InputDoesNotExist
from transactions.common.transaction import Input, TransactionLink
# Create an input for a non existing transaction
input = Input(
@ -116,8 +116,8 @@ class TestBigchainApi(object):
class TestTransactionValidation(object):
def test_non_create_input_not_found(self, b, signed_transfer_tx):
from planetmint.transactions.common.exceptions import InputDoesNotExist
from planetmint.transactions.common.transaction import TransactionLink
from transactions.common.exceptions import InputDoesNotExist
from transactions.common.transaction import TransactionLink
signed_transfer_tx.inputs[0].fulfills = TransactionLink("c", 0)
with pytest.raises(InputDoesNotExist):
@ -125,8 +125,8 @@ class TestTransactionValidation(object):
@pytest.mark.usefixtures("inputs")
def test_non_create_valid_input_wrong_owner(self, b, user_pk):
from planetmint.transactions.common.crypto import generate_key_pair
from planetmint.transactions.common.exceptions import InvalidSignature
from transactions.common.crypto import generate_key_pair
from transactions.common.exceptions import InvalidSignature
input_tx = b.fastquery.get_outputs_by_public_key(user_pk).pop()
input_transaction = b.get_transaction(input_tx.txid)
@ -141,7 +141,7 @@ class TestTransactionValidation(object):
@pytest.mark.usefixtures("inputs")
def test_non_create_double_spend(self, b, signed_create_tx, signed_transfer_tx, double_spend_tx):
from planetmint.transactions.common.exceptions import DoubleSpend
from transactions.common.exceptions import DoubleSpend
b.store_bulk_transactions([signed_create_tx, signed_transfer_tx])
@ -151,7 +151,7 @@ class TestTransactionValidation(object):
class TestMultipleInputs(object):
def test_transfer_single_owner_single_input(self, b, inputs, user_pk, user_sk):
from planetmint.transactions.common import crypto
from transactions.common import crypto
user2_sk, user2_pk = crypto.generate_key_pair()
@ -167,7 +167,7 @@ class TestMultipleInputs(object):
assert len(tx.outputs) == 1
def test_single_owner_before_multiple_owners_after_single_input(self, b, user_sk, user_pk, inputs):
from planetmint.transactions.common import crypto
from transactions.common import crypto
user2_sk, user2_pk = crypto.generate_key_pair()
user3_sk, user3_pk = crypto.generate_key_pair()
@ -183,7 +183,7 @@ class TestMultipleInputs(object):
@pytest.mark.usefixtures("inputs")
def test_multiple_owners_before_single_owner_after_single_input(self, b, user_sk, user_pk, alice):
from planetmint.transactions.common import crypto
from transactions.common import crypto
user2_sk, user2_pk = crypto.generate_key_pair()
user3_sk, user3_pk = crypto.generate_key_pair()
@ -206,7 +206,7 @@ class TestMultipleInputs(object):
@pytest.mark.usefixtures("inputs")
def test_multiple_owners_before_multiple_owners_after_single_input(self, b, user_sk, user_pk, alice):
from planetmint.transactions.common import crypto
from transactions.common import crypto
user2_sk, user2_pk = crypto.generate_key_pair()
user3_sk, user3_pk = crypto.generate_key_pair()
@ -228,8 +228,8 @@ class TestMultipleInputs(object):
assert len(tx.outputs) == 1
def test_get_owned_ids_single_tx_single_output(self, b, user_sk, user_pk, alice):
from planetmint.transactions.common import crypto
from planetmint.transactions.common.transaction import TransactionLink
from transactions.common import crypto
from transactions.common.transaction import TransactionLink
user2_sk, user2_pk = crypto.generate_key_pair()
@ -253,8 +253,8 @@ class TestMultipleInputs(object):
assert owned_inputs_user2 == [TransactionLink(tx_transfer.id, 0)]
def test_get_owned_ids_single_tx_multiple_outputs(self, b, user_sk, user_pk, alice):
from planetmint.transactions.common import crypto
from planetmint.transactions.common.transaction import TransactionLink
from transactions.common import crypto
from transactions.common.transaction import TransactionLink
user2_sk, user2_pk = crypto.generate_key_pair()
@ -284,8 +284,8 @@ class TestMultipleInputs(object):
assert owned_inputs_user2 == [TransactionLink(tx_transfer.id, 0), TransactionLink(tx_transfer.id, 1)]
def test_get_owned_ids_multiple_owners(self, b, user_sk, user_pk, alice):
from planetmint.transactions.common import crypto
from planetmint.transactions.common.transaction import TransactionLink
from transactions.common import crypto
from transactions.common.transaction import TransactionLink
user2_sk, user2_pk = crypto.generate_key_pair()
user3_sk, user3_pk = crypto.generate_key_pair()
@ -314,7 +314,7 @@ class TestMultipleInputs(object):
assert not spent_user1
def test_get_spent_single_tx_single_output(self, b, user_sk, user_pk, alice):
from planetmint.transactions.common import crypto
from transactions.common import crypto
user2_sk, user2_pk = crypto.generate_key_pair()
@ -338,7 +338,7 @@ class TestMultipleInputs(object):
assert spent_inputs_user1 == tx
def test_get_spent_single_tx_multiple_outputs(self, b, user_sk, user_pk, alice):
from planetmint.transactions.common import crypto
from transactions.common import crypto
# create a new users
user2_sk, user2_pk = crypto.generate_key_pair()
@ -371,7 +371,7 @@ class TestMultipleInputs(object):
assert b.get_spent(tx_create.to_inputs()[2].fulfills.txid, 2) is None
def test_get_spent_multiple_owners(self, b, user_sk, user_pk, alice):
from planetmint.transactions.common import crypto
from transactions.common import crypto
user2_sk, user2_pk = crypto.generate_key_pair()
user3_sk, user3_pk = crypto.generate_key_pair()
@ -403,7 +403,7 @@ class TestMultipleInputs(object):
def test_get_outputs_filtered_only_unspent():
from planetmint.transactions.common.transaction import TransactionLink
from transactions.common.transaction import TransactionLink
from planetmint.lib import Planetmint
go = "planetmint.fastquery.FastQuery.get_outputs_by_public_key"
@ -418,7 +418,7 @@ def test_get_outputs_filtered_only_unspent():
def test_get_outputs_filtered_only_spent():
from planetmint.transactions.common.transaction import TransactionLink
from transactions.common.transaction import TransactionLink
from planetmint.lib import Planetmint
go = "planetmint.fastquery.FastQuery.get_outputs_by_public_key"
@ -435,7 +435,7 @@ def test_get_outputs_filtered_only_spent():
@patch("planetmint.fastquery.FastQuery.filter_unspent_outputs")
@patch("planetmint.fastquery.FastQuery.filter_spent_outputs")
def test_get_outputs_filtered(filter_spent, filter_unspent):
from planetmint.transactions.common.transaction import TransactionLink
from transactions.common.transaction import TransactionLink
from planetmint.lib import Planetmint
go = "planetmint.fastquery.FastQuery.get_outputs_by_public_key"
@ -452,7 +452,7 @@ def test_cant_spend_same_input_twice_in_tx(b, alice):
"""Recreate duplicated fulfillments bug
https://github.com/planetmint/planetmint/issues/1099
"""
from planetmint.transactions.common.exceptions import DoubleSpend
from transactions.common.exceptions import DoubleSpend
# create a divisible asset
tx_create = Create.generate([alice.public_key], [([alice.public_key], 100)])
@ -470,7 +470,7 @@ def test_cant_spend_same_input_twice_in_tx(b, alice):
def test_transaction_unicode(b, alice):
import copy
from planetmint.transactions.common.utils import serialize
from transactions.common.utils import serialize
# http://www.fileformat.info/info/unicode/char/1f37a/index.htm

View File

@ -3,9 +3,9 @@ import pytest
from tests.utils import generate_election, generate_validators
from planetmint.lib import Block
from planetmint.transactions.types.elections.election import Election
from planetmint.transactions.types.elections.chain_migration_election import ChainMigrationElection
from planetmint.transactions.types.elections.validator_election import ValidatorElection
from transactions.types.elections.election import Election
from transactions.types.elections.chain_migration_election import ChainMigrationElection
from transactions.types.elections.validator_election import ValidatorElection
@pytest.mark.bdb

View File

@ -1,4 +1,4 @@
from planetmint.transactions.types.elections.chain_migration_election import ChainMigrationElection
from transactions.types.elections.chain_migration_election import ChainMigrationElection
def test_valid_migration_election(b_mock, node_key):

View File

@ -4,28 +4,27 @@
# Code is Apache-2.0 and docs are CC-BY-4.0
import json
from planetmint.transactions.types.assets.create import Create
from planetmint.transactions.types.assets.transfer import Transfer
import pytest
import random
import multiprocessing as mp
import pytest
from tendermint.abci import types_pb2 as types
from tendermint.crypto import keys_pb2
from transactions import ValidatorElection, ChainMigrationElection
from transactions.common.crypto import generate_key_pair
from transactions.types.assets.create import Create
from transactions.types.assets.transfer import Transfer
from planetmint import App
from planetmint.backend import query
from planetmint.transactions.common.crypto import generate_key_pair
from planetmint.core import OkCode, CodeTypeError, rollback
from planetmint.lib import Block
from planetmint.transactions.types.elections.chain_migration_election import ChainMigrationElection
from planetmint.transactions.types.elections.validator_election import ValidatorElection
from planetmint.tendermint_utils import new_validator_set
from planetmint.tendermint_utils import public_key_to_base64
from planetmint.version import __tm_supported_versions__
from tests.utils import generate_election, generate_validators
pytestmark = pytest.mark.bdb
@ -197,9 +196,6 @@ def test_info(b):
def test_check_tx__signed_create_is_ok(b):
from planetmint import App
from planetmint.transactions.common.crypto import generate_key_pair
alice = generate_key_pair()
bob = generate_key_pair()
@ -211,9 +207,6 @@ def test_check_tx__signed_create_is_ok(b):
def test_check_tx__unsigned_create_is_error(b):
from planetmint import App
from planetmint.transactions.common.crypto import generate_key_pair
alice = generate_key_pair()
bob = generate_key_pair()
@ -225,10 +218,6 @@ def test_check_tx__unsigned_create_is_error(b):
def test_deliver_tx__valid_create_updates_db_and_emits_event(b, init_chain_request):
import multiprocessing as mp
from planetmint import App
from planetmint.transactions.common.crypto import generate_key_pair
alice = generate_key_pair()
bob = generate_key_pair()
events = mp.Queue()
@ -260,9 +249,6 @@ def test_deliver_tx__valid_create_updates_db_and_emits_event(b, init_chain_reque
def test_deliver_tx__double_spend_fails(b, init_chain_request):
from planetmint import App
from planetmint.transactions.common.crypto import generate_key_pair
alice = generate_key_pair()
bob = generate_key_pair()
@ -285,9 +271,6 @@ def test_deliver_tx__double_spend_fails(b, init_chain_request):
def test_deliver_transfer_tx__double_spend_fails(b, init_chain_request):
from planetmint import App
from planetmint.transactions.common.crypto import generate_key_pair
app = App(b)
app.init_chain(init_chain_request)

View File

@ -5,9 +5,9 @@
import pytest
from planetmint.transactions.common.transaction import TransactionLink
from planetmint.transactions.types.assets.create import Create
from planetmint.transactions.types.assets.transfer import Transfer
from transactions.common.transaction import TransactionLink
from transactions.types.assets.create import Create
from transactions.types.assets.transfer import Transfer
pytestmark = pytest.mark.bdb

View File

@ -4,8 +4,8 @@
# Code is Apache-2.0 and docs are CC-BY-4.0
import codecs
from planetmint.transactions.types.assets.create import Create
from planetmint.transactions.types.assets.transfer import Transfer
from transactions.types.assets.create import Create
from transactions.types.assets.transfer import Transfer
from tendermint.abci import types_pb2 as types
import json
@ -15,7 +15,7 @@ import pytest
from abci.server import ProtocolHandler
from abci.utils import read_messages
from planetmint.transactions.common.transaction_mode_types import BROADCAST_TX_COMMIT, BROADCAST_TX_SYNC
from transactions.common.transaction_mode_types import BROADCAST_TX_COMMIT, BROADCAST_TX_SYNC
from planetmint.version import __tm_supported_versions__
from io import BytesIO
@ -24,7 +24,7 @@ from io import BytesIO
def test_app(b, eventqueue_fixture, init_chain_request):
from planetmint import App
from planetmint.tendermint_utils import calculate_hash
from planetmint.transactions.common.crypto import generate_key_pair
from transactions.common.crypto import generate_key_pair
app = App(b, eventqueue_fixture)
p = ProtocolHandler(app)
@ -111,7 +111,7 @@ def test_app(b, eventqueue_fixture, init_chain_request):
@pytest.mark.abci
def test_post_transaction_responses(tendermint_ws_url, b):
from planetmint.transactions.common.crypto import generate_key_pair
from transactions.common.crypto import generate_key_pair
alice = generate_key_pair()
bob = generate_key_pair()

View File

@ -6,8 +6,8 @@
from operator import index
import os
from unittest.mock import patch
from planetmint.transactions.types.assets.create import Create
from planetmint.transactions.types.assets.transfer import Transfer
from transactions.types.assets.create import Create
from transactions.types.assets.transfer import Transfer
from hashlib import sha3_256
@ -15,7 +15,7 @@ import pytest
from pymongo import MongoClient
from planetmint import backend
from planetmint.transactions.common.transaction_mode_types import (
from transactions.common.transaction_mode_types import (
BROADCAST_TX_COMMIT,
BROADCAST_TX_ASYNC,
BROADCAST_TX_SYNC,
@ -27,7 +27,7 @@ from ipld import marshal, multihash
@pytest.mark.bdb
def test_asset_is_separated_from_transaciton(b):
import copy
from planetmint.transactions.common.crypto import generate_key_pair
from transactions.common.crypto import generate_key_pair
from planetmint.backend.tarantool.connection import TarantoolDBConnection
if isinstance(b.connection, TarantoolDBConnection):
@ -92,7 +92,7 @@ def test_get_empty_block(_0, _1, b):
def test_validation_error(b):
from planetmint.transactions.common.crypto import generate_key_pair
from transactions.common.crypto import generate_key_pair
alice = generate_key_pair()
tx = Create.generate([alice.public_key], [([alice.public_key], 1)], asset=None).sign([alice.private_key]).to_dict()
@ -103,7 +103,7 @@ def test_validation_error(b):
@patch("requests.post")
def test_write_and_post_transaction(mock_post, b):
from planetmint.transactions.common.crypto import generate_key_pair
from transactions.common.crypto import generate_key_pair
from planetmint.tendermint_utils import encode_transaction
alice = generate_key_pair()
@ -122,7 +122,7 @@ def test_write_and_post_transaction(mock_post, b):
@patch("requests.post")
@pytest.mark.parametrize("mode", [BROADCAST_TX_SYNC, BROADCAST_TX_ASYNC, BROADCAST_TX_COMMIT])
def test_post_transaction_valid_modes(mock_post, b, mode):
from planetmint.transactions.common.crypto import generate_key_pair
from transactions.common.crypto import generate_key_pair
alice = generate_key_pair()
tx = Create.generate([alice.public_key], [([alice.public_key], 1)], asset=None).sign([alice.private_key]).to_dict()
@ -134,8 +134,8 @@ def test_post_transaction_valid_modes(mock_post, b, mode):
def test_post_transaction_invalid_mode(b):
from planetmint.transactions.common.crypto import generate_key_pair
from planetmint.transactions.common.exceptions import ValidationError
from transactions.common.crypto import generate_key_pair
from transactions.common.exceptions import ValidationError
alice = generate_key_pair()
tx = Create.generate([alice.public_key], [([alice.public_key], 1)], asset=None).sign([alice.private_key]).to_dict()
@ -405,7 +405,7 @@ def test_get_utxoset_merkle_root(b, utxoset):
@pytest.mark.bdb
def test_get_spent_transaction_critical_double_spend(b, alice, bob, carol):
from planetmint.exceptions import CriticalDoubleSpend
from planetmint.transactions.common.exceptions import DoubleSpend
from transactions.common.exceptions import DoubleSpend
asset = {"data": multihash(marshal({"test": "asset"}))}
@ -443,7 +443,7 @@ def test_get_spent_transaction_critical_double_spend(b, alice, bob, carol):
def test_validation_with_transaction_buffer(b):
from planetmint.transactions.common.crypto import generate_key_pair
from transactions.common.crypto import generate_key_pair
priv_key, pub_key = generate_key_pair()
@ -493,8 +493,8 @@ def test_migrate_abci_chain_generates_new_chains(b, chain, block_height, expecte
@pytest.mark.bdb
def test_get_spent_key_order(b, user_pk, user_sk, user2_pk, user2_sk):
from planetmint import backend
from planetmint.transactions.common.crypto import generate_key_pair
from planetmint.transactions.common.exceptions import DoubleSpend
from transactions.common.crypto import generate_key_pair
from transactions.common.exceptions import DoubleSpend
alice = generate_key_pair()
bob = generate_key_pair()

View File

@ -289,7 +289,7 @@ def test_file_config():
def test_invalid_file_config():
from planetmint.config_utils import file_config
from planetmint.transactions.common import exceptions
from transactions.common import exceptions
with patch("builtins.open", mock_open(read_data="{_INVALID_JSON_}")):
with pytest.raises(exceptions.ConfigurationError):

View File

@ -5,8 +5,8 @@
import pytest
from planetmint.version import __tm_supported_versions__
from planetmint.transactions.types.assets.create import Create
from planetmint.transactions.types.assets.transfer import Transfer
from transactions.types.assets.create import Create
from transactions.types.assets.transfer import Transfer
@pytest.fixture

View File

@ -5,9 +5,9 @@
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
from transactions.common.crypto import generate_key_pair
from transactions.types.assets.create import Create
from transactions.types.assets.transfer import Transfer
pytestmark = pytest.mark.tendermint

View File

@ -12,8 +12,8 @@ import pytest
@pytest.fixture
def txlist(b, user_pk, user2_pk, user_sk, user2_sk):
from planetmint.transactions.types.assets.create import Create
from planetmint.transactions.types.assets.transfer import Transfer
from transactions.types.assets.create import Create
from transactions.types.assets.transfer import Transfer
# Create two CREATE transactions
create1 = Create.generate([user_pk], [([user2_pk], 6)]).sign([user_sk])

View File

@ -7,7 +7,7 @@ from unittest.mock import patch
import pytest
from planetmint.backend import query
from planetmint.transactions.types.elections.validator_election import ValidatorElection
from transactions.types.elections.validator_election import ValidatorElection
@pytest.fixture
@ -17,7 +17,7 @@ def valid_upsert_validator_election_b(b, node_key, new_validator):
@pytest.fixture
@patch("planetmint.transactions.types.elections.election.uuid4", lambda: "mock_uuid4")
@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()
return ValidatorElection.generate([node_key.public_key], voters, new_validator, None).sign([node_key.private_key])

View File

@ -6,15 +6,14 @@
import pytest
import codecs
from planetmint.transactions.types.elections.election import Election
from planetmint.tendermint_utils import public_key_to_base64
from planetmint.transactions.types.elections.validator_election import ValidatorElection
from planetmint.transactions.common.exceptions import AmountError
from planetmint.transactions.common.crypto import generate_key_pair
from planetmint.transactions.common.exceptions import ValidationError
from planetmint.transactions.common.transaction_mode_types import BROADCAST_TX_COMMIT
from planetmint.transactions.types.elections.vote import Vote
from planetmint.transactions.types.elections.validator_utils import election_id_to_public_key
from transactions.types.elections.validator_election import ValidatorElection
from transactions.common.exceptions import AmountError
from transactions.common.crypto import generate_key_pair
from transactions.common.exceptions import ValidationError
from transactions.common.transaction_mode_types import BROADCAST_TX_COMMIT
from transactions.types.elections.vote import Vote
from transactions.types.elections.validator_utils import election_id_to_public_key
from tests.utils import generate_block, gen_vote
pytestmark = [pytest.mark.execute]

View File

@ -8,8 +8,8 @@ from unittest.mock import patch
import pytest
from planetmint.tendermint_utils import public_key_to_base64
from planetmint.transactions.types.elections.validator_election import ValidatorElection
from planetmint.transactions.common.exceptions import (
from transactions.types.elections.validator_election import ValidatorElection
from transactions.common.exceptions import (
DuplicateTransaction,
UnequalValidatorSet,
InvalidProposer,
@ -29,7 +29,7 @@ def test_upsert_validator_valid_election(b_mock, new_validator, node_key):
def test_upsert_validator_invalid_election_public_key(b_mock, new_validator, node_key):
from planetmint.transactions.common.exceptions import InvalidPublicKey
from transactions.common.exceptions import InvalidPublicKey
for iv in ["ed25519-base32", "ed25519-base64"]:
new_validator["public_key"]["type"] = iv
@ -51,7 +51,7 @@ def test_upsert_validator_invalid_power_election(b_mock, new_validator, node_key
def test_upsert_validator_invalid_proposed_election(b_mock, new_validator, node_key):
from planetmint.transactions.common.crypto import generate_key_pair
from transactions.common.crypto import generate_key_pair
alice = generate_key_pair()
voters = b_mock.get_recipients_list()
@ -61,7 +61,7 @@ def test_upsert_validator_invalid_proposed_election(b_mock, new_validator, node_
def test_upsert_validator_invalid_inputs_election(b_mock, new_validator, node_key):
from planetmint.transactions.common.crypto import generate_key_pair
from transactions.common.crypto import generate_key_pair
alice = generate_key_pair()
voters = b_mock.get_recipients_list()
@ -72,7 +72,7 @@ def test_upsert_validator_invalid_inputs_election(b_mock, new_validator, node_ke
b_mock.validate_election(election)
@patch("planetmint.transactions.types.elections.election.uuid4", lambda: "mock_uuid4")
@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(

View File

@ -12,11 +12,11 @@ from functools import singledispatch
from planetmint.backend.localmongodb.connection import LocalMongoDBConnection
from planetmint.backend.tarantool.connection import TarantoolDBConnection
from planetmint.backend.schema import TABLES, SPACE_NAMES
from planetmint.transactions.common import crypto
from planetmint.transactions.common.transaction_mode_types import BROADCAST_TX_COMMIT
from planetmint.transactions.types.assets.create import Create
from planetmint.transactions.types.elections.vote import Vote
from planetmint.transactions.types.elections.validator_utils import election_id_to_public_key
from transactions.common import crypto
from transactions.common.transaction_mode_types import BROADCAST_TX_COMMIT
from transactions.types.assets.create import Create
from transactions.types.elections.vote import Vote
from transactions.types.elections.validator_utils import election_id_to_public_key
from planetmint.tendermint_utils import key_to_base64
@ -55,7 +55,7 @@ def flush_tarantool_db(connection, dbname):
def generate_block(planet):
from planetmint.transactions.common.crypto import generate_key_pair
from transactions.common.crypto import generate_key_pair
alice = generate_key_pair()
tx = Create.generate([alice.public_key], [([alice.public_key], 1)], asset=None).sign([alice.private_key])

View File

@ -14,9 +14,9 @@ import pytest
import hashlib as sha3
from unittest.mock import MagicMock
from planetmint.transactions.common.exceptions import AmountError, SchemaValidationError, ThresholdTooDeep
from planetmint.transactions.common.transaction import Transaction
from planetmint.transactions.common.utils import _fulfillment_to_details, _fulfillment_from_details
from transactions.common.exceptions import AmountError, SchemaValidationError, ThresholdTooDeep
from transactions.common.transaction import Transaction
from transactions.common.utils import _fulfillment_to_details, _fulfillment_from_details
from ipld import marshal, multihash
################################################################################
@ -51,7 +51,7 @@ def test_tx_serialization_hash_function(signed_create_tx):
def test_tx_serialization_with_incorrect_hash(signed_create_tx):
from planetmint.transactions.common.exceptions import InvalidHash
from transactions.common.exceptions import InvalidHash
tx = signed_create_tx.to_dict()
tx["id"] = "a" * 64
@ -60,7 +60,7 @@ def test_tx_serialization_with_incorrect_hash(signed_create_tx):
def test_tx_serialization_with_no_hash(signed_create_tx):
from planetmint.transactions.common.exceptions import InvalidHash
from transactions.common.exceptions import InvalidHash
tx = signed_create_tx.to_dict()
del tx["id"]
@ -105,7 +105,7 @@ def test_validate_fails_metadata_empty_dict(b, create_tx, alice):
def test_transfer_asset_schema(user_sk, signed_transfer_tx):
from planetmint.transactions.common.transaction import Transaction
from transactions.common.transaction import Transaction
tx = signed_transfer_tx.to_dict()
validate(tx)
@ -152,7 +152,7 @@ def test_no_inputs(b, create_tx, alice):
def test_create_single_input(b, create_tx, alice):
from planetmint.transactions.common.transaction import Transaction
from transactions.common.transaction import Transaction
tx = create_tx.to_dict()
tx["inputs"] += tx["inputs"]
@ -165,7 +165,7 @@ def test_create_single_input(b, create_tx, alice):
def test_create_tx_no_fulfills(b, create_tx, alice):
from planetmint.transactions.common.transaction import Transaction
from transactions.common.transaction import Transaction
tx = create_tx.to_dict()
tx["inputs"][0]["fulfills"] = {"transaction_id": "a" * 64, "output_index": 0}

View File

@ -4,7 +4,7 @@
# Code is Apache-2.0 and docs are CC-BY-4.0
import pytest
from planetmint.transactions.types.assets.create import Create
from transactions.types.assets.create import Create
from ipld import marshal, multihash
ASSETS_ENDPOINT = "/api/v1/assets/"

View File

@ -5,7 +5,7 @@
import pytest
from planetmint.transactions.types.assets.create import Create
from transactions.types.assets.create import Create
from planetmint.lib import Block
from ipld import marshal, multihash

View File

@ -4,7 +4,7 @@
# Code is Apache-2.0 and docs are CC-BY-4.0
import pytest
from planetmint.transactions.types.assets.create import Create
from transactions.types.assets.create import Create
from ipld import marshal, multihash
METADATA_ENDPOINT = "/api/v1/metadata/"

View File

@ -5,8 +5,8 @@
import pytest
from planetmint.transactions.types.assets.create import Create
from planetmint.transactions.types.assets.transfer import Transfer
from transactions.types.assets.create import Create
from transactions.types.assets.transfer import Transfer
from unittest.mock import MagicMock, patch
@ -83,7 +83,7 @@ def test_get_outputs_endpoint_with_invalid_spent(client, user_pk):
@pytest.mark.abci
def test_get_divisble_transactions_returns_500(b, client):
from planetmint.transactions.common import crypto
from transactions.common import crypto
import json
TX_ENDPOINT = "/api/v1/transactions"

View File

@ -13,10 +13,10 @@ from ipld import multihash, marshal
from hashlib import sha3_256
from planetmint.transactions.common import crypto
from planetmint.transactions.types.assets.create import Create
from planetmint.transactions.types.assets.transfer import Transfer
from planetmint.transactions.common.transaction_mode_types import (
from transactions.common import crypto
from transactions.types.assets.create import Create
from transactions.types.assets.transfer import Transfer
from transactions.common.transaction_mode_types import (
BROADCAST_TX_COMMIT,
BROADCAST_TX_ASYNC,
BROADCAST_TX_SYNC,
@ -161,7 +161,7 @@ def test_post_create_transaction_with_invalid_key(b, client, field, value, err_k
@pytest.mark.abci
@patch("planetmint.web.views.base.logger")
def test_post_create_transaction_with_invalid_id(mock_logger, b, client):
from planetmint.transactions.common.exceptions import InvalidHash
from transactions.common.exceptions import InvalidHash
user_priv, user_pub = crypto.generate_key_pair()
@ -193,7 +193,7 @@ def test_post_create_transaction_with_invalid_id(mock_logger, b, client):
@pytest.mark.abci
@patch("planetmint.web.views.base.logger")
def test_post_create_transaction_with_invalid_signature(mock_logger, b, client):
from planetmint.transactions.common.exceptions import InvalidSignature
from transactions.common.exceptions import InvalidSignature
user_priv, user_pub = crypto.generate_key_pair()
@ -300,7 +300,7 @@ def test_post_invalid_transaction(
msg,
monkeypatch,
):
from planetmint.transactions.common import exceptions
from transactions.common import exceptions
exc_cls = getattr(exceptions, exc)
@ -310,7 +310,7 @@ def test_post_invalid_transaction(
TransactionMock = Mock(validate=mock_validation)
monkeypatch.setattr(
"planetmint.transactions.common.transaction.Transaction.from_dict",
"transactions.common.transaction.Transaction.from_dict",
lambda tx, skip_schema_validation: TransactionMock,
)
res = client.post(TX_ENDPOINT, data=json.dumps({}))
@ -346,7 +346,7 @@ def test_post_transfer_transaction_endpoint(client, user_pk, user_sk, posted_cre
@pytest.mark.abci
def test_post_invalid_transfer_transaction_returns_400(client, user_pk, posted_create_tx):
from planetmint.transactions.common.exceptions import InvalidSignature
from transactions.common.exceptions import InvalidSignature
transfer_tx = Transfer.generate(posted_create_tx.to_inputs(), [([user_pk], 1)], asset_id=posted_create_tx.id)
transfer_tx._hash()
@ -362,7 +362,7 @@ def test_post_invalid_transfer_transaction_returns_400(client, user_pk, posted_c
@pytest.mark.abci
def test_post_wrong_asset_division_transfer_returns_400(b, client, user_pk):
from planetmint.transactions.common.exceptions import AmountError
from transactions.common.exceptions import AmountError
priv_key, pub_key = crypto.generate_key_pair()
@ -448,7 +448,7 @@ def test_transactions_get_list_bad(client):
],
)
def test_post_transaction_valid_modes(mock_post, client, mode):
from planetmint.transactions.common.crypto import generate_key_pair
from transactions.common.crypto import generate_key_pair
def _mock_post(*args, **kwargs):
return Mock(json=Mock(return_value={"result": {"code": 0}}))
@ -465,7 +465,7 @@ def test_post_transaction_valid_modes(mock_post, client, mode):
@pytest.mark.abci
def test_post_transaction_invalid_mode(client):
from planetmint.transactions.common.crypto import generate_key_pair
from transactions.common.crypto import generate_key_pair
alice = generate_key_pair()
tx = Create.generate([alice.public_key], [([alice.public_key], 1)], asset=None).sign([alice.private_key])

View File

@ -9,8 +9,8 @@ import queue
import threading
# from unittest.mock import patch
from planetmint.transactions.types.assets.create import Create
from planetmint.transactions.types.assets.transfer import Transfer
from transactions.types.assets.create import Create
from transactions.types.assets.transfer import Transfer
from ipld import multihash, marshal
import pytest
@ -26,7 +26,7 @@ class MockWebSocket:
def test_eventify_block_works_with_any_transaction():
from planetmint.web.websocket_dispatcher import Dispatcher
from planetmint.transactions.common.crypto import generate_key_pair
from transactions.common.crypto import generate_key_pair
alice = generate_key_pair()
@ -48,7 +48,7 @@ def test_eventify_block_works_with_any_transaction():
def test_simplified_block_works():
from planetmint.web.websocket_dispatcher import Dispatcher
from planetmint.transactions.common.crypto import generate_key_pair
from transactions.common.crypto import generate_key_pair
alice = generate_key_pair()
@ -138,7 +138,7 @@ async def test_bridge_sync_async_queue(event_loop):
async def test_websocket_block_event(aiohttp_client, event_loop):
from planetmint import events
from planetmint.web.websocket_server import init_app, EVENTS_ENDPOINT_BLOCKS
from planetmint.transactions.common import crypto
from transactions.common import crypto
user_priv, user_pub = crypto.generate_key_pair()
tx = Create.generate([user_pub], [([user_pub], 1)])
@ -172,7 +172,7 @@ async def test_websocket_block_event(aiohttp_client, event_loop):
async def test_websocket_transaction_event(aiohttp_client, event_loop):
from planetmint import events
from planetmint.web.websocket_server import init_app, EVENTS_ENDPOINT
from planetmint.transactions.common import crypto
from transactions.common import crypto
user_priv, user_pub = crypto.generate_key_pair()
tx = Create.generate([user_pub], [([user_pub], 1)])
@ -241,7 +241,7 @@ def test_integration_from_webapi_to_websocket(monkeypatch, client, loop):
import random
import aiohttp
from planetmint.transactions.common import crypto
from transactions.common import crypto
# TODO processes does not exist anymore, when reactivating this test it
# will fail because of this