diff --git a/planetmint/transactions/common/output.py b/planetmint/transactions/common/output.py index 6462941..ad071c5 100644 --- a/planetmint/transactions/common/output.py +++ b/planetmint/transactions/common/output.py @@ -6,7 +6,8 @@ from functools import reduce import base58 -from cryptoconditions import Fulfillment, ThresholdSha256, Ed25519Sha256 +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 @@ -70,6 +71,7 @@ class Output(object): # 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 @@ -123,6 +125,9 @@ class Output(object): 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], Fulfillment.ZenroomSha512Fulfillment): + ffill = ZenroomSha256( + public_key=base58.b58decode(public_keys[0])) else: ffill = Ed25519Sha256( public_key=base58.b58decode(public_keys[0])) diff --git a/planetmint/transactions/common/schema/v2.0/transaction.yaml b/planetmint/transactions/common/schema/v2.0/transaction.yaml index 604302f..c09c6f2 100644 --- a/planetmint/transactions/common/schema/v2.0/transaction.yaml +++ b/planetmint/transactions/common/schema/v2.0/transaction.yaml @@ -100,8 +100,8 @@ definitions: 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}$" + (fpt=(ed25519|threshold|zenroom)-sha-256(&)?|cost=[0-9]+(&)?|\ + subtypes=(ed25519|zenroom)-sha-256(&)?){2,3}$" public_keys: "$ref": "#/definitions/public_keys" input: @@ -147,7 +147,7 @@ definitions: properties: type: type: string - pattern: "^ed25519-sha-256$" + pattern: "^(ed25519|zenroom)-sha-256$" public_key: "$ref": "#/definitions/base58" - type: object diff --git a/planetmint/transactions/common/transaction.py b/planetmint/transactions/common/transaction.py index c21e99f..f229b6a 100644 --- a/planetmint/transactions/common/transaction.py +++ b/planetmint/transactions/common/transaction.py @@ -17,7 +17,7 @@ from functools import lru_cache import rapidjson import base58 -from cryptoconditions import Fulfillment, ThresholdSha256, Ed25519Sha256 +from cryptoconditions import Fulfillment, ThresholdSha256, Ed25519Sha256, ZenroomSha256 from cryptoconditions.exceptions import ( ParsingError, ASN1DecodeError, ASN1EncodeError) try: @@ -235,6 +235,7 @@ class Transaction(object): 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. @@ -289,7 +290,7 @@ class Transaction(object): currently: - Ed25519Fulfillment - ThresholdSha256. - + - ZenroomSha256 Args: input_ (:class:`~planetmint.transactions.common.transaction. Input`) The Input to be signed. @@ -302,11 +303,46 @@ class Transaction(object): elif isinstance(input_.fulfillment, ThresholdSha256): return cls._sign_threshold_signature_fulfillment(input_, message, key_pairs) + elif isinstance(input_.fulfillment, ZenroomSha256): + return cls._sign_threshold_signature_fulfillment(input_, message, + key_pairs) else: raise ValueError( 'Fulfillment couldn\'t be matched to ' 'Cryptocondition fulfillment type.') + @classmethod + def _sign_zenroom_fulfillment(cls, input_, message, key_pairs): + """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] + message = sha3_256(message.encode()) + if input_.fulfills: + 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( + 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_, message, key_pairs): """Signs a Ed25519Fulfillment. diff --git a/planetmint/transactions/common/utils.py b/planetmint/transactions/common/utils.py index e18580d..3aa1434 100644 --- a/planetmint/transactions/common/utils.py +++ b/planetmint/transactions/common/utils.py @@ -191,6 +191,12 @@ def _fulfillment_to_details(fulfillment): '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(), + } raise UnsupportedTypeError(fulfillment.type_name) @@ -214,5 +220,10 @@ def _fulfillment_from_details(data, _depth=0): 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']) + zenroom = ZenroomSha256( script= script, data=None , keys= {public_key}) + raise UnsupportedTypeError(data.get('type')) diff --git a/setup.py b/setup.py index 6f881dc..f926f1d 100644 --- a/setup.py +++ b/setup.py @@ -89,7 +89,7 @@ install_requires = [ 'pymongo==3.11.4', 'python-rapidjson==1.0', 'pyyaml==5.4.1', - 'requests==2.25.1', + 'requests>=2.25.1', 'setproctitle==1.2.2', 'werkzeug==2.0.3', ]