added type hints to transactions module

Signed-off-by: Lorenz Herzberger <lorenzherzberger@gmail.com>
This commit is contained in:
Lorenz Herzberger 2022-09-27 16:46:44 +02:00
parent cddfe862ef
commit a7d302e94d
No known key found for this signature in database
GPG Key ID: FA5EE906EB55316A
10 changed files with 90 additions and 79 deletions

View File

@ -17,7 +17,7 @@ def hash_data(data):
return sha3_256(data.encode()).hexdigest() return sha3_256(data.encode()).hexdigest()
def generate_key_pair(): def generate_key_pair() -> CryptoKeypair:
"""Generates a cryptographic key pair. """Generates a cryptographic key pair.
Returns: Returns:

View File

@ -3,6 +3,7 @@
# 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 typing import Optional
from cryptoconditions import Fulfillment from cryptoconditions import Fulfillment
from cryptoconditions.exceptions import ASN1DecodeError, ASN1EncodeError from cryptoconditions.exceptions import ASN1DecodeError, ASN1EncodeError
@ -27,7 +28,7 @@ class Input(object):
Transaction. Transaction.
""" """
def __init__(self, fulfillment, owners_before, fulfills=None): def __init__(self, fulfillment: Fulfillment, owners_before: list[str], fulfills: Optional[TransactionLink] = None):
"""Create an instance of an :class:`~.Input`. """Create an instance of an :class:`~.Input`.
Args: Args:
@ -86,7 +87,7 @@ class Input(object):
return input_ return input_
@classmethod @classmethod
def generate(cls, public_keys): def generate(cls, public_keys: list[str]):
# TODO: write docstring # TODO: write docstring
# The amount here does not really matter. It is only use on the # 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 data model but here we only care about the fulfillment
@ -94,7 +95,7 @@ class Input(object):
return cls(output.fulfillment, public_keys) return cls(output.fulfillment, public_keys)
@classmethod @classmethod
def from_dict(cls, data): def from_dict(cls, data: dict):
"""Transforms a Python dictionary to an Input object. """Transforms a Python dictionary to an Input object.
Note: Note:

View File

@ -1,7 +1,7 @@
import functools import functools
import codecs import codecs
from functools import lru_cache from functools import lru_cache
from typing import Callable
class HDict(dict): class HDict(dict):
def __hash__(self): def __hash__(self):
@ -9,11 +9,11 @@ class HDict(dict):
@lru_cache(maxsize=16384) @lru_cache(maxsize=16384)
def from_dict(func, *args, **kwargs): def from_dict(func: Callable, *args, **kwargs):
return func(*args, **kwargs) return func(*args, **kwargs)
def memoize_from_dict(func): def memoize_from_dict(func: Callable):
@functools.wraps(func) @functools.wraps(func)
def memoized_func(*args, **kwargs): def memoized_func(*args, **kwargs):
if args[1] is None: if args[1] is None:
@ -45,7 +45,7 @@ def to_dict(func, tx_wrapped):
return func(tx_wrapped.tx) return func(tx_wrapped.tx)
def memoize_to_dict(func): def memoize_to_dict(func: Callable):
@functools.wraps(func) @functools.wraps(func)
def memoized_func(*args, **kwargs): def memoized_func(*args, **kwargs):

View File

@ -4,6 +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
from functools import reduce from functools import reduce
from typing import Union, Optional
import base58 import base58
from cryptoconditions import ThresholdSha256, Ed25519Sha256, ZenroomSha256 from cryptoconditions import ThresholdSha256, Ed25519Sha256, ZenroomSha256
@ -27,7 +28,7 @@ class Output(object):
MAX_AMOUNT = 9 * 10**18 MAX_AMOUNT = 9 * 10**18
def __init__(self, fulfillment, public_keys=None, amount=1): def __init__(self, fulfillment: type[Fulfillment], public_keys: Optional[list[str]] = None, amount: int = 1):
"""Create an instance of a :class:`~.Output`. """Create an instance of a :class:`~.Output`.
Args: Args:
@ -90,7 +91,7 @@ class Output(object):
return output return output
@classmethod @classmethod
def generate(cls, public_keys, amount): def generate(cls, public_keys: list[str], amount: int):
"""Generates a Output from a specifically formed tuple or list. """Generates a Output from a specifically formed tuple or list.
Note: Note:
@ -136,7 +137,7 @@ class Output(object):
return cls(threshold_cond, public_keys, amount=amount) return cls(threshold_cond, public_keys, amount=amount)
@classmethod @classmethod
def _gen_condition(cls, initial, new_public_keys): 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. """Generates ThresholdSha256 conditions from a list of new owners.
Note: Note:
@ -165,6 +166,7 @@ class Output(object):
raise ValueError("Sublist cannot contain single owner") raise ValueError("Sublist cannot contain single owner")
else: else:
try: try:
if isinstance(new_public_keys, list):
new_public_keys = new_public_keys.pop() new_public_keys = new_public_keys.pop()
except AttributeError: except AttributeError:
pass pass
@ -182,7 +184,7 @@ class Output(object):
return initial return initial
@classmethod @classmethod
def from_dict(cls, data): def from_dict(cls, data: dict):
"""Transforms a Python dictionary to an Output object. """Transforms a Python dictionary to an Output object.
Note: Note:

View File

@ -14,6 +14,7 @@ Attributes:
from collections import namedtuple from collections import namedtuple
from copy import deepcopy from copy import deepcopy
from functools import lru_cache from functools import lru_cache
from typing import Optional
import rapidjson import rapidjson
import base58 import base58
@ -83,16 +84,16 @@ class Transaction(object):
version (string): Defines the version number of a Transaction. version (string): Defines the version number of a Transaction.
""" """
CREATE = "CREATE" CREATE: str = "CREATE"
TRANSFER = "TRANSFER" TRANSFER: str = "TRANSFER"
VALIDATOR_ELECTION = VALIDATOR_ELECTION VALIDATOR_ELECTION: str = VALIDATOR_ELECTION
CHAIN_MIGRATION_ELECTION = CHAIN_MIGRATION_ELECTION CHAIN_MIGRATION_ELECTION: str = CHAIN_MIGRATION_ELECTION
VOTE = VOTE VOTE: str = VOTE
ALLOWED_OPERATIONS = (CREATE, TRANSFER) ALLOWED_OPERATIONS: tuple[str, ...] = (CREATE, TRANSFER)
ASSET = "asset" ASSET: str = "asset"
METADATA = "metadata" METADATA: str = "metadata"
DATA = "data" DATA: str = "data"
VERSION = "2.0" VERSION: str = "2.0"
def __init__( def __init__(
self, self,
@ -250,7 +251,7 @@ class Transaction(object):
return False return False
return self.to_dict() == other return self.to_dict() == other
def to_inputs(self, indices=None): def to_inputs(self, indices: Optional[list[int]] = None) -> list[Input]:
"""Converts a Transaction's outputs to spendable inputs. """Converts a Transaction's outputs to spendable inputs.
Note: Note:
@ -272,17 +273,17 @@ class Transaction(object):
""" """
# NOTE: If no indices are passed, we just assume to take all outputs # NOTE: If no indices are passed, we just assume to take all outputs
# as inputs. # as inputs.
indices = indices or range(len(self.outputs)) iterable_indices = indices or range(len(self.outputs))
return [ return [
Input( Input(
self.outputs[idx].fulfillment, self.outputs[idx].fulfillment,
self.outputs[idx].public_keys, self.outputs[idx].public_keys,
TransactionLink(self.id, idx), TransactionLink(self.id, idx),
) )
for idx in indices for idx in iterable_indices
] ]
def add_input(self, input_): def add_input(self, input_: Input) -> None:
"""Adds an input to a Transaction's list of inputs. """Adds an input to a Transaction's list of inputs.
Args: Args:
@ -293,7 +294,7 @@ class Transaction(object):
raise TypeError("`input_` must be a Input instance") raise TypeError("`input_` must be a Input instance")
self.inputs.append(input_) self.inputs.append(input_)
def add_output(self, output): def add_output(self, output: Output) -> None:
"""Adds an output to a Transaction's list of outputs. """Adds an output to a Transaction's list of outputs.
Args: Args:
@ -305,7 +306,7 @@ class Transaction(object):
raise TypeError("`output` must be an Output instance or None") raise TypeError("`output` must be an Output instance or None")
self.outputs.append(output) self.outputs.append(output)
def sign(self, private_keys): def sign(self, private_keys: list[str]):
"""Fulfills a previous Transaction's Output by signing Inputs. """Fulfills a previous Transaction's Output by signing Inputs.
Note: Note:
@ -359,7 +360,7 @@ class Transaction(object):
return self return self
@classmethod @classmethod
def _sign_input(cls, input_, message, key_pairs): def _sign_input(cls, input_: Input, message: str, key_pairs: dict) -> Input:
"""Signs a single Input. """Signs a single Input.
Note: Note:
@ -384,7 +385,7 @@ class Transaction(object):
raise ValueError("Fulfillment couldn't be matched to " "Cryptocondition fulfillment type.") raise ValueError("Fulfillment couldn't be matched to " "Cryptocondition fulfillment type.")
@classmethod @classmethod
def _sign_zenroom_fulfillment(cls, input_, message, key_pairs): def _sign_zenroom_fulfillment(cls, input_: Input, message: str, key_pairs: dict) -> Input:
"""Signs a Zenroomful. """Signs a Zenroomful.
Args: Args:
@ -399,14 +400,14 @@ class Transaction(object):
# this should never happen, but then again, never say never. # this should never happen, but then again, never say never.
input_ = deepcopy(input_) input_ = deepcopy(input_)
public_key = input_.owners_before[0] public_key = input_.owners_before[0]
message = sha3_256(message.encode()) sha3_message = sha3_256(message.encode())
if input_.fulfills: if input_.fulfills:
message.update("{}{}".format(input_.fulfills.txid, input_.fulfills.output).encode()) sha3_message.update("{}{}".format(input_.fulfills.txid, input_.fulfills.output).encode())
try: try:
# cryptoconditions makes no assumptions of the encoding of the # cryptoconditions makes no assumptions of the encoding of the
# message to sign or verify. It only accepts bytestrings # message to sign or verify. It only accepts bytestrings
input_.fulfillment.sign(message.digest(), base58.b58decode(key_pairs[public_key].encode())) input_.fulfillment.sign(sha3_message.digest(), base58.b58decode(key_pairs[public_key].encode()))
except KeyError: except KeyError:
raise KeypairMismatchException( raise KeypairMismatchException(
"Public key {} is not a pair to " "any of the private keys".format(public_key) "Public key {} is not a pair to " "any of the private keys".format(public_key)
@ -414,7 +415,7 @@ class Transaction(object):
return input_ return input_
@classmethod @classmethod
def _sign_simple_signature_fulfillment(cls, input_, message, key_pairs): def _sign_simple_signature_fulfillment(cls, input_: Input, message: str, key_pairs: dict) -> Input:
"""Signs a Ed25519Fulfillment. """Signs a Ed25519Fulfillment.
Args: Args:
@ -429,14 +430,14 @@ class Transaction(object):
# this should never happen, but then again, never say never. # this should never happen, but then again, never say never.
input_ = deepcopy(input_) input_ = deepcopy(input_)
public_key = input_.owners_before[0] public_key = input_.owners_before[0]
message = sha3_256(message.encode()) sha3_message = sha3_256(message.encode())
if input_.fulfills: if input_.fulfills:
message.update("{}{}".format(input_.fulfills.txid, input_.fulfills.output).encode()) sha3_message.update("{}{}".format(input_.fulfills.txid, input_.fulfills.output).encode())
try: try:
# cryptoconditions makes no assumptions of the encoding of the # cryptoconditions makes no assumptions of the encoding of the
# message to sign or verify. It only accepts bytestrings # message to sign or verify. It only accepts bytestrings
input_.fulfillment.sign(message.digest(), base58.b58decode(key_pairs[public_key].encode())) input_.fulfillment.sign(sha3_message.digest(), base58.b58decode(key_pairs[public_key].encode()))
except KeyError: except KeyError:
raise KeypairMismatchException( raise KeypairMismatchException(
"Public key {} is not a pair to " "any of the private keys".format(public_key) "Public key {} is not a pair to " "any of the private keys".format(public_key)
@ -444,7 +445,7 @@ class Transaction(object):
return input_ return input_
@classmethod @classmethod
def _sign_threshold_signature_fulfillment(cls, input_, message, key_pairs): def _sign_threshold_signature_fulfillment(cls, input_: Input, message: str, key_pairs: dict) -> Input:
"""Signs a ThresholdSha256. """Signs a ThresholdSha256.
Args: Args:
@ -454,9 +455,9 @@ class Transaction(object):
key_pairs (dict): The keys to sign the Transaction with. key_pairs (dict): The keys to sign the Transaction with.
""" """
input_ = deepcopy(input_) input_ = deepcopy(input_)
message = sha3_256(message.encode()) sha3_message = sha3_256(message.encode())
if input_.fulfills: if input_.fulfills:
message.update("{}{}".format(input_.fulfills.txid, input_.fulfills.output).encode()) sha3_message.update("{}{}".format(input_.fulfills.txid, input_.fulfills.output).encode())
for owner_before in set(input_.owners_before): for owner_before in set(input_.owners_before):
# TODO: CC should throw a KeypairMismatchException, instead of # TODO: CC should throw a KeypairMismatchException, instead of
@ -484,10 +485,10 @@ class Transaction(object):
# cryptoconditions makes no assumptions of the encoding of the # cryptoconditions makes no assumptions of the encoding of the
# message to sign or verify. It only accepts bytestrings # message to sign or verify. It only accepts bytestrings
for subffill in subffills: for subffill in subffills:
subffill.sign(message.digest(), base58.b58decode(private_key.encode())) subffill.sign(sha3_message.digest(), base58.b58decode(private_key.encode()))
return input_ return input_
def inputs_valid(self, outputs=None): def inputs_valid(self, outputs=None) -> bool:
"""Validates the Inputs in the Transaction against given """Validates the Inputs in the Transaction against given
Outputs. Outputs.
@ -520,7 +521,7 @@ class Transaction(object):
allowed_ops = ", ".join(self.__class__.ALLOWED_OPERATIONS) allowed_ops = ", ".join(self.__class__.ALLOWED_OPERATIONS)
raise TypeError("`operation` must be one of {}".format(allowed_ops)) raise TypeError("`operation` must be one of {}".format(allowed_ops))
def _inputs_valid(self, output_condition_uris): def _inputs_valid(self, output_condition_uris: list[str]) -> bool:
"""Validates an Input against a given set of Outputs. """Validates an Input against a given set of Outputs.
Note: Note:
@ -550,7 +551,7 @@ class Transaction(object):
return all(validate(i, cond) for i, cond in enumerate(output_condition_uris)) return all(validate(i, cond) for i, cond in enumerate(output_condition_uris))
@lru_cache(maxsize=16384) @lru_cache(maxsize=16384)
def _input_valid(self, input_, operation, message, output_condition_uri=None): 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. """Validates a single Input against a single Output.
Note: Note:
@ -601,16 +602,16 @@ class Transaction(object):
msg = json.loads(message) msg = json.loads(message)
ffill_valid = parsed_ffill.validate(message=json.dumps(msg["script"])) ffill_valid = parsed_ffill.validate(message=json.dumps(msg["script"]))
else: else:
message = sha3_256(message.encode()) sha3_message = sha3_256(message.encode())
if input_.fulfills: if input_.fulfills:
message.update("{}{}".format(input_.fulfills.txid, input_.fulfills.output).encode()) sha3_message.update("{}{}".format(input_.fulfills.txid, input_.fulfills.output).encode())
# NOTE: We pass a timestamp to `.validate`, as in case of a timeout # NOTE: We pass a timestamp to `.validate`, as in case of a timeout
# condition we'll have to validate against it # condition we'll have to validate against it
# cryptoconditions makes no assumptions of the encoding of the # cryptoconditions makes no assumptions of the encoding of the
# message to sign or verify. It only accepts bytestrings # message to sign or verify. It only accepts bytestrings
ffill_valid = parsed_ffill.validate(message=message.digest()) ffill_valid = parsed_ffill.validate(message=sha3_message.digest())
return output_valid and ffill_valid return output_valid and ffill_valid
# This function is required by `lru_cache` to create a key for memoization # This function is required by `lru_cache` to create a key for memoization
@ -618,7 +619,7 @@ class Transaction(object):
return hash(self.id) return hash(self.id)
@memoize_to_dict @memoize_to_dict
def to_dict(self): def to_dict(self) -> dict:
"""Transforms the object to a Python dictionary. """Transforms the object to a Python dictionary.
Returns: Returns:
@ -639,7 +640,7 @@ class Transaction(object):
@staticmethod @staticmethod
# TODO: Remove `_dict` prefix of variable. # TODO: Remove `_dict` prefix of variable.
def _remove_signatures(tx_dict): def _remove_signatures(tx_dict: dict) -> dict:
"""Takes a Transaction dictionary and removes all signatures. """Takes a Transaction dictionary and removes all signatures.
Args: Args:
@ -716,7 +717,7 @@ class Transaction(object):
return asset_ids.pop() return asset_ids.pop()
@staticmethod @staticmethod
def validate_id(tx_body): def validate_id(tx_body: dict):
"""Validate the transaction ID of a transaction """Validate the transaction ID of a transaction
Args: Args:
@ -741,7 +742,7 @@ class Transaction(object):
@classmethod @classmethod
@memoize_from_dict @memoize_from_dict
def from_dict(cls, tx, skip_schema_validation=True): def from_dict(cls, tx: dict, skip_schema_validation=True):
"""Transforms a Python dictionary to a Transaction object. """Transforms a Python dictionary to a Transaction object.
Args: Args:
@ -848,7 +849,7 @@ class Transaction(object):
tx = list(tx_map.values())[0] tx = list(tx_map.values())[0]
return cls.from_dict(tx) return cls.from_dict(tx)
type_registry = {} type_registry: dict[type, type] = {}
@staticmethod @staticmethod
def register_type(tx_type, tx_class): def register_type(tx_type, tx_class):

View File

@ -4,6 +4,9 @@
# 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 typing import Optional, Union
class TransactionLink(object): class TransactionLink(object):
"""An object for unidirectional linking to a Transaction's Output. """An object for unidirectional linking to a Transaction's Output.
@ -13,7 +16,7 @@ class TransactionLink(object):
`txid`. `txid`.
""" """
def __init__(self, txid=None, output=None): def __init__(self, txid: Optional[str] = None, output: Optional[int] = None):
"""Create an instance of a :class:`~.TransactionLink`. """Create an instance of a :class:`~.TransactionLink`.
Note: Note:
@ -42,7 +45,7 @@ class TransactionLink(object):
return hash((self.txid, self.output)) return hash((self.txid, self.output))
@classmethod @classmethod
def from_dict(cls, link): def from_dict(cls, link: dict):
"""Transforms a Python dictionary to a TransactionLink object. """Transforms a Python dictionary to a TransactionLink object.
Args: Args:
@ -56,7 +59,7 @@ class TransactionLink(object):
except TypeError: except TypeError:
return cls() return cls()
def to_dict(self): def to_dict(self) -> Union[dict,None]:
"""Transforms the object to a Python dictionary. """Transforms the object to a Python dictionary.
Returns: Returns:
@ -70,7 +73,7 @@ class TransactionLink(object):
"output_index": self.output, "output_index": self.output,
} }
def to_uri(self, path=""): def to_uri(self, path: str = "") -> Union[str,None]:
if self.txid is None and self.output is None: if self.txid is None and self.output is None:
return None return None
return "{}/transactions/{}/outputs/{}".format(path, self.txid, self.output) return "{}/transactions/{}/outputs/{}".format(path, self.txid, self.output)

View File

@ -7,15 +7,16 @@ import base58
import time import time
import re import re
import rapidjson import rapidjson
from typing import Callable
from planetmint.config import Config from planetmint.config import Config
from planetmint.transactions.common.exceptions import ValidationError from planetmint.transactions.common.exceptions import ValidationError
from cryptoconditions import ThresholdSha256, Ed25519Sha256, ZenroomSha256 from cryptoconditions import ThresholdSha256, Ed25519Sha256, ZenroomSha256, Fulfillment
from planetmint.transactions.common.exceptions import ThresholdTooDeep from planetmint.transactions.common.exceptions import ThresholdTooDeep
from cryptoconditions.exceptions import UnsupportedTypeError from cryptoconditions.exceptions import UnsupportedTypeError
def gen_timestamp(): def gen_timestamp() -> str:
"""The Unix time, rounded to the nearest second. """The Unix time, rounded to the nearest second.
See https://en.wikipedia.org/wiki/Unix_time See https://en.wikipedia.org/wiki/Unix_time
@ -25,7 +26,7 @@ def gen_timestamp():
return str(round(time.time())) return str(round(time.time()))
def serialize(data): def serialize(data: dict) -> str:
"""Serialize a dict into a JSON formatted string. """Serialize a dict into a JSON formatted string.
This function enforces rules like the separator and order of keys. This function enforces rules like the separator and order of keys.
@ -46,7 +47,7 @@ def serialize(data):
return rapidjson.dumps(data, skipkeys=False, ensure_ascii=False, sort_keys=True) return rapidjson.dumps(data, skipkeys=False, ensure_ascii=False, sort_keys=True)
def deserialize(data): def deserialize(data: str) -> dict:
"""Deserialize a JSON formatted string into a dict. """Deserialize a JSON formatted string into a dict.
Args: Args:
@ -59,7 +60,7 @@ def deserialize(data):
return rapidjson.loads(data) return rapidjson.loads(data)
def validate_txn_obj(obj_name, obj, key, validation_fun): def validate_txn_obj(obj_name: str, obj: dict, key: str, validation_fun: Callable) -> None:
"""Validate value of `key` in `obj` using `validation_fun`. """Validate value of `key` in `obj` using `validation_fun`.
Args: Args:
@ -85,7 +86,7 @@ def validate_txn_obj(obj_name, obj, key, validation_fun):
validate_all_items_in_list(obj_name, data, validation_fun) validate_all_items_in_list(obj_name, data, validation_fun)
def 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: for item in data:
if isinstance(item, dict): if isinstance(item, dict):
validate_all_keys_in_obj(obj_name, item, validation_fun) validate_all_keys_in_obj(obj_name, item, validation_fun)
@ -93,7 +94,7 @@ def validate_all_items_in_list(obj_name, data, validation_fun):
validate_all_items_in_list(obj_name, item, validation_fun) validate_all_items_in_list(obj_name, item, validation_fun)
def validate_all_keys_in_obj(obj_name, obj, 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`. """Validate all (nested) keys in `obj` by using `validation_fun`.
Args: Args:
@ -116,7 +117,7 @@ def validate_all_keys_in_obj(obj_name, obj, validation_fun):
validate_all_items_in_list(obj_name, value, validation_fun) validate_all_items_in_list(obj_name, value, validation_fun)
def validate_all_values_for_key_in_obj(obj, key, 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` """Validate value for all (nested) occurrence of `key` in `obj`
using `validation_fun`. using `validation_fun`.
@ -138,7 +139,7 @@ def validate_all_values_for_key_in_obj(obj, key, validation_fun):
validate_all_values_for_key_in_list(value, key, validation_fun) validate_all_values_for_key_in_list(value, key, validation_fun)
def validate_all_values_for_key_in_list(input_list, 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: for item in input_list:
if isinstance(item, dict): if isinstance(item, dict):
validate_all_values_for_key_in_obj(item, key, validation_fun) validate_all_values_for_key_in_obj(item, key, validation_fun)
@ -146,7 +147,7 @@ def validate_all_values_for_key_in_list(input_list, key, validation_fun):
validate_all_values_for_key_in_list(item, key, validation_fun) validate_all_values_for_key_in_list(item, key, validation_fun)
def validate_key(obj_name, key): def validate_key(obj_name: str, key: str) -> None:
"""Check if `key` contains ".", "$" or null characters. """Check if `key` contains ".", "$" or null characters.
https://docs.mongodb.com/manual/reference/limits/#Restrictions-on-Field-Names https://docs.mongodb.com/manual/reference/limits/#Restrictions-on-Field-Names
@ -170,7 +171,7 @@ def validate_key(obj_name, key):
raise ValidationError(error_str) raise ValidationError(error_str)
def _fulfillment_to_details(fulfillment): def _fulfillment_to_details(fulfillment: type[Fulfillment]) -> dict:
"""Encode a fulfillment as a details dictionary """Encode a fulfillment as a details dictionary
Args: Args:
@ -201,7 +202,7 @@ def _fulfillment_to_details(fulfillment):
raise UnsupportedTypeError(fulfillment.type_name) raise UnsupportedTypeError(fulfillment.type_name)
def _fulfillment_from_details(data, _depth=0): def _fulfillment_from_details(data: dict, _depth: int = 0):
"""Load a fulfillment for a signing spec dictionary """Load a fulfillment for a signing spec dictionary
Args: Args:

View File

@ -3,11 +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 typing import Optional
from cid import is_cid from cid import is_cid
from planetmint.transactions.common.transaction import Transaction from planetmint.transactions.common.transaction import Transaction
from planetmint.transactions.common.input import Input
from planetmint.transactions.common.output import Output
class Create(Transaction): class Create(Transaction):
@ -16,7 +15,7 @@ class Create(Transaction):
ALLOWED_OPERATIONS = (OPERATION,) ALLOWED_OPERATIONS = (OPERATION,)
@classmethod @classmethod
def validate_create(self, tx_signers, recipients, asset, metadata): 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): if not isinstance(tx_signers, list):
raise TypeError("`tx_signers` must be a list instance") raise TypeError("`tx_signers` must be a list instance")
if not isinstance(recipients, list): if not isinstance(recipients, list):
@ -36,7 +35,7 @@ class Create(Transaction):
return True return True
@classmethod @classmethod
def generate(cls, tx_signers, recipients, metadata=None, asset=None): 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. """A simple way to generate a `CREATE` transaction.
Note: Note:

View File

@ -3,7 +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 typing import Optional
from planetmint.transactions.common.transaction import Transaction from planetmint.transactions.common.transaction import Transaction
from planetmint.transactions.common.input import Input
from planetmint.transactions.common.output import Output from planetmint.transactions.common.output import Output
from copy import deepcopy from copy import deepcopy
@ -14,7 +17,7 @@ class Transfer(Transaction):
ALLOWED_OPERATIONS = (OPERATION,) ALLOWED_OPERATIONS = (OPERATION,)
@classmethod @classmethod
def validate_transfer(cls, inputs, recipients, asset_id, metadata): def validate_transfer(cls, inputs: list[Input], recipients: list[tuple[list[str],int]], asset_id: str, metadata: Optional[dict]):
if not isinstance(inputs, list): if not isinstance(inputs, list):
raise TypeError("`inputs` must be a list instance") raise TypeError("`inputs` must be a list instance")
if len(inputs) == 0: if len(inputs) == 0:
@ -39,7 +42,7 @@ class Transfer(Transaction):
return (deepcopy(inputs), outputs) return (deepcopy(inputs), outputs)
@classmethod @classmethod
def generate(cls, inputs, recipients, asset_id, metadata=None): 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. """A simple way to generate a `TRANSFER` transaction.
Note: Note:

View File

@ -6,6 +6,7 @@ from collections import OrderedDict
import base58 import base58
from uuid import uuid4 from uuid import uuid4
from typing import Optional
from planetmint import backend from planetmint import backend
from planetmint.transactions.types.assets.create import Create from planetmint.transactions.types.assets.create import Create
@ -32,13 +33,13 @@ class Election(Transaction):
set to (OPERATION,), CREATE set to OPERATION. set to (OPERATION,), CREATE set to OPERATION.
""" """
OPERATION = None OPERATION: Optional[str] = None
# Custom validation schema # Custom validation schema
TX_SCHEMA_CUSTOM = None TX_SCHEMA_CUSTOM = None
# Election Statuses: # Election Statuses:
ONGOING = "ongoing" ONGOING: str = "ongoing"
CONCLUDED = "concluded" CONCLUDED: str = "concluded"
INCONCLUSIVE = "inconclusive" INCONCLUSIVE: str = "inconclusive"
# Vote ratio to approve an election # Vote ratio to approve an election
ELECTION_THRESHOLD = 2 / 3 ELECTION_THRESHOLD = 2 / 3