def test_from_uri_malformed_uri(uri): from cryptoconditions.condition import Condition from cryptoconditions.exceptions import ParsingError # Note that the uri will contain `sha-265` instead of `sha-256` with raises(ParsingError): Condition.from_uri(uri)
def test_from_uri_parse_error_missing_cost(uri): from cryptoconditions.condition import Condition from cryptoconditions.exceptions import ParsingError with raises(ParsingError) as exc_info: Condition.from_uri(uri) assert exc_info.value.args == ( 'Invalid condition format: "cost" parameter or value missing.',)
def test_from_uri_prefix_error(uri): from cryptoconditions.condition import Condition, CONDITION_URI_SCHEME from cryptoconditions.exceptions import PrefixError with raises(PrefixError) as exc_info: Condition.from_uri(uri) assert exc_info.value.args == ( 'Serialized condition must start with "{}:"' .format(CONDITION_URI_SCHEME),)
def test_condition_cost(): from cryptoconditions.condition import Condition condition = Condition() # raises a ValueError if the cost is not an int condition.cost = 'a' with raises(ValueError): condition.cost
def test_from_uri_with_unsupported_type(uri): from cryptoconditions.condition import Condition from cryptoconditions.exceptions import UnsupportedTypeError with raises(UnsupportedTypeError) as exc_info: Condition.from_uri(uri) condition_type = parse_qs(urlparse(uri.rstrip()).query)['fpt'][0] assert exc_info.value.args == ( 'Type {} is not supported'.format(condition_type),)
def test_condition_comparison(minimal_ed25519, minimal_prefix): from cryptoconditions.condition import Condition assert Condition.from_uri(minimal_ed25519.condition_uri) == \ Condition.from_uri(minimal_ed25519.condition_uri) assert not Condition.from_uri(minimal_ed25519.condition_uri) == \ Condition.from_uri(minimal_prefix.condition_uri)
def test_from_uri_invalid_arguments(minimal_ed25519): from cryptoconditions.condition import Condition # raises a TypeError if the serialized_condition is not a str with raises(TypeError): Condition.from_uri(1) # Note: This should be removed in future versions of the code # from uri will return the the condition instance if we pass it a # condition instance condition = Condition.from_uri(minimal_ed25519.condition_uri) assert Condition.from_uri(condition) == condition
def test_condition_hash(): from cryptoconditions.condition import Condition condition = Condition() # raises an exception if hash is not 32 bytes long with raises(ValueError): condition.hash = 'a' # raises a ValueError if the hash is not set with raises(ValueError): condition.hash # correctly set the hash condition.hash = 'a' * 32 assert condition.hash == 'a' * 32
def subcondition(self, subcondition): if isinstance(subcondition, str): subcondition = Condition.from_uri(subcondition) elif not isinstance(subcondition, Condition): raise Exception( 'Subconditions must be URIs or objects of type Condition') self._subcondition = subcondition
def parse_payload(self, reader, *args): """ Parse a fulfillment payload. Read a fulfillment payload from a Reader and populate this object with that fulfillment. Args: reader (Reader): Source to read the fulfillment payload from. """ if not isinstance(reader, Reader): raise TypeError('reader must be a Reader instance') self.threshold = reader.read_var_uint() condition_count = reader.read_var_uint() for i in range(condition_count): weight = reader.read_var_uint() # reader, weight = read_weight(reader) fulfillment = reader.read_var_octet_string() condition = reader.read_var_octet_string() if len(fulfillment) and len(condition): raise TypeError('Subconditions may not provide both subcondition and fulfillment.') elif len(fulfillment): self.add_subfulfillment(Fulfillment.from_binary(fulfillment), weight) elif len(condition): self.add_subcondition(Condition.from_binary(condition), weight) else: raise TypeError('Subconditions must provide either subcondition or fulfillment.')
def parse_dict(self, data): """ Generate fulfillment payload from a dict Args: data (dict): description of the fulfillment Returns: Fulfillment """ if not isinstance(data, dict): raise TypeError('reader must be a dict instance') self.threshold = data['threshold'] for subfulfillments in data['subfulfillments']: weight = subfulfillments['weight'] if subfulfillments['type'] == FULFILLMENT: self.add_subfulfillment(Fulfillment.from_dict(subfulfillments), weight) elif subfulfillments['type'] == CONDITION: self.add_subcondition(Condition.from_dict(subfulfillments), weight) else: raise TypeError( 'Subconditions must provide either subcondition or fulfillment.' )
def parse_payload(self, reader, *args): """ Parse a fulfillment payload. Read a fulfillment payload from a Reader and populate this object with that fulfillment. Args: reader (Reader): Source to read the fulfillment payload from. """ if not isinstance(reader, Reader): raise TypeError('reader must be a Reader instance') self.threshold = reader.read_var_uint() condition_count = reader.read_var_uint() for i in range(condition_count): weight = reader.read_var_uint() fulfillment = reader.read_var_octet_string() condition = reader.read_var_octet_string() if len(fulfillment) and len(condition): raise TypeError('Subconditions may not provide both subcondition and fulfillment.') elif len(fulfillment): self.add_subfulfillment(Fulfillment.from_binary(fulfillment), weight) elif len(condition): self.add_subcondition(Condition.from_binary(condition), weight) else: raise TypeError('Subconditions must provide either subcondition or fulfillment.')
def subcondition(self, subcondition): if isinstance(subcondition, str): subcondition = Condition.from_uri(subcondition) elif not isinstance(subcondition, Condition): raise Exception( 'Subconditions must be URIs or objects of type Condition') self._subcondition = subcondition
def condition(self): """ Generate condition corresponding to this fulfillment. An important property of crypto-conditions is that the condition can always be derived from the fulfillment. This makes it very easy to post fulfillments to a system without having to specify which condition the relate to. The system can keep an index of conditions and look up any matching events related to that condition. Return: Condition: Condition corresponding to this fulfillment. """ condition = Condition() condition.type_id = self.type_id condition.hash = self.generate_hash() condition.cost = self.calculate_cost() condition.subtypes = self.subtypes return condition
def add_subcondition_uri(self, subcondition_uri): """ Add a subcondition (unfulfilled). This will automatically parse the URI and call addSubcondition. Args: subcondition_uri (str): Subcondition URI. """ if not isinstance(subcondition_uri, str): raise TypeError('Subcondition must be provided as a URI string, was {}'.format(subcondition_uri)) self.add_subcondition(Condition.from_uri(subcondition_uri))
def add_subcondition_uri(self, subcondition_uri): """ Add a subcondition (unfulfilled). This will automatically parse the URI and call addSubcondition. Args: subcondition_uri (str): Subcondition URI. """ if not isinstance(subcondition_uri, str): raise TypeError('Subcondition must be provided as a URI string') self.add_subcondition(Condition.from_uri(subcondition_uri))
def parse_asn1_dict_payload(self, data): self.threshold = len(data['subfulfillments']) for subfulfillment in data['subfulfillments']: self.subconditions.append({ 'type': FULFILLMENT, 'body': Fulfillment.from_asn1_dict(subfulfillment), }) for subcondition in data['subconditions']: self.subconditions.append({ 'type': CONDITION, 'body': Condition.from_asn1_dict(subcondition), })
def parse_dict(self, data): """ Generate fulfillment payload from a dict Args: data (dict): description of the fulfillment Returns: Fulfillment """ self.threshold = data['threshold'] for subfulfillments in data.get('subfulfillments', ()): self.add_subfulfillment(Fulfillment.from_dict(subfulfillments)) for subconditions in data.get('subconditions', ()): self.add_subcondition(Condition.from_dict(subfulfillments))
def test_condition_to_asn1_json(basic_threshold): from cryptoconditions.condition import Condition from cryptoconditions.type_registry import TypeRegistry condition = Condition.from_uri(basic_threshold.condition_uri) condition_type = TypeRegistry.find_by_type_id(condition.type_id) assert condition.to_asn1_json() == { 'type': condition_type['asn1_condition'], 'value': { 'cost': basic_threshold.cost, 'fingerprint': condition.hash, 'subtypes': '01011' } }
def parse_dict(self, data): """ Generate fulfillment payload from a dict Args: data (dict): description of the fulfillment Returns: Fulfillment """ self.threshold = data['threshold'] for subfulfillments in data.get('subfulfillments', ()): self.add_subfulfillment(Fulfillment.from_dict(subfulfillments)) for subconditions in data.get('subconditions', ()): self.add_subcondition(Condition.from_dict(subfulfillments))
def parse_asn1_dict_payload(self, data): self.threshold = len(data['subfulfillments']) for subfulfillment in data['subfulfillments']: self.subconditions.append({ 'type': FULFILLMENT, 'body': Fulfillment.from_asn1_dict(subfulfillment), }) for subcondition in data['subconditions']: self.subconditions.append({ 'type': CONDITION, 'body': Condition.from_asn1_dict(subcondition), })
def add_subcondition(self, subcondition): """ Add a subcondition (unfulfilled). This can be used to generate a new threshold condition from a set of subconditions or to provide a non-fulfilled subcondition when creating a threshold fulfillment. Args: subcondition (:class:`~cryptoconditions.condition.Condition` or :obj:`str`): Condition object or URI string representing a new subcondition to be added. """ if isinstance(subcondition, str): subcondition = Condition.from_uri(subcondition) elif not isinstance(subcondition, Condition): raise TypeError('Subconditions must be URIs or objects of type Condition') self.subconditions.append({'type': CONDITION, 'body': subcondition})
def add_subcondition(self, subcondition): """ Add a subcondition (unfulfilled). This can be used to generate a new threshold condition from a set of subconditions or to provide a non-fulfilled subcondition when creating a threshold fulfillment. Args: subcondition (:class:`~cryptoconditions.condition.Condition` or :obj:`str`): Condition object or URI string representing a new subcondition to be added. """ if isinstance(subcondition, str): subcondition = Condition.from_uri(subcondition) elif not isinstance(subcondition, Condition): raise TypeError( 'Subconditions must be URIs or objects of type Condition') self.subconditions.append({'type': CONDITION, 'body': subcondition})
def add_subcondition(self, subcondition, weight=1): """ Add a subcondition (unfulfilled). This can be used to generate a new threshold condition from a set of subconditions or to provide a non-fulfilled subcondition when creating a threshold fulfillment. Args: subcondition (Condition, str): Condition to add weight (int): Integer weight of the subcondition. """ if isinstance(subcondition, str): subcondition = Condition.from_uri(subcondition) elif not isinstance(subcondition, Condition): raise TypeError('Subconditions must be URIs or objects of type Condition') if not isinstance(weight, int): raise ValueError('Invalid weight, not an integer: {}'.format(weight)) self.subconditions.append( { 'type': CONDITION, 'body': subcondition, 'weight': weight })
def parse_json(self, json_data): """ Generate fulfillment payload from a json Args: json_data: json description of the fulfillment Returns: Fulfillment """ if not isinstance(json_data, dict): raise TypeError('reader must be a dict instance') self.threshold = json_data['threshold'] for subfulfillments_json in json_data['subfulfillments']: weight = subfulfillments_json['weight'] if subfulfillments_json['type'] == FULFILLMENT: self.add_subfulfillment(Fulfillment.from_json(subfulfillments_json), weight) elif subfulfillments_json['type'] == CONDITION: self.add_subcondition(Condition.from_json(subfulfillments_json), weight) else: raise TypeError('Subconditions must provide either subcondition or fulfillment.')
def add_subcondition(self, subcondition, weight=1): """ Add a subcondition (unfulfilled). This can be used to generate a new threshold condition from a set of subconditions or to provide a non-fulfilled subcondition when creating a threshold fulfillment. Args: subcondition (Condition, str): Condition to add weight (int): Integer weight of the subcondition. """ if isinstance(subcondition, str): subcondition = Condition.from_uri(subcondition) elif not isinstance(subcondition, Condition): raise TypeError('Subconditions must be URIs or objects of type Condition') if not isinstance(weight, int) or weight < 1: raise ValueError('Invalid weight: {}'.format(weight)) self.subconditions.append( { 'type': CONDITION, 'body': subcondition, 'weight': weight })
def condition(self): """ Generate condition corresponding to this fulfillment. An important property of crypto-conditions is that the condition can always be derived from the fulfillment. This makes it very easy to post fulfillments to a system without having to specify which condition the relate to. The system can keep an index of conditions and look up any matching events related to that condition. Return: Condition: Condition corresponding to this fulfillment. """ condition = Condition() condition.type_id = self.type_id condition.hash = self.generate_hash() condition.cost = self.calculate_cost() condition.subtypes = self.subtypes return condition
def validate_condition(serialized_condition): condition = Condition.from_uri(serialized_condition) return condition.validate()
def test_from_uri_parse_error_invalid_cost(uri): from cryptoconditions.condition import Condition from cryptoconditions.exceptions import ParsingError with raises(ParsingError) as exc_info: Condition.from_uri(uri) assert exc_info.value.args == ('No or invalid cost provided',)
def test_condition_validate(): from cryptoconditions.condition import Condition # lets set a known type_id so that the TypeRegistry can return the correct # condition type condition = Condition() condition.type_id = 0 # subtypes can have at most 32 bits or else raise a value error condition.subtypes = range(Condition.MAX_SAFE_SUBTYPES + 1) with raises(ValueError): condition.validate() # raises a ValueError if there is unsuported subtype condition.subtypes = set(['magic']) with raises(ValueError): condition.validate() # raises a ValueError if the cost if higher than MAX_COST condition.subtypes = set() condition.cost = Condition.MAX_COST + 1 with raises(ValueError): condition.validate()
def validate_condition(serialized_condition): condition = Condition.from_uri(serialized_condition) return condition.validate()