mirror of
https://github.com/planetmint/planetmint.git
synced 2025-11-24 14:35:45 +00:00
Use planetmint-transaction pypi package
Signed-off-by: cybnon <stefan.weber93@googlemail.com>
This commit is contained in:
parent
db7d9663f1
commit
ab58eec5af
@ -3,10 +3,10 @@
|
|||||||
# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
|
# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
|
||||||
# Code is Apache-2.0 and docs are CC-BY-4.0
|
# Code is Apache-2.0 and docs are CC-BY-4.0
|
||||||
|
|
||||||
from planetmint.transactions.common.transaction import Transaction # noqa
|
from transactions.common.transaction import Transaction # noqa
|
||||||
from planetmint.transactions.types.elections.validator_election import ValidatorElection # noqa
|
from transactions.types.elections.validator_election import ValidatorElection # noqa
|
||||||
from planetmint.transactions.types.elections.vote import Vote # noqa
|
from transactions.types.elections.vote import Vote # noqa
|
||||||
from planetmint.transactions.types.elections.chain_migration_election import ChainMigrationElection
|
from transactions.types.elections.chain_migration_election import ChainMigrationElection
|
||||||
from planetmint.lib import Planetmint
|
from planetmint.lib import Planetmint
|
||||||
from planetmint.core import App
|
from planetmint.core import App
|
||||||
|
|
||||||
|
|||||||
@ -8,10 +8,10 @@ import logging
|
|||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
|
|
||||||
import tarantool
|
import tarantool
|
||||||
|
from transactions.common.exceptions import ConfigurationError
|
||||||
|
|
||||||
from planetmint.config import Config
|
from planetmint.config import Config
|
||||||
from planetmint.backend.exceptions import ConnectionError
|
from planetmint.backend.exceptions import ConnectionError
|
||||||
from planetmint.transactions.common.exceptions import ConfigurationError
|
|
||||||
|
|
||||||
BACKENDS = {
|
BACKENDS = {
|
||||||
"tarantool_db": "planetmint.backend.tarantool.connection.TarantoolDBConnection",
|
"tarantool_db": "planetmint.backend.tarantool.connection.TarantoolDBConnection",
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import pymongo
|
|||||||
|
|
||||||
from planetmint.config import Config
|
from planetmint.config import Config
|
||||||
from planetmint.backend.exceptions import DuplicateKeyError, OperationError, ConnectionError
|
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.utils import Lazy
|
||||||
from planetmint.backend.connection import Connection
|
from planetmint.backend.connection import Connection
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,7 @@ from planetmint import backend
|
|||||||
from planetmint.backend.exceptions import DuplicateKeyError
|
from planetmint.backend.exceptions import DuplicateKeyError
|
||||||
from planetmint.backend.utils import module_dispatch_registrar
|
from planetmint.backend.utils import module_dispatch_registrar
|
||||||
from planetmint.backend.localmongodb.connection import LocalMongoDBConnection
|
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)
|
register_query = module_dispatch_registrar(backend.query)
|
||||||
|
|
||||||
|
|||||||
@ -10,8 +10,8 @@ import logging
|
|||||||
|
|
||||||
from planetmint.config import Config
|
from planetmint.config import Config
|
||||||
from planetmint.backend.connection import connect
|
from planetmint.backend.connection import connect
|
||||||
from planetmint.transactions.common.exceptions import ValidationError
|
from transactions.common.exceptions import ValidationError
|
||||||
from planetmint.transactions.common.utils import (
|
from transactions.common.utils import (
|
||||||
validate_all_values_for_key_in_obj,
|
validate_all_values_for_key_in_obj,
|
||||||
validate_all_values_for_key_in_list,
|
validate_all_values_for_key_in_list,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import logging
|
|||||||
import tarantool
|
import tarantool
|
||||||
|
|
||||||
from planetmint.config import Config
|
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.utils import Lazy
|
||||||
from planetmint.backend.connection import Connection
|
from planetmint.backend.connection import Connection
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
from secrets import token_hex
|
from secrets import token_hex
|
||||||
import copy
|
import copy
|
||||||
import json
|
import json
|
||||||
from planetmint.transactions.common.memoize import HDict
|
from transactions.common.memoize import HDict
|
||||||
|
|
||||||
|
|
||||||
def get_items(_list):
|
def get_items(_list):
|
||||||
|
|||||||
@ -15,11 +15,11 @@ import sys
|
|||||||
|
|
||||||
from planetmint.core import rollback
|
from planetmint.core import rollback
|
||||||
from planetmint.utils import load_node_key
|
from planetmint.utils import load_node_key
|
||||||
from planetmint.transactions.common.transaction_mode_types import BROADCAST_TX_COMMIT
|
from transactions.common.transaction_mode_types import BROADCAST_TX_COMMIT
|
||||||
from planetmint.transactions.common.exceptions import DatabaseDoesNotExist, ValidationError
|
from transactions.common.exceptions import DatabaseDoesNotExist, ValidationError
|
||||||
from planetmint.transactions.types.elections.vote import Vote
|
from transactions.types.elections.vote import Vote
|
||||||
from planetmint.transactions.types.elections.chain_migration_election import ChainMigrationElection
|
from transactions.types.elections.chain_migration_election import ChainMigrationElection
|
||||||
from planetmint.transactions.types.elections.validator_utils import election_id_to_public_key
|
from transactions.types.elections.validator_utils import election_id_to_public_key
|
||||||
import planetmint
|
import planetmint
|
||||||
from planetmint import ValidatorElection, Planetmint
|
from planetmint import ValidatorElection, Planetmint
|
||||||
from planetmint.backend import schema
|
from planetmint.backend import schema
|
||||||
|
|||||||
@ -25,7 +25,7 @@ from functools import lru_cache
|
|||||||
from pkg_resources import iter_entry_points, ResolutionError
|
from pkg_resources import iter_entry_points, ResolutionError
|
||||||
|
|
||||||
from planetmint.config import Config
|
from planetmint.config import Config
|
||||||
from planetmint.transactions.common import exceptions
|
from transactions.common import exceptions
|
||||||
from planetmint.validation import BaseValidationRules
|
from planetmint.validation import BaseValidationRules
|
||||||
|
|
||||||
# TODO: move this to a proper configuration file for logging
|
# TODO: move this to a proper configuration file for logging
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
from planetmint.utils import condition_details_has_owner
|
from planetmint.utils import condition_details_has_owner
|
||||||
from planetmint.backend import query
|
from planetmint.backend import query
|
||||||
from planetmint.transactions.common.transaction import TransactionLink
|
from transactions.common.transaction import TransactionLink
|
||||||
|
|
||||||
|
|
||||||
class FastQuery:
|
class FastQuery:
|
||||||
|
|||||||
@ -17,37 +17,21 @@ import rapidjson
|
|||||||
from hashlib import sha3_256
|
from hashlib import sha3_256
|
||||||
|
|
||||||
import requests
|
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
|
import planetmint
|
||||||
from planetmint.config import Config
|
from planetmint.config import Config
|
||||||
from planetmint import backend, config_utils, fastquery
|
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.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 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
|
from planetmint.validation import BaseValidationRules
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
import planetmint
|
import planetmint
|
||||||
import logging
|
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 logging.config import dictConfig as set_logging_config
|
||||||
from planetmint.config import Config, DEFAULT_LOGGING_CONFIG
|
from planetmint.config import Config, DEFAULT_LOGGING_CONFIG
|
||||||
import os
|
import os
|
||||||
|
|||||||
@ -14,7 +14,7 @@ from tendermint.crypto import keys_pb2
|
|||||||
|
|
||||||
from hashlib import sha3_256
|
from hashlib import sha3_256
|
||||||
|
|
||||||
from planetmint.transactions.common.exceptions import InvalidPublicKey
|
from transactions.common.exceptions import InvalidPublicKey
|
||||||
|
|
||||||
def encode_validator(v):
|
def encode_validator(v):
|
||||||
ed25519_public_key = v["public_key"]["value"]
|
ed25519_public_key = v["public_key"]["value"]
|
||||||
|
|||||||
@ -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")
|
|
||||||
@ -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"""
|
|
||||||
@ -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)
|
|
||||||
@ -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
|
|
||||||
@ -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)
|
|
||||||
@ -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)
|
|
||||||
@ -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)
|
|
||||||
@ -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"
|
|
||||||
@ -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"
|
|
||||||
@ -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
|
|
||||||
@ -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
|
|
||||||
@ -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}$"
|
|
||||||
@ -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"
|
|
||||||
@ -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
|
|
||||||
@ -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}$"
|
|
||||||
@ -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}$"
|
|
||||||
@ -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
|
|
||||||
@ -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}$"
|
|
||||||
@ -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
|
|
||||||
@ -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
|
|
||||||
@ -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
|
|
||||||
@ -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}$"
|
|
||||||
@ -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)
|
|
||||||
@ -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)
|
|
||||||
@ -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"
|
|
||||||
@ -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"))
|
|
||||||
@ -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)
|
|
||||||
@ -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)
|
|
||||||
@ -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
|
|
||||||
@ -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)
|
|
||||||
@ -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"])
|
|
||||||
@ -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()
|
|
||||||
@ -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)
|
|
||||||
@ -13,7 +13,7 @@ import setproctitle
|
|||||||
from packaging import version
|
from packaging import version
|
||||||
from planetmint.version import __tm_supported_versions__
|
from planetmint.version import __tm_supported_versions__
|
||||||
from planetmint.tendermint_utils import key_from_base64
|
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):
|
class ProcessGroup(object):
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from planetmint.transactions.common.transaction_mode_types import (
|
from transactions.common.transaction_mode_types import (
|
||||||
BROADCAST_TX_COMMIT,
|
BROADCAST_TX_COMMIT,
|
||||||
BROADCAST_TX_ASYNC,
|
BROADCAST_TX_ASYNC,
|
||||||
BROADCAST_TX_SYNC,
|
BROADCAST_TX_SYNC,
|
||||||
|
|||||||
@ -12,14 +12,14 @@ import logging
|
|||||||
from flask import current_app, request, jsonify
|
from flask import current_app, request, jsonify
|
||||||
from flask_restful import Resource, reqparse
|
from flask_restful import Resource, reqparse
|
||||||
|
|
||||||
from planetmint.transactions.common.transaction_mode_types import BROADCAST_TX_ASYNC
|
from transactions.common.transaction_mode_types import BROADCAST_TX_ASYNC
|
||||||
from planetmint.transactions.common.exceptions import (
|
from transactions.common.exceptions import (
|
||||||
SchemaValidationError,
|
SchemaValidationError,
|
||||||
ValidationError,
|
ValidationError,
|
||||||
)
|
)
|
||||||
from planetmint.web.views.base import make_error
|
from planetmint.web.views.base import make_error
|
||||||
from planetmint.web.views import parameters
|
from planetmint.web.views import parameters
|
||||||
from planetmint.transactions.common.transaction import Transaction
|
from transactions.common.transaction import Transaction
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|||||||
7
setup.py
7
setup.py
@ -116,14 +116,14 @@ install_requires = [
|
|||||||
"flask-restful==0.3.9",
|
"flask-restful==0.3.9",
|
||||||
"flask==2.1.2",
|
"flask==2.1.2",
|
||||||
"gunicorn==20.1.0",
|
"gunicorn==20.1.0",
|
||||||
"jsonschema==3.2.0",
|
"jsonschema==4.16.0",
|
||||||
"logstats==0.3.0",
|
"logstats==0.3.0",
|
||||||
"packaging>=20.9",
|
"packaging>=20.9",
|
||||||
# TODO Consider not installing the db drivers, or putting them in extras.
|
# TODO Consider not installing the db drivers, or putting them in extras.
|
||||||
"pymongo==3.11.4",
|
"pymongo==3.11.4",
|
||||||
"tarantool==0.7.1",
|
"tarantool==0.7.1",
|
||||||
"python-rapidjson>=1.0",
|
"python-rapidjson>=1.0",
|
||||||
"pyyaml==5.4.1",
|
"pyyaml==6.0.0",
|
||||||
"requests==2.25.1",
|
"requests==2.25.1",
|
||||||
"setproctitle==1.2.2",
|
"setproctitle==1.2.2",
|
||||||
"werkzeug==2.0.3",
|
"werkzeug==2.0.3",
|
||||||
@ -136,6 +136,7 @@ install_requires = [
|
|||||||
"PyNaCl==1.4.0",
|
"PyNaCl==1.4.0",
|
||||||
"pyasn1>=0.4.8",
|
"pyasn1>=0.4.8",
|
||||||
"cryptography==3.4.7",
|
"cryptography==3.4.7",
|
||||||
|
"planetmint-transactions==0.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
@ -176,7 +177,7 @@ setup(
|
|||||||
"docs": docs_require,
|
"docs": docs_require,
|
||||||
},
|
},
|
||||||
package_data={
|
package_data={
|
||||||
"planetmint.transactions.common.schema": [
|
"transactions.common.schema": [
|
||||||
"v1.0/*.yaml",
|
"v1.0/*.yaml",
|
||||||
"v2.0/*.yaml",
|
"v2.0/*.yaml",
|
||||||
"v3.0/*.yaml",
|
"v3.0/*.yaml",
|
||||||
|
|||||||
@ -4,8 +4,8 @@
|
|||||||
# Code is Apache-2.0 and docs are CC-BY-4.0
|
# Code is Apache-2.0 and docs are CC-BY-4.0
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from planetmint.transactions.types.assets.create import Create
|
from transactions.types.assets.create import Create
|
||||||
from planetmint.transactions.types.assets.transfer import Transfer
|
from transactions.types.assets.transfer import Transfer
|
||||||
|
|
||||||
|
|
||||||
def test_asset_transfer(b, signed_create_tx, user_pk, user_sk):
|
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):
|
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 = Transfer.generate(signed_create_tx.to_inputs(), [([user_pk], 1)], signed_create_tx.id)
|
||||||
tx_transfer.asset["id"] = "a" * 64
|
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):
|
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)])
|
tx_create = Create.generate([alice.public_key], [([user_pk], 1)])
|
||||||
assert Transaction.get_asset_id(tx_create) == tx_create.id
|
assert Transaction.get_asset_id(tx_create) == tx_create.id
|
||||||
|
|
||||||
|
|
||||||
def test_get_asset_id_transfer_transaction(b, signed_create_tx, user_pk):
|
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)
|
tx_transfer = Transfer.generate(signed_create_tx.to_inputs(), [([user_pk], 1)], signed_create_tx.id)
|
||||||
asset_id = Transaction.get_asset_id(tx_transfer)
|
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):
|
def test_asset_id_mismatch(alice, user_pk):
|
||||||
from planetmint.transactions.common.transaction import Transaction
|
from transactions.common.transaction import Transaction
|
||||||
from planetmint.transactions.common.exceptions import AssetIdMismatch
|
from transactions.common.exceptions import AssetIdMismatch
|
||||||
|
|
||||||
tx1 = Create.generate(
|
tx1 = Create.generate(
|
||||||
[alice.public_key], [([user_pk], 1)], metadata="QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4"
|
[alice.public_key], [([user_pk], 1)], metadata="QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4"
|
||||||
|
|||||||
@ -7,9 +7,9 @@
|
|||||||
import pytest
|
import pytest
|
||||||
import random
|
import random
|
||||||
|
|
||||||
from planetmint.transactions.types.assets.create import Create
|
from transactions.types.assets.create import Create
|
||||||
from planetmint.transactions.types.assets.transfer import Transfer
|
from transactions.types.assets.transfer import Transfer
|
||||||
from planetmint.transactions.common.exceptions import DoubleSpend
|
from transactions.common.exceptions import DoubleSpend
|
||||||
|
|
||||||
|
|
||||||
# CREATE divisible asset
|
# 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
|
# Multiple owners_before
|
||||||
# Output combinations already tested above
|
# Output combinations already tested above
|
||||||
def test_single_in_multiple_own_single_out_single_own_create(alice, b, user_pk, user_sk):
|
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(
|
tx = Create.generate(
|
||||||
[alice.public_key, user_pk],
|
[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 output
|
||||||
# Single owners_after
|
# Single owners_after
|
||||||
def test_single_in_multiple_own_single_out_single_own_transfer(alice, b, user_pk, user_sk):
|
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
|
# CREATE divisible asset
|
||||||
tx_create = Create.generate(
|
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 output
|
||||||
# Single owners_after
|
# Single owners_after
|
||||||
def test_multiple_in_multiple_own_single_out_single_own_transfer(alice, b, user_pk, user_sk):
|
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
|
# CREATE divisible asset
|
||||||
tx_create = Create.generate(
|
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 output
|
||||||
# Single owners_after
|
# Single owners_after
|
||||||
def test_muiltiple_in_mix_own_multiple_out_single_own_transfer(alice, b, user_pk, user_sk):
|
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
|
# CREATE divisible asset
|
||||||
tx_create = Create.generate(
|
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
|
# Mix: one output with a single owners_after, one output with multiple
|
||||||
# owners_after
|
# owners_after
|
||||||
def test_muiltiple_in_mix_own_multiple_out_mix_own_transfer(alice, b, user_pk, user_sk):
|
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
|
# CREATE divisible asset
|
||||||
tx_create = Create.generate(
|
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.
|
# inputs needs to match the amount being sent in the outputs.
|
||||||
# In other words `amount_in_inputs - amount_in_outputs == 0`
|
# In other words `amount_in_inputs - amount_in_outputs == 0`
|
||||||
def test_amount_error_transfer(alice, b, user_pk, user_sk):
|
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
|
# CREATE divisible asset
|
||||||
tx_create = Create.generate(
|
tx_create = Create.generate(
|
||||||
|
|||||||
@ -1,11 +1,9 @@
|
|||||||
import pytest
|
|
||||||
import json
|
import json
|
||||||
import base58
|
import base58
|
||||||
from hashlib import sha3_256
|
from hashlib import sha3_256
|
||||||
from zenroom import zencode_exec
|
from zenroom import zencode_exec
|
||||||
from cryptoconditions.types.ed25519 import Ed25519Sha256
|
|
||||||
from cryptoconditions.types.zenroom import ZenroomSha256
|
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
|
from ipld import multihash, marshal
|
||||||
|
|
||||||
CONDITION_SCRIPT = """Scenario 'ecdh': create the signature of an object
|
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()
|
shared_creation_txid = sha3_256(json_str_tx.encode()).hexdigest()
|
||||||
tx["id"] = shared_creation_txid
|
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.lib import Planetmint
|
||||||
from planetmint.transactions.common.exceptions import (
|
from transactions.common.exceptions import (
|
||||||
SchemaValidationError,
|
SchemaValidationError,
|
||||||
ValidationError,
|
ValidationError,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -4,8 +4,8 @@
|
|||||||
# # # Code is Apache-2.0 and docs are CC-BY-4.0
|
# # # Code is Apache-2.0 and docs are CC-BY-4.0
|
||||||
#
|
#
|
||||||
# from copy import deepcopy
|
# from copy import deepcopy
|
||||||
# from planetmint.transactions.types.assets.create import Create
|
# from transactions.types.assets.create import Create
|
||||||
# from planetmint.transactions.types.assets.transfer import Transfer
|
# from transactions.types.assets.transfer import Transfer
|
||||||
#
|
#
|
||||||
# # import pytest
|
# # import pytest
|
||||||
# # import pymongo
|
# # import pymongo
|
||||||
@ -238,7 +238,7 @@
|
|||||||
# @pytest.mark.skip
|
# @pytest.mark.skip
|
||||||
# def test_get_spending_transactions_multiple_inputs():
|
# def test_get_spending_transactions_multiple_inputs():
|
||||||
# from planetmint.backend import connect, query
|
# 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()
|
# conn = connect()
|
||||||
# (alice_sk, alice_pk) = generate_key_pair()
|
# (alice_sk, alice_pk) = generate_key_pair()
|
||||||
# (bob_sk, bob_pk) = generate_key_pair()
|
# (bob_sk, bob_pk) = generate_key_pair()
|
||||||
|
|||||||
@ -7,9 +7,9 @@ from copy import deepcopy
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import json
|
import json
|
||||||
from planetmint.transactions.common.transaction import Transaction
|
from transactions.common.transaction import Transaction
|
||||||
from planetmint.transactions.types.assets.create import Create
|
from transactions.types.assets.create import Create
|
||||||
from planetmint.transactions.types.assets.transfer import Transfer
|
from transactions.types.assets.transfer import Transfer
|
||||||
|
|
||||||
pytestmark = pytest.mark.bdb
|
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):
|
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
|
from planetmint.backend.tarantool import query
|
||||||
|
|
||||||
(alice_sk, alice_pk) = generate_key_pair()
|
(alice_sk, alice_pk) = generate_key_pair()
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import pytest
|
|||||||
|
|
||||||
|
|
||||||
def test_get_connection_raises_a_configuration_error(monkeypatch):
|
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
|
from planetmint.backend.connection import connect
|
||||||
|
|
||||||
with pytest.raises(ConfigurationError):
|
with pytest.raises(ConfigurationError):
|
||||||
|
|||||||
@ -39,7 +39,7 @@ def mock_processes_start(monkeypatch):
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def mock_generate_key_pair(monkeypatch):
|
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
|
@pytest.fixture
|
||||||
|
|||||||
@ -14,9 +14,8 @@ import pytest
|
|||||||
from planetmint.config import Config
|
from planetmint.config import Config
|
||||||
from planetmint import ValidatorElection
|
from planetmint import ValidatorElection
|
||||||
from planetmint.commands.planetmint import run_election_show
|
from planetmint.commands.planetmint import run_election_show
|
||||||
from planetmint.transactions.types.elections.election import Election
|
|
||||||
from planetmint.lib import Block
|
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
|
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")
|
@patch("planetmint.backend.schema.drop_database")
|
||||||
def test_drop_db_when_db_does_not_exist(mock_db_drop, capsys):
|
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
|
from planetmint.commands.planetmint import run_drop
|
||||||
|
|
||||||
args = Namespace(config=None, yes=True)
|
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
|
@pytest.mark.bdb
|
||||||
def test_run_recover(b, alice, bob):
|
def test_run_recover(b, alice, bob):
|
||||||
from planetmint.commands.planetmint import run_recover
|
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.lib import Block
|
||||||
from planetmint.backend import query
|
from planetmint.backend import query
|
||||||
|
|
||||||
@ -408,7 +407,7 @@ def test_election_new_upsert_validator_invalid_election(caplog, b, priv_validato
|
|||||||
@pytest.mark.bdb
|
@pytest.mark.bdb
|
||||||
def test_election_new_upsert_validator_invalid_power(caplog, b, priv_validator_path, user_sk):
|
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.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):
|
def mock_write(tx, mode):
|
||||||
b.store_bulk_transactions([tx])
|
b.store_bulk_transactions([tx])
|
||||||
|
|||||||
@ -89,35 +89,35 @@ def user2_Ed25519(user2_pub):
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def user_input(user_Ed25519, user_pub):
|
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])
|
return Input(user_Ed25519, [user_pub])
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def user_user2_threshold_output(user_user2_threshold, user_pub, user2_pub):
|
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])
|
return Output(user_user2_threshold, [user_pub, user2_pub])
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def user_user2_threshold_input(user_user2_threshold, user_pub, user2_pub):
|
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])
|
return Input(user_user2_threshold, [user_pub, user2_pub])
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def user_output(user_Ed25519, user_pub):
|
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])
|
return Output(user_Ed25519, [user_pub])
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def user2_output(user2_Ed25519, user2_pub):
|
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])
|
return Output(user2_Ed25519, [user2_pub])
|
||||||
|
|
||||||
@ -134,7 +134,7 @@ def data():
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def utx(user_input, user_output):
|
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])
|
return Transaction(Transaction.CREATE, {"data": None}, [user_input], [user_output])
|
||||||
|
|
||||||
@ -146,7 +146,7 @@ def tx(utx, user_priv):
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def transfer_utx(user_output, user2_output, utx):
|
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()
|
user_output = user_output.to_dict()
|
||||||
input = Input(utx.outputs[0].fulfillment, user_output["public_keys"], TransactionLink(utx.id, 0))
|
input = Input(utx.outputs[0].fulfillment, user_output["public_keys"], TransactionLink(utx.id, 0))
|
||||||
|
|||||||
@ -6,10 +6,10 @@
|
|||||||
import pytest
|
import pytest
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
from planetmint.transactions.common.transaction import Transaction
|
from transactions.common.transaction import Transaction
|
||||||
from planetmint.transactions.types.assets.create import Create
|
from transactions.types.assets.create import Create
|
||||||
from planetmint.transactions.common.crypto import generate_key_pair
|
from transactions.common.crypto import generate_key_pair
|
||||||
from planetmint.transactions.common.memoize import to_dict, from_dict
|
from transactions.common.memoize import to_dict, from_dict
|
||||||
|
|
||||||
|
|
||||||
pytestmark = pytest.mark.bdb
|
pytestmark = pytest.mark.bdb
|
||||||
|
|||||||
@ -13,8 +13,8 @@ from hypothesis import given
|
|||||||
from hypothesis.strategies import from_regex as regex
|
from hypothesis.strategies import from_regex as regex
|
||||||
from pytest import raises
|
from pytest import raises
|
||||||
|
|
||||||
from planetmint.transactions.common.exceptions import SchemaValidationError
|
from transactions.common.exceptions import SchemaValidationError
|
||||||
from planetmint.transactions.common.schema import (
|
from transactions.common.schema import (
|
||||||
TX_SCHEMA_COMMON,
|
TX_SCHEMA_COMMON,
|
||||||
validate_transaction_schema,
|
validate_transaction_schema,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -10,13 +10,13 @@ import json
|
|||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
from base58 import b58encode, b58decode
|
from base58 import b58encode, b58decode
|
||||||
from planetmint.transactions.types.assets.create import Create
|
from transactions.types.assets.create import Create
|
||||||
from planetmint.transactions.types.assets.transfer import Transfer
|
from transactions.types.assets.transfer import Transfer
|
||||||
from planetmint.transactions.common.transaction import Output
|
from transactions.common.transaction import Output
|
||||||
from planetmint.transactions.common.transaction import Input
|
from transactions.common.transaction import Input
|
||||||
from planetmint.transactions.common.exceptions import AmountError
|
from transactions.common.exceptions import AmountError
|
||||||
from planetmint.transactions.common.transaction import Transaction
|
from transactions.common.transaction import Transaction
|
||||||
from planetmint.transactions.common.transaction import TransactionLink
|
from transactions.common.transaction import TransactionLink
|
||||||
from cryptoconditions import ThresholdSha256
|
from cryptoconditions import ThresholdSha256
|
||||||
from cryptoconditions import Fulfillment
|
from cryptoconditions import Fulfillment
|
||||||
from cryptoconditions import PreimageSha256
|
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.")
|
@mark.skip(reason="None is tolerated because it is None before fulfilling.")
|
||||||
def test_input_deserialization_with_invalid_input(user_pub):
|
def test_input_deserialization_with_invalid_input(user_pub):
|
||||||
from planetmint.transactions.common.transaction import Input
|
from transactions.common.transaction import Input
|
||||||
|
|
||||||
ffill = {
|
ffill = {
|
||||||
"owners_before": [user_pub],
|
"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):
|
def test_input_deserialization_with_invalid_fulfillment_uri(user_pub):
|
||||||
from planetmint.transactions.common.exceptions import InvalidSignature
|
from transactions.common.exceptions import InvalidSignature
|
||||||
from planetmint.transactions.common.transaction import Input
|
from transactions.common.transaction import Input
|
||||||
|
|
||||||
ffill = {
|
ffill = {
|
||||||
"owners_before": [user_pub],
|
"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):
|
def test_output_serialization(user_Ed25519, user_pub):
|
||||||
from planetmint.transactions.common.transaction import Output
|
from transactions.common.transaction import Output
|
||||||
|
|
||||||
expected = {
|
expected = {
|
||||||
"condition": {
|
"condition": {
|
||||||
@ -109,7 +109,7 @@ def test_output_serialization(user_Ed25519, user_pub):
|
|||||||
|
|
||||||
|
|
||||||
def test_output_deserialization(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)
|
expected = Output(user_Ed25519, [user_pub], 1)
|
||||||
cond = {
|
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):
|
def test_generate_output_invalid_parameters(user_pub, user2_pub, user3_pub):
|
||||||
from planetmint.transactions.common.transaction import Output
|
from transactions.common.transaction import Output
|
||||||
from planetmint.transactions.common.exceptions import AmountError
|
from transactions.common.exceptions import AmountError
|
||||||
|
|
||||||
with raises(ValueError):
|
with raises(ValueError):
|
||||||
Output.generate([], 1)
|
Output.generate([], 1)
|
||||||
@ -298,7 +298,7 @@ def test_transaction_deserialization(tri_state_transaction):
|
|||||||
|
|
||||||
|
|
||||||
def test_invalid_input_initialization(user_input, user_pub):
|
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):
|
with raises(TypeError):
|
||||||
Input(user_input, user_pub)
|
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):
|
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):
|
with raises(KeypairMismatchException):
|
||||||
invalid_key_pair = {"wrong_pub_key": "wrong_priv_key"}
|
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):
|
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):
|
with raises(KeypairMismatchException):
|
||||||
utx._sign_threshold_signature_fulfillment(user_user2_threshold_input, "somemessage", {user3_pub: user3_priv})
|
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):
|
def test_output_from_dict_invalid_amount(user_output):
|
||||||
from planetmint.transactions.common.transaction import Output
|
from transactions.common.transaction import Output
|
||||||
from planetmint.transactions.common.exceptions import AmountError
|
from transactions.common.exceptions import AmountError
|
||||||
|
|
||||||
out = user_output.to_dict()
|
out = user_output.to_dict()
|
||||||
out["amount"] = "a"
|
out["amount"] = "a"
|
||||||
|
|||||||
@ -5,8 +5,8 @@
|
|||||||
|
|
||||||
|
|
||||||
def validate_transaction_model(tx):
|
def validate_transaction_model(tx):
|
||||||
from planetmint.transactions.common.transaction import Transaction
|
from transactions.common.transaction import Transaction
|
||||||
from planetmint.transactions.common.schema import validate_transaction_schema
|
from transactions.common.schema import validate_transaction_schema
|
||||||
|
|
||||||
tx_dict = tx.to_dict()
|
tx_dict = tx.to_dict()
|
||||||
# Check that a transaction is valid by re-serializing it
|
# Check that a transaction is valid by re-serializing it
|
||||||
|
|||||||
@ -11,7 +11,6 @@ Tasks:
|
|||||||
"""
|
"""
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import copy
|
|
||||||
import random
|
import random
|
||||||
import tempfile
|
import tempfile
|
||||||
import codecs
|
import codecs
|
||||||
@ -26,16 +25,16 @@ import pytest
|
|||||||
|
|
||||||
# from pymongo import MongoClient
|
# from pymongo import MongoClient
|
||||||
|
|
||||||
from planetmint.transactions.common import crypto
|
from transactions.common import crypto
|
||||||
from planetmint.transactions.common.transaction_mode_types import BROADCAST_TX_COMMIT
|
from transactions.common.transaction_mode_types import BROADCAST_TX_COMMIT
|
||||||
from planetmint.tendermint_utils import key_from_base64
|
from planetmint.tendermint_utils import key_from_base64
|
||||||
from planetmint.backend import schema, query
|
from planetmint.backend import schema, query
|
||||||
from planetmint.transactions.common.crypto import key_pair_from_ed25519_key, public_key_from_ed25519_key
|
from transactions.common.crypto import key_pair_from_ed25519_key, public_key_from_ed25519_key
|
||||||
from planetmint.transactions.common.exceptions import DatabaseDoesNotExist
|
from transactions.common.exceptions import DatabaseDoesNotExist
|
||||||
from planetmint.lib import Block
|
from planetmint.lib import Block
|
||||||
from tests.utils import gen_vote
|
from tests.utils import gen_vote
|
||||||
from planetmint.config import Config
|
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.abci import types_pb2 as types
|
||||||
from tendermint.crypto import keys_pb2
|
from tendermint.crypto import keys_pb2
|
||||||
@ -140,8 +139,8 @@ def _setup_database(_configure_planetmint): # TODO Here is located setup databa
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def _bdb(_setup_database, _configure_planetmint):
|
def _bdb(_setup_database, _configure_planetmint):
|
||||||
from planetmint.transactions.common.memoize import to_dict, from_dict
|
from transactions.common.memoize import to_dict, from_dict
|
||||||
from planetmint.transactions.common.transaction import Transaction
|
from transactions.common.transaction import Transaction
|
||||||
from .utils import flush_db
|
from .utils import flush_db
|
||||||
from planetmint.config import Config
|
from planetmint.config import Config
|
||||||
|
|
||||||
@ -198,14 +197,14 @@ def user2_pk():
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def alice():
|
def alice():
|
||||||
from planetmint.transactions.common.crypto import generate_key_pair
|
from transactions.common.crypto import generate_key_pair
|
||||||
|
|
||||||
return generate_key_pair()
|
return generate_key_pair()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def bob():
|
def bob():
|
||||||
from planetmint.transactions.common.crypto import generate_key_pair
|
from transactions.common.crypto import generate_key_pair
|
||||||
|
|
||||||
return generate_key_pair()
|
return generate_key_pair()
|
||||||
|
|
||||||
@ -222,7 +221,7 @@ def bob_pubkey(carol):
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def carol():
|
def carol():
|
||||||
from planetmint.transactions.common.crypto import generate_key_pair
|
from transactions.common.crypto import generate_key_pair
|
||||||
|
|
||||||
return generate_key_pair()
|
return generate_key_pair()
|
||||||
|
|
||||||
@ -239,7 +238,7 @@ def carol_pubkey(carol):
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def merlin():
|
def merlin():
|
||||||
from planetmint.transactions.common.crypto import generate_key_pair
|
from transactions.common.crypto import generate_key_pair
|
||||||
|
|
||||||
return generate_key_pair()
|
return generate_key_pair()
|
||||||
|
|
||||||
@ -284,7 +283,7 @@ def mock_get_validators(network_validators):
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def create_tx(alice, user_pk):
|
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()}."
|
name = f"I am created by the create_tx fixture. My random identifier is {random.random()}."
|
||||||
asset = {"data": multihash(marshal({"name": name}))}
|
asset = {"data": multihash(marshal({"name": name}))}
|
||||||
@ -305,7 +304,7 @@ def posted_create_tx(b, signed_create_tx):
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def signed_transfer_tx(signed_create_tx, user_pk, user_sk):
|
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()
|
inputs = signed_create_tx.to_inputs()
|
||||||
tx = Transfer.generate(inputs, [([user_pk], 1)], asset_id=signed_create_tx.id)
|
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
|
@pytest.fixture
|
||||||
def double_spend_tx(signed_create_tx, carol_pubkey, user_sk):
|
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()
|
inputs = signed_create_tx.to_inputs()
|
||||||
tx = Transfer.generate(inputs, [([carol_pubkey], 1)], asset_id=signed_create_tx.id)
|
tx = Transfer.generate(inputs, [([carol_pubkey], 1)], asset_id=signed_create_tx.id)
|
||||||
@ -328,7 +327,7 @@ def _get_height(b):
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def inputs(user_pk, b, alice):
|
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
|
# create blocks with transactions for `USER` to spend
|
||||||
for height in range(1, 4):
|
for height in range(1, 4):
|
||||||
|
|||||||
@ -4,8 +4,8 @@
|
|||||||
# Code is Apache-2.0 and docs are CC-BY-4.0
|
# Code is Apache-2.0 and docs are CC-BY-4.0
|
||||||
import warnings
|
import warnings
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
from planetmint.transactions.types.assets.create import Create
|
from transactions.types.assets.create import Create
|
||||||
from planetmint.transactions.types.assets.transfer import Transfer
|
from transactions.types.assets.transfer import Transfer
|
||||||
from ipld import marshal, multihash
|
from ipld import marshal, multihash
|
||||||
import pytest
|
import pytest
|
||||||
from base58 import b58decode
|
from base58 import b58decode
|
||||||
@ -16,7 +16,7 @@ pytestmark = pytest.mark.bdb
|
|||||||
|
|
||||||
class TestBigchainApi(object):
|
class TestBigchainApi(object):
|
||||||
def test_get_spent_with_double_spend_detected(self, b, alice):
|
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
|
from planetmint.exceptions import CriticalDoubleSpend
|
||||||
|
|
||||||
tx = Create.generate([alice.public_key], [([alice.public_key], 1)])
|
tx = Create.generate([alice.public_key], [([alice.public_key], 1)])
|
||||||
@ -85,8 +85,8 @@ class TestBigchainApi(object):
|
|||||||
@pytest.mark.usefixtures("inputs")
|
@pytest.mark.usefixtures("inputs")
|
||||||
def test_non_create_input_not_found(self, b, user_pk):
|
def test_non_create_input_not_found(self, b, user_pk):
|
||||||
from cryptoconditions import Ed25519Sha256
|
from cryptoconditions import Ed25519Sha256
|
||||||
from planetmint.transactions.common.exceptions import InputDoesNotExist
|
from transactions.common.exceptions import InputDoesNotExist
|
||||||
from planetmint.transactions.common.transaction import Input, TransactionLink
|
from transactions.common.transaction import Input, TransactionLink
|
||||||
|
|
||||||
# Create an input for a non existing transaction
|
# Create an input for a non existing transaction
|
||||||
input = Input(
|
input = Input(
|
||||||
@ -116,8 +116,8 @@ class TestBigchainApi(object):
|
|||||||
|
|
||||||
class TestTransactionValidation(object):
|
class TestTransactionValidation(object):
|
||||||
def test_non_create_input_not_found(self, b, signed_transfer_tx):
|
def test_non_create_input_not_found(self, b, signed_transfer_tx):
|
||||||
from planetmint.transactions.common.exceptions import InputDoesNotExist
|
from transactions.common.exceptions import InputDoesNotExist
|
||||||
from planetmint.transactions.common.transaction import TransactionLink
|
from transactions.common.transaction import TransactionLink
|
||||||
|
|
||||||
signed_transfer_tx.inputs[0].fulfills = TransactionLink("c", 0)
|
signed_transfer_tx.inputs[0].fulfills = TransactionLink("c", 0)
|
||||||
with pytest.raises(InputDoesNotExist):
|
with pytest.raises(InputDoesNotExist):
|
||||||
@ -125,8 +125,8 @@ class TestTransactionValidation(object):
|
|||||||
|
|
||||||
@pytest.mark.usefixtures("inputs")
|
@pytest.mark.usefixtures("inputs")
|
||||||
def test_non_create_valid_input_wrong_owner(self, b, user_pk):
|
def test_non_create_valid_input_wrong_owner(self, b, user_pk):
|
||||||
from planetmint.transactions.common.crypto import generate_key_pair
|
from transactions.common.crypto import generate_key_pair
|
||||||
from planetmint.transactions.common.exceptions import InvalidSignature
|
from transactions.common.exceptions import InvalidSignature
|
||||||
|
|
||||||
input_tx = b.fastquery.get_outputs_by_public_key(user_pk).pop()
|
input_tx = b.fastquery.get_outputs_by_public_key(user_pk).pop()
|
||||||
input_transaction = b.get_transaction(input_tx.txid)
|
input_transaction = b.get_transaction(input_tx.txid)
|
||||||
@ -141,7 +141,7 @@ class TestTransactionValidation(object):
|
|||||||
|
|
||||||
@pytest.mark.usefixtures("inputs")
|
@pytest.mark.usefixtures("inputs")
|
||||||
def test_non_create_double_spend(self, b, signed_create_tx, signed_transfer_tx, double_spend_tx):
|
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])
|
b.store_bulk_transactions([signed_create_tx, signed_transfer_tx])
|
||||||
|
|
||||||
@ -151,7 +151,7 @@ class TestTransactionValidation(object):
|
|||||||
|
|
||||||
class TestMultipleInputs(object):
|
class TestMultipleInputs(object):
|
||||||
def test_transfer_single_owner_single_input(self, b, inputs, user_pk, user_sk):
|
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()
|
user2_sk, user2_pk = crypto.generate_key_pair()
|
||||||
|
|
||||||
@ -167,7 +167,7 @@ class TestMultipleInputs(object):
|
|||||||
assert len(tx.outputs) == 1
|
assert len(tx.outputs) == 1
|
||||||
|
|
||||||
def test_single_owner_before_multiple_owners_after_single_input(self, b, user_sk, user_pk, inputs):
|
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()
|
user2_sk, user2_pk = crypto.generate_key_pair()
|
||||||
user3_sk, user3_pk = crypto.generate_key_pair()
|
user3_sk, user3_pk = crypto.generate_key_pair()
|
||||||
@ -183,7 +183,7 @@ class TestMultipleInputs(object):
|
|||||||
|
|
||||||
@pytest.mark.usefixtures("inputs")
|
@pytest.mark.usefixtures("inputs")
|
||||||
def test_multiple_owners_before_single_owner_after_single_input(self, b, user_sk, user_pk, alice):
|
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()
|
user2_sk, user2_pk = crypto.generate_key_pair()
|
||||||
user3_sk, user3_pk = crypto.generate_key_pair()
|
user3_sk, user3_pk = crypto.generate_key_pair()
|
||||||
@ -206,7 +206,7 @@ class TestMultipleInputs(object):
|
|||||||
|
|
||||||
@pytest.mark.usefixtures("inputs")
|
@pytest.mark.usefixtures("inputs")
|
||||||
def test_multiple_owners_before_multiple_owners_after_single_input(self, b, user_sk, user_pk, alice):
|
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()
|
user2_sk, user2_pk = crypto.generate_key_pair()
|
||||||
user3_sk, user3_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
|
assert len(tx.outputs) == 1
|
||||||
|
|
||||||
def test_get_owned_ids_single_tx_single_output(self, b, user_sk, user_pk, alice):
|
def test_get_owned_ids_single_tx_single_output(self, b, user_sk, user_pk, alice):
|
||||||
from planetmint.transactions.common import crypto
|
from transactions.common import crypto
|
||||||
from planetmint.transactions.common.transaction import TransactionLink
|
from transactions.common.transaction import TransactionLink
|
||||||
|
|
||||||
user2_sk, user2_pk = crypto.generate_key_pair()
|
user2_sk, user2_pk = crypto.generate_key_pair()
|
||||||
|
|
||||||
@ -253,8 +253,8 @@ class TestMultipleInputs(object):
|
|||||||
assert owned_inputs_user2 == [TransactionLink(tx_transfer.id, 0)]
|
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):
|
def test_get_owned_ids_single_tx_multiple_outputs(self, b, user_sk, user_pk, alice):
|
||||||
from planetmint.transactions.common import crypto
|
from transactions.common import crypto
|
||||||
from planetmint.transactions.common.transaction import TransactionLink
|
from transactions.common.transaction import TransactionLink
|
||||||
|
|
||||||
user2_sk, user2_pk = crypto.generate_key_pair()
|
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)]
|
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):
|
def test_get_owned_ids_multiple_owners(self, b, user_sk, user_pk, alice):
|
||||||
from planetmint.transactions.common import crypto
|
from transactions.common import crypto
|
||||||
from planetmint.transactions.common.transaction import TransactionLink
|
from transactions.common.transaction import TransactionLink
|
||||||
|
|
||||||
user2_sk, user2_pk = crypto.generate_key_pair()
|
user2_sk, user2_pk = crypto.generate_key_pair()
|
||||||
user3_sk, user3_pk = crypto.generate_key_pair()
|
user3_sk, user3_pk = crypto.generate_key_pair()
|
||||||
@ -314,7 +314,7 @@ class TestMultipleInputs(object):
|
|||||||
assert not spent_user1
|
assert not spent_user1
|
||||||
|
|
||||||
def test_get_spent_single_tx_single_output(self, b, user_sk, user_pk, alice):
|
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()
|
user2_sk, user2_pk = crypto.generate_key_pair()
|
||||||
|
|
||||||
@ -338,7 +338,7 @@ class TestMultipleInputs(object):
|
|||||||
assert spent_inputs_user1 == tx
|
assert spent_inputs_user1 == tx
|
||||||
|
|
||||||
def test_get_spent_single_tx_multiple_outputs(self, b, user_sk, user_pk, alice):
|
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
|
# create a new users
|
||||||
user2_sk, user2_pk = crypto.generate_key_pair()
|
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
|
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):
|
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()
|
user2_sk, user2_pk = crypto.generate_key_pair()
|
||||||
user3_sk, user3_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():
|
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
|
from planetmint.lib import Planetmint
|
||||||
|
|
||||||
go = "planetmint.fastquery.FastQuery.get_outputs_by_public_key"
|
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():
|
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
|
from planetmint.lib import Planetmint
|
||||||
|
|
||||||
go = "planetmint.fastquery.FastQuery.get_outputs_by_public_key"
|
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_unspent_outputs")
|
||||||
@patch("planetmint.fastquery.FastQuery.filter_spent_outputs")
|
@patch("planetmint.fastquery.FastQuery.filter_spent_outputs")
|
||||||
def test_get_outputs_filtered(filter_spent, filter_unspent):
|
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
|
from planetmint.lib import Planetmint
|
||||||
|
|
||||||
go = "planetmint.fastquery.FastQuery.get_outputs_by_public_key"
|
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
|
"""Recreate duplicated fulfillments bug
|
||||||
https://github.com/planetmint/planetmint/issues/1099
|
https://github.com/planetmint/planetmint/issues/1099
|
||||||
"""
|
"""
|
||||||
from planetmint.transactions.common.exceptions import DoubleSpend
|
from transactions.common.exceptions import DoubleSpend
|
||||||
|
|
||||||
# create a divisible asset
|
# create a divisible asset
|
||||||
tx_create = Create.generate([alice.public_key], [([alice.public_key], 100)])
|
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):
|
def test_transaction_unicode(b, alice):
|
||||||
import copy
|
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
|
# http://www.fileformat.info/info/unicode/char/1f37a/index.htm
|
||||||
|
|
||||||
|
|||||||
@ -3,9 +3,9 @@ import pytest
|
|||||||
from tests.utils import generate_election, generate_validators
|
from tests.utils import generate_election, generate_validators
|
||||||
|
|
||||||
from planetmint.lib import Block
|
from planetmint.lib import Block
|
||||||
from planetmint.transactions.types.elections.election import Election
|
from transactions.types.elections.election import Election
|
||||||
from planetmint.transactions.types.elections.chain_migration_election import ChainMigrationElection
|
from transactions.types.elections.chain_migration_election import ChainMigrationElection
|
||||||
from planetmint.transactions.types.elections.validator_election import ValidatorElection
|
from transactions.types.elections.validator_election import ValidatorElection
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.bdb
|
@pytest.mark.bdb
|
||||||
|
|||||||
@ -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):
|
def test_valid_migration_election(b_mock, node_key):
|
||||||
|
|||||||
@ -4,28 +4,27 @@
|
|||||||
# Code is Apache-2.0 and docs are CC-BY-4.0
|
# Code is Apache-2.0 and docs are CC-BY-4.0
|
||||||
|
|
||||||
import json
|
import json
|
||||||
from planetmint.transactions.types.assets.create import Create
|
|
||||||
from planetmint.transactions.types.assets.transfer import Transfer
|
|
||||||
import pytest
|
|
||||||
import random
|
import random
|
||||||
|
import multiprocessing as mp
|
||||||
|
|
||||||
|
import pytest
|
||||||
from tendermint.abci import types_pb2 as types
|
from tendermint.abci import types_pb2 as types
|
||||||
from tendermint.crypto import keys_pb2
|
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 import App
|
||||||
from planetmint.backend import query
|
from planetmint.backend import query
|
||||||
from planetmint.transactions.common.crypto import generate_key_pair
|
|
||||||
from planetmint.core import OkCode, CodeTypeError, rollback
|
from planetmint.core import OkCode, CodeTypeError, rollback
|
||||||
from planetmint.lib import Block
|
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 new_validator_set
|
||||||
from planetmint.tendermint_utils import public_key_to_base64
|
from planetmint.tendermint_utils import public_key_to_base64
|
||||||
from planetmint.version import __tm_supported_versions__
|
from planetmint.version import __tm_supported_versions__
|
||||||
|
|
||||||
from tests.utils import generate_election, generate_validators
|
from tests.utils import generate_election, generate_validators
|
||||||
|
|
||||||
|
|
||||||
pytestmark = pytest.mark.bdb
|
pytestmark = pytest.mark.bdb
|
||||||
|
|
||||||
|
|
||||||
@ -197,9 +196,6 @@ def test_info(b):
|
|||||||
|
|
||||||
|
|
||||||
def test_check_tx__signed_create_is_ok(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()
|
alice = generate_key_pair()
|
||||||
bob = 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):
|
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()
|
alice = generate_key_pair()
|
||||||
bob = 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):
|
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()
|
alice = generate_key_pair()
|
||||||
bob = generate_key_pair()
|
bob = generate_key_pair()
|
||||||
events = mp.Queue()
|
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):
|
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()
|
alice = generate_key_pair()
|
||||||
bob = 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):
|
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 = App(b)
|
||||||
app.init_chain(init_chain_request)
|
app.init_chain(init_chain_request)
|
||||||
|
|
||||||
|
|||||||
@ -5,9 +5,9 @@
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from planetmint.transactions.common.transaction import TransactionLink
|
from transactions.common.transaction import TransactionLink
|
||||||
from planetmint.transactions.types.assets.create import Create
|
from transactions.types.assets.create import Create
|
||||||
from planetmint.transactions.types.assets.transfer import Transfer
|
from transactions.types.assets.transfer import Transfer
|
||||||
|
|
||||||
pytestmark = pytest.mark.bdb
|
pytestmark = pytest.mark.bdb
|
||||||
|
|
||||||
|
|||||||
@ -4,8 +4,8 @@
|
|||||||
# Code is Apache-2.0 and docs are CC-BY-4.0
|
# Code is Apache-2.0 and docs are CC-BY-4.0
|
||||||
|
|
||||||
import codecs
|
import codecs
|
||||||
from planetmint.transactions.types.assets.create import Create
|
from transactions.types.assets.create import Create
|
||||||
from planetmint.transactions.types.assets.transfer import Transfer
|
from transactions.types.assets.transfer import Transfer
|
||||||
|
|
||||||
from tendermint.abci import types_pb2 as types
|
from tendermint.abci import types_pb2 as types
|
||||||
import json
|
import json
|
||||||
@ -15,7 +15,7 @@ import pytest
|
|||||||
from abci.server import ProtocolHandler
|
from abci.server import ProtocolHandler
|
||||||
from abci.utils import read_messages
|
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 planetmint.version import __tm_supported_versions__
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
@ -24,7 +24,7 @@ from io import BytesIO
|
|||||||
def test_app(b, eventqueue_fixture, init_chain_request):
|
def test_app(b, eventqueue_fixture, init_chain_request):
|
||||||
from planetmint import App
|
from planetmint import App
|
||||||
from planetmint.tendermint_utils import calculate_hash
|
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)
|
app = App(b, eventqueue_fixture)
|
||||||
p = ProtocolHandler(app)
|
p = ProtocolHandler(app)
|
||||||
@ -111,7 +111,7 @@ def test_app(b, eventqueue_fixture, init_chain_request):
|
|||||||
|
|
||||||
@pytest.mark.abci
|
@pytest.mark.abci
|
||||||
def test_post_transaction_responses(tendermint_ws_url, b):
|
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()
|
alice = generate_key_pair()
|
||||||
bob = generate_key_pair()
|
bob = generate_key_pair()
|
||||||
|
|||||||
@ -6,8 +6,8 @@
|
|||||||
from operator import index
|
from operator import index
|
||||||
import os
|
import os
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
from planetmint.transactions.types.assets.create import Create
|
from transactions.types.assets.create import Create
|
||||||
from planetmint.transactions.types.assets.transfer import Transfer
|
from transactions.types.assets.transfer import Transfer
|
||||||
|
|
||||||
from hashlib import sha3_256
|
from hashlib import sha3_256
|
||||||
|
|
||||||
@ -15,7 +15,7 @@ import pytest
|
|||||||
from pymongo import MongoClient
|
from pymongo import MongoClient
|
||||||
|
|
||||||
from planetmint import backend
|
from planetmint import backend
|
||||||
from planetmint.transactions.common.transaction_mode_types import (
|
from transactions.common.transaction_mode_types import (
|
||||||
BROADCAST_TX_COMMIT,
|
BROADCAST_TX_COMMIT,
|
||||||
BROADCAST_TX_ASYNC,
|
BROADCAST_TX_ASYNC,
|
||||||
BROADCAST_TX_SYNC,
|
BROADCAST_TX_SYNC,
|
||||||
@ -27,7 +27,7 @@ from ipld import marshal, multihash
|
|||||||
@pytest.mark.bdb
|
@pytest.mark.bdb
|
||||||
def test_asset_is_separated_from_transaciton(b):
|
def test_asset_is_separated_from_transaciton(b):
|
||||||
import copy
|
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
|
from planetmint.backend.tarantool.connection import TarantoolDBConnection
|
||||||
|
|
||||||
if isinstance(b.connection, TarantoolDBConnection):
|
if isinstance(b.connection, TarantoolDBConnection):
|
||||||
@ -92,7 +92,7 @@ def test_get_empty_block(_0, _1, b):
|
|||||||
|
|
||||||
|
|
||||||
def test_validation_error(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()
|
alice = generate_key_pair()
|
||||||
tx = Create.generate([alice.public_key], [([alice.public_key], 1)], asset=None).sign([alice.private_key]).to_dict()
|
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")
|
@patch("requests.post")
|
||||||
def test_write_and_post_transaction(mock_post, b):
|
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
|
from planetmint.tendermint_utils import encode_transaction
|
||||||
|
|
||||||
alice = generate_key_pair()
|
alice = generate_key_pair()
|
||||||
@ -122,7 +122,7 @@ def test_write_and_post_transaction(mock_post, b):
|
|||||||
@patch("requests.post")
|
@patch("requests.post")
|
||||||
@pytest.mark.parametrize("mode", [BROADCAST_TX_SYNC, BROADCAST_TX_ASYNC, BROADCAST_TX_COMMIT])
|
@pytest.mark.parametrize("mode", [BROADCAST_TX_SYNC, BROADCAST_TX_ASYNC, BROADCAST_TX_COMMIT])
|
||||||
def test_post_transaction_valid_modes(mock_post, b, mode):
|
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()
|
alice = generate_key_pair()
|
||||||
tx = Create.generate([alice.public_key], [([alice.public_key], 1)], asset=None).sign([alice.private_key]).to_dict()
|
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):
|
def test_post_transaction_invalid_mode(b):
|
||||||
from planetmint.transactions.common.crypto import generate_key_pair
|
from transactions.common.crypto import generate_key_pair
|
||||||
from planetmint.transactions.common.exceptions import ValidationError
|
from transactions.common.exceptions import ValidationError
|
||||||
|
|
||||||
alice = 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()
|
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
|
@pytest.mark.bdb
|
||||||
def test_get_spent_transaction_critical_double_spend(b, alice, bob, carol):
|
def test_get_spent_transaction_critical_double_spend(b, alice, bob, carol):
|
||||||
from planetmint.exceptions import CriticalDoubleSpend
|
from planetmint.exceptions import CriticalDoubleSpend
|
||||||
from planetmint.transactions.common.exceptions import DoubleSpend
|
from transactions.common.exceptions import DoubleSpend
|
||||||
|
|
||||||
asset = {"data": multihash(marshal({"test": "asset"}))}
|
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):
|
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()
|
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
|
@pytest.mark.bdb
|
||||||
def test_get_spent_key_order(b, user_pk, user_sk, user2_pk, user2_sk):
|
def test_get_spent_key_order(b, user_pk, user_sk, user2_pk, user2_sk):
|
||||||
from planetmint import backend
|
from planetmint import backend
|
||||||
from planetmint.transactions.common.crypto import generate_key_pair
|
from transactions.common.crypto import generate_key_pair
|
||||||
from planetmint.transactions.common.exceptions import DoubleSpend
|
from transactions.common.exceptions import DoubleSpend
|
||||||
|
|
||||||
alice = generate_key_pair()
|
alice = generate_key_pair()
|
||||||
bob = generate_key_pair()
|
bob = generate_key_pair()
|
||||||
|
|||||||
@ -289,7 +289,7 @@ def test_file_config():
|
|||||||
|
|
||||||
def test_invalid_file_config():
|
def test_invalid_file_config():
|
||||||
from planetmint.config_utils import 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 patch("builtins.open", mock_open(read_data="{_INVALID_JSON_}")):
|
||||||
with pytest.raises(exceptions.ConfigurationError):
|
with pytest.raises(exceptions.ConfigurationError):
|
||||||
|
|||||||
@ -5,8 +5,8 @@
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from planetmint.version import __tm_supported_versions__
|
from planetmint.version import __tm_supported_versions__
|
||||||
from planetmint.transactions.types.assets.create import Create
|
from transactions.types.assets.create import Create
|
||||||
from planetmint.transactions.types.assets.transfer import Transfer
|
from transactions.types.assets.transfer import Transfer
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|||||||
@ -5,9 +5,9 @@
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from planetmint.transactions.common.crypto import generate_key_pair
|
from transactions.common.crypto import generate_key_pair
|
||||||
from planetmint.transactions.types.assets.create import Create
|
from transactions.types.assets.create import Create
|
||||||
from planetmint.transactions.types.assets.transfer import Transfer
|
from transactions.types.assets.transfer import Transfer
|
||||||
|
|
||||||
pytestmark = pytest.mark.tendermint
|
pytestmark = pytest.mark.tendermint
|
||||||
|
|
||||||
|
|||||||
@ -12,8 +12,8 @@ import pytest
|
|||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def txlist(b, user_pk, user2_pk, user_sk, user2_sk):
|
def txlist(b, user_pk, user2_pk, user_sk, user2_sk):
|
||||||
from planetmint.transactions.types.assets.create import Create
|
from transactions.types.assets.create import Create
|
||||||
from planetmint.transactions.types.assets.transfer import Transfer
|
from transactions.types.assets.transfer import Transfer
|
||||||
|
|
||||||
# Create two CREATE transactions
|
# Create two CREATE transactions
|
||||||
create1 = Create.generate([user_pk], [([user2_pk], 6)]).sign([user_sk])
|
create1 = Create.generate([user_pk], [([user2_pk], 6)]).sign([user_sk])
|
||||||
|
|||||||
@ -7,7 +7,7 @@ from unittest.mock import patch
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from planetmint.backend import query
|
from planetmint.backend import query
|
||||||
from planetmint.transactions.types.elections.validator_election import ValidatorElection
|
from transactions.types.elections.validator_election import ValidatorElection
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@ -17,7 +17,7 @@ def valid_upsert_validator_election_b(b, node_key, new_validator):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@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):
|
def fixed_seed_election(b_mock, node_key, new_validator):
|
||||||
voters = b_mock.get_recipients_list()
|
voters = b_mock.get_recipients_list()
|
||||||
return ValidatorElection.generate([node_key.public_key], voters, new_validator, None).sign([node_key.private_key])
|
return ValidatorElection.generate([node_key.public_key], voters, new_validator, None).sign([node_key.private_key])
|
||||||
|
|||||||
@ -6,15 +6,14 @@
|
|||||||
import pytest
|
import pytest
|
||||||
import codecs
|
import codecs
|
||||||
|
|
||||||
from planetmint.transactions.types.elections.election import Election
|
|
||||||
from planetmint.tendermint_utils import public_key_to_base64
|
from planetmint.tendermint_utils import public_key_to_base64
|
||||||
from planetmint.transactions.types.elections.validator_election import ValidatorElection
|
from transactions.types.elections.validator_election import ValidatorElection
|
||||||
from planetmint.transactions.common.exceptions import AmountError
|
from transactions.common.exceptions import AmountError
|
||||||
from planetmint.transactions.common.crypto import generate_key_pair
|
from transactions.common.crypto import generate_key_pair
|
||||||
from planetmint.transactions.common.exceptions import ValidationError
|
from transactions.common.exceptions import ValidationError
|
||||||
from planetmint.transactions.common.transaction_mode_types import BROADCAST_TX_COMMIT
|
from transactions.common.transaction_mode_types import BROADCAST_TX_COMMIT
|
||||||
from planetmint.transactions.types.elections.vote import Vote
|
from transactions.types.elections.vote import Vote
|
||||||
from planetmint.transactions.types.elections.validator_utils import election_id_to_public_key
|
from transactions.types.elections.validator_utils import election_id_to_public_key
|
||||||
from tests.utils import generate_block, gen_vote
|
from tests.utils import generate_block, gen_vote
|
||||||
|
|
||||||
pytestmark = [pytest.mark.execute]
|
pytestmark = [pytest.mark.execute]
|
||||||
|
|||||||
@ -8,8 +8,8 @@ from unittest.mock import patch
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from planetmint.tendermint_utils import public_key_to_base64
|
from planetmint.tendermint_utils import public_key_to_base64
|
||||||
from planetmint.transactions.types.elections.validator_election import ValidatorElection
|
from transactions.types.elections.validator_election import ValidatorElection
|
||||||
from planetmint.transactions.common.exceptions import (
|
from transactions.common.exceptions import (
|
||||||
DuplicateTransaction,
|
DuplicateTransaction,
|
||||||
UnequalValidatorSet,
|
UnequalValidatorSet,
|
||||||
InvalidProposer,
|
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):
|
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"]:
|
for iv in ["ed25519-base32", "ed25519-base64"]:
|
||||||
new_validator["public_key"]["type"] = iv
|
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):
|
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()
|
alice = generate_key_pair()
|
||||||
voters = b_mock.get_recipients_list()
|
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):
|
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()
|
alice = generate_key_pair()
|
||||||
voters = b_mock.get_recipients_list()
|
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)
|
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):
|
def test_upsert_validator_invalid_election(b_mock, new_validator, node_key, fixed_seed_election):
|
||||||
voters = b_mock.get_recipients_list()
|
voters = b_mock.get_recipients_list()
|
||||||
duplicate_election = ValidatorElection.generate([node_key.public_key], voters, new_validator, None).sign(
|
duplicate_election = ValidatorElection.generate([node_key.public_key], voters, new_validator, None).sign(
|
||||||
|
|||||||
@ -12,11 +12,11 @@ from functools import singledispatch
|
|||||||
from planetmint.backend.localmongodb.connection import LocalMongoDBConnection
|
from planetmint.backend.localmongodb.connection import LocalMongoDBConnection
|
||||||
from planetmint.backend.tarantool.connection import TarantoolDBConnection
|
from planetmint.backend.tarantool.connection import TarantoolDBConnection
|
||||||
from planetmint.backend.schema import TABLES, SPACE_NAMES
|
from planetmint.backend.schema import TABLES, SPACE_NAMES
|
||||||
from planetmint.transactions.common import crypto
|
from transactions.common import crypto
|
||||||
from planetmint.transactions.common.transaction_mode_types import BROADCAST_TX_COMMIT
|
from transactions.common.transaction_mode_types import BROADCAST_TX_COMMIT
|
||||||
from planetmint.transactions.types.assets.create import Create
|
from transactions.types.assets.create import Create
|
||||||
from planetmint.transactions.types.elections.vote import Vote
|
from transactions.types.elections.vote import Vote
|
||||||
from planetmint.transactions.types.elections.validator_utils import election_id_to_public_key
|
from transactions.types.elections.validator_utils import election_id_to_public_key
|
||||||
from planetmint.tendermint_utils import key_to_base64
|
from planetmint.tendermint_utils import key_to_base64
|
||||||
|
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ def flush_tarantool_db(connection, dbname):
|
|||||||
|
|
||||||
|
|
||||||
def generate_block(planet):
|
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()
|
alice = generate_key_pair()
|
||||||
tx = Create.generate([alice.public_key], [([alice.public_key], 1)], asset=None).sign([alice.private_key])
|
tx = Create.generate([alice.public_key], [([alice.public_key], 1)], asset=None).sign([alice.private_key])
|
||||||
|
|||||||
@ -14,9 +14,9 @@ import pytest
|
|||||||
import hashlib as sha3
|
import hashlib as sha3
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
from planetmint.transactions.common.exceptions import AmountError, SchemaValidationError, ThresholdTooDeep
|
from transactions.common.exceptions import AmountError, SchemaValidationError, ThresholdTooDeep
|
||||||
from planetmint.transactions.common.transaction import Transaction
|
from transactions.common.transaction import Transaction
|
||||||
from planetmint.transactions.common.utils import _fulfillment_to_details, _fulfillment_from_details
|
from transactions.common.utils import _fulfillment_to_details, _fulfillment_from_details
|
||||||
from ipld import marshal, multihash
|
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):
|
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 = signed_create_tx.to_dict()
|
||||||
tx["id"] = "a" * 64
|
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):
|
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()
|
tx = signed_create_tx.to_dict()
|
||||||
del tx["id"]
|
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):
|
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()
|
tx = signed_transfer_tx.to_dict()
|
||||||
validate(tx)
|
validate(tx)
|
||||||
@ -152,7 +152,7 @@ def test_no_inputs(b, create_tx, alice):
|
|||||||
|
|
||||||
|
|
||||||
def test_create_single_input(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 = create_tx.to_dict()
|
||||||
tx["inputs"] += tx["inputs"]
|
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):
|
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 = create_tx.to_dict()
|
||||||
tx["inputs"][0]["fulfills"] = {"transaction_id": "a" * 64, "output_index": 0}
|
tx["inputs"][0]["fulfills"] = {"transaction_id": "a" * 64, "output_index": 0}
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
# Code is Apache-2.0 and docs are CC-BY-4.0
|
# Code is Apache-2.0 and docs are CC-BY-4.0
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from planetmint.transactions.types.assets.create import Create
|
from transactions.types.assets.create import Create
|
||||||
from ipld import marshal, multihash
|
from ipld import marshal, multihash
|
||||||
|
|
||||||
ASSETS_ENDPOINT = "/api/v1/assets/"
|
ASSETS_ENDPOINT = "/api/v1/assets/"
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from planetmint.transactions.types.assets.create import Create
|
from transactions.types.assets.create import Create
|
||||||
from planetmint.lib import Block
|
from planetmint.lib import Block
|
||||||
from ipld import marshal, multihash
|
from ipld import marshal, multihash
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
# Code is Apache-2.0 and docs are CC-BY-4.0
|
# Code is Apache-2.0 and docs are CC-BY-4.0
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from planetmint.transactions.types.assets.create import Create
|
from transactions.types.assets.create import Create
|
||||||
from ipld import marshal, multihash
|
from ipld import marshal, multihash
|
||||||
|
|
||||||
METADATA_ENDPOINT = "/api/v1/metadata/"
|
METADATA_ENDPOINT = "/api/v1/metadata/"
|
||||||
|
|||||||
@ -5,8 +5,8 @@
|
|||||||
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from planetmint.transactions.types.assets.create import Create
|
from transactions.types.assets.create import Create
|
||||||
from planetmint.transactions.types.assets.transfer import Transfer
|
from transactions.types.assets.transfer import Transfer
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
|
|
||||||
@ -83,7 +83,7 @@ def test_get_outputs_endpoint_with_invalid_spent(client, user_pk):
|
|||||||
|
|
||||||
@pytest.mark.abci
|
@pytest.mark.abci
|
||||||
def test_get_divisble_transactions_returns_500(b, client):
|
def test_get_divisble_transactions_returns_500(b, client):
|
||||||
from planetmint.transactions.common import crypto
|
from transactions.common import crypto
|
||||||
import json
|
import json
|
||||||
|
|
||||||
TX_ENDPOINT = "/api/v1/transactions"
|
TX_ENDPOINT = "/api/v1/transactions"
|
||||||
|
|||||||
@ -13,10 +13,10 @@ from ipld import multihash, marshal
|
|||||||
|
|
||||||
from hashlib import sha3_256
|
from hashlib import sha3_256
|
||||||
|
|
||||||
from planetmint.transactions.common import crypto
|
from transactions.common import crypto
|
||||||
from planetmint.transactions.types.assets.create import Create
|
from transactions.types.assets.create import Create
|
||||||
from planetmint.transactions.types.assets.transfer import Transfer
|
from transactions.types.assets.transfer import Transfer
|
||||||
from planetmint.transactions.common.transaction_mode_types import (
|
from transactions.common.transaction_mode_types import (
|
||||||
BROADCAST_TX_COMMIT,
|
BROADCAST_TX_COMMIT,
|
||||||
BROADCAST_TX_ASYNC,
|
BROADCAST_TX_ASYNC,
|
||||||
BROADCAST_TX_SYNC,
|
BROADCAST_TX_SYNC,
|
||||||
@ -161,7 +161,7 @@ def test_post_create_transaction_with_invalid_key(b, client, field, value, err_k
|
|||||||
@pytest.mark.abci
|
@pytest.mark.abci
|
||||||
@patch("planetmint.web.views.base.logger")
|
@patch("planetmint.web.views.base.logger")
|
||||||
def test_post_create_transaction_with_invalid_id(mock_logger, b, client):
|
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()
|
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
|
@pytest.mark.abci
|
||||||
@patch("planetmint.web.views.base.logger")
|
@patch("planetmint.web.views.base.logger")
|
||||||
def test_post_create_transaction_with_invalid_signature(mock_logger, b, client):
|
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()
|
user_priv, user_pub = crypto.generate_key_pair()
|
||||||
|
|
||||||
@ -300,7 +300,7 @@ def test_post_invalid_transaction(
|
|||||||
msg,
|
msg,
|
||||||
monkeypatch,
|
monkeypatch,
|
||||||
):
|
):
|
||||||
from planetmint.transactions.common import exceptions
|
from transactions.common import exceptions
|
||||||
|
|
||||||
exc_cls = getattr(exceptions, exc)
|
exc_cls = getattr(exceptions, exc)
|
||||||
|
|
||||||
@ -310,7 +310,7 @@ def test_post_invalid_transaction(
|
|||||||
TransactionMock = Mock(validate=mock_validation)
|
TransactionMock = Mock(validate=mock_validation)
|
||||||
|
|
||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
"planetmint.transactions.common.transaction.Transaction.from_dict",
|
"transactions.common.transaction.Transaction.from_dict",
|
||||||
lambda tx, skip_schema_validation: TransactionMock,
|
lambda tx, skip_schema_validation: TransactionMock,
|
||||||
)
|
)
|
||||||
res = client.post(TX_ENDPOINT, data=json.dumps({}))
|
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
|
@pytest.mark.abci
|
||||||
def test_post_invalid_transfer_transaction_returns_400(client, user_pk, posted_create_tx):
|
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 = Transfer.generate(posted_create_tx.to_inputs(), [([user_pk], 1)], asset_id=posted_create_tx.id)
|
||||||
transfer_tx._hash()
|
transfer_tx._hash()
|
||||||
@ -362,7 +362,7 @@ def test_post_invalid_transfer_transaction_returns_400(client, user_pk, posted_c
|
|||||||
|
|
||||||
@pytest.mark.abci
|
@pytest.mark.abci
|
||||||
def test_post_wrong_asset_division_transfer_returns_400(b, client, user_pk):
|
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()
|
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):
|
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):
|
def _mock_post(*args, **kwargs):
|
||||||
return Mock(json=Mock(return_value={"result": {"code": 0}}))
|
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
|
@pytest.mark.abci
|
||||||
def test_post_transaction_invalid_mode(client):
|
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()
|
alice = generate_key_pair()
|
||||||
tx = Create.generate([alice.public_key], [([alice.public_key], 1)], asset=None).sign([alice.private_key])
|
tx = Create.generate([alice.public_key], [([alice.public_key], 1)], asset=None).sign([alice.private_key])
|
||||||
|
|||||||
@ -9,8 +9,8 @@ import queue
|
|||||||
import threading
|
import threading
|
||||||
|
|
||||||
# from unittest.mock import patch
|
# from unittest.mock import patch
|
||||||
from planetmint.transactions.types.assets.create import Create
|
from transactions.types.assets.create import Create
|
||||||
from planetmint.transactions.types.assets.transfer import Transfer
|
from transactions.types.assets.transfer import Transfer
|
||||||
from ipld import multihash, marshal
|
from ipld import multihash, marshal
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@ -26,7 +26,7 @@ class MockWebSocket:
|
|||||||
|
|
||||||
def test_eventify_block_works_with_any_transaction():
|
def test_eventify_block_works_with_any_transaction():
|
||||||
from planetmint.web.websocket_dispatcher import Dispatcher
|
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()
|
alice = generate_key_pair()
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ def test_eventify_block_works_with_any_transaction():
|
|||||||
|
|
||||||
def test_simplified_block_works():
|
def test_simplified_block_works():
|
||||||
from planetmint.web.websocket_dispatcher import Dispatcher
|
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()
|
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):
|
async def test_websocket_block_event(aiohttp_client, event_loop):
|
||||||
from planetmint import events
|
from planetmint import events
|
||||||
from planetmint.web.websocket_server import init_app, EVENTS_ENDPOINT_BLOCKS
|
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()
|
user_priv, user_pub = crypto.generate_key_pair()
|
||||||
tx = Create.generate([user_pub], [([user_pub], 1)])
|
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):
|
async def test_websocket_transaction_event(aiohttp_client, event_loop):
|
||||||
from planetmint import events
|
from planetmint import events
|
||||||
from planetmint.web.websocket_server import init_app, EVENTS_ENDPOINT
|
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()
|
user_priv, user_pub = crypto.generate_key_pair()
|
||||||
tx = Create.generate([user_pub], [([user_pub], 1)])
|
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 random
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
|
||||||
from planetmint.transactions.common import crypto
|
from transactions.common import crypto
|
||||||
|
|
||||||
# TODO processes does not exist anymore, when reactivating this test it
|
# TODO processes does not exist anymore, when reactivating this test it
|
||||||
# will fail because of this
|
# will fail because of this
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user