-
Notifications
You must be signed in to change notification settings - Fork 41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft 02 impl #67
Draft 02 impl #67
Conversation
Codecov Report
@@ Coverage Diff @@
## master #67 +/- ##
==========================================
+ Coverage 85.5% 89.19% +3.68%
==========================================
Files 15 15
Lines 766 824 +58
==========================================
+ Hits 655 735 +80
+ Misses 111 89 -22 |
cryptoconditions/__init__.py
Outdated
fulfillment = Fulfillment.from_uri(serialized_fulfillment) | ||
condition_uri = fulfillment.condition_uri | ||
if condition_uri != serialized_condition: | ||
raise Exception( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should define an exception for this case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh yes
cryptoconditions/__init__.py
Outdated
|
||
def fulfillment_to_condition(serialized_fulfillment): | ||
fulfillment = Fulfillment.from_uri(serialized_fulfillment) | ||
return fulfillment.condition_uri() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
condition_uri
is a property
@@ -74,63 +94,98 @@ def from_uri(serialized_condition): | |||
Returns: | |||
Condition: Resulting object | |||
""" | |||
# TODO consider removing. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree. Don't see the reason for it. The check to see if its a string should be enough
if not re.match(CONDITION_REGEX_STRICT, serialized_condition): | ||
raise ValueError('Invalid condition format') | ||
|
||
if pieces[0] != CONDITION_URI_SCHEME: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a need for this check here? Wouldn't the regex_match
also catch this error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I think the regex would be enough. I kept it there as this check was already there.
I guess it is useful to give a more explicit error message as to what is wrong.
def max_fulfillment_length(self, value): | ||
# TODO update docstrings | ||
@cost.setter | ||
def cost(self, value): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we want to be consistent we should to cost
the same we did to hash
. Check that the value has the correct type on the setter
and and raise a ValueError
on the property
if the value is not set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue: #69
# Bitmask can have at most 32 bits with current implementation | ||
if self.bitmask > Condition.MAX_SAFE_BITMASK: | ||
raise ValueError('Bitmask too large to be safely represented') | ||
# Subtypes can have at most 32 bits with current implementation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand this check. We are not dealing with bits here. We are only checking the number of subtypes.
I think the next check for supported subtypes should be enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it is somewhat clumsy. This was done quickly, as the JS implementation had yet to be updated there. See interledgerjs/five-bells-condition#67
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue: #70
cryptoconditions/condition.py
Outdated
condition_type = TypeRegistry.find_by_type_id(self.type_id) | ||
condition_class = condition_type['class'] | ||
asn1_dict = { | ||
'type': condition_class.TYPE_ASN1_CONDITION, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can just use condition_type['asn1_condition']
instead of declaring condition_class
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmmm, yes, we need it for the TYPE_CATEGORY
though ...
cryptoconditions/condition.py
Outdated
""" | ||
# TODO Use more precise Exception class | ||
if len(value) != 32: | ||
raise Exception( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it make sense to raise a ValueError
here instead of Exception
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changing it now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
addressed in 91d6852
# This is a stricter version based on limitations of the current implementation. | ||
# Specifically, we can't handle bitmasks greater than 32 bits. | ||
CONDITION_REGEX_STRICT = \ | ||
r'^cc:([1-9a-f][0-9a-f]{0,3}|0):[1-9a-f][0-9a-f]{0,7}:[a-zA-Z0-9_-]{0,86}:([1-9][0-9]{0,17}|0)$' | ||
CONDITION_REGEX_STRICT = CONDITION_REGEX |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason to have both CONDITION_REGEX_STRICT
and CONDITION_REGEX
since they are now the same?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know 😄
we can clean that yes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue: #71
raise ParsingError( | ||
'Invalid condition format: "cost" parameter or value missing.') | ||
|
||
if not re.match(INTEGER_REGEX, cost): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this the same as int(cost)
?
It would eliminate the need for INTEGER_REGEX
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh, sure
issue: #72
condition.cost = value['cost'] | ||
condition._subtypes = set() | ||
if registered_type['class'].TYPE_CATEGORY == 'compound': | ||
subtypes = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice use of itertools. But I feel that this could be in a separate method. Something like subtypes_from_bitstring
and subtypes_to_bitstring
for to_ans1_dict
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't the asn1 lib provide something to do this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah ok, good point!
# The asn1 json payload that ILP uses in the JS implementation differs from | ||
# the dictionary payload generated by pyasn1 -- hence the question: | ||
# Is there a standard JSON representation of a condition? | ||
def to_asn1_json(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could reuse to_asn1_dict
. The value
is the payload
produced by to_asn1_dict
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
addressed in 0359c52
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note: broke the thing and fixed it now
cryptoconditions/schemas/rsa.py
Outdated
@@ -0,0 +1 @@ | |||
"""ASN.1 definition for an RSA public key object.""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, removed it. The JS implementation had one.
@@ -23,13 +26,49 @@ def get_class_from_type_id(type_id): | |||
""" | |||
# Determine type of condition | |||
if type_id > MAX_SAFE_INTEGER_JS: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check here seems unnecessary
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I don't know, this came from the JS implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue: #74
|
||
|
||
class BaseSha256Fulfillment(Fulfillment): | ||
class BaseSha256(Fulfillment): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need this class? It seems that this method could just be added to the Fulfillment
class
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm, good point. I think the idea is that the Fulfillment
is "SHA" agnostic. This way, one can imagine in the near future a new suite of types based of SHA-512 for instance, that would still use the class Fulfillment
as a parent class.
As a side note: These things could be achieved via mixins probably.
self._signature = signature | ||
|
||
# TODO check type or use static typing (mypy) | ||
def _validate_public_key(self, public_key): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we check isinstance(public_key, bytes)
the same way we do with the signature.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
cryptoconditions/types/ed25519.py
Outdated
raise Exception( | ||
'Public key must be {} bytes, was: {}'.format( | ||
self.PUBLIC_KEY_LENGTH, len(public_key))) | ||
# TODO More validation? Ask ILP folks. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The underlying crypto library should already do extra checks. In fact this could just call VerifyKey
and see if an exception is thrown.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
cryptoconditions/types/ed25519.py
Outdated
|
||
def parse_asn1_dict_payload(self, data): | ||
self.public_key = data['publicKey'] | ||
self.signature = data['signature'] | ||
|
||
def validate(self, message=None, **kwargs): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think there is a reason for message
to be an optional argument. verify
should fail if message is None
. Also I don't see a reason for **kwargs
cryptoconditions/types/preimage.py
Outdated
def bitmask(self): | ||
return self.FEATURE_BITMASK | ||
|
||
#def write_hash_payload(self, hasher): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we remove all this commented code?
cryptoconditions/types/preimage.py
Outdated
|
||
@property | ||
def bitmask(self): | ||
return self.FEATURE_BITMASK |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FEATURE_BITMASK
is not declared anywhere.
cryptoconditions/types/prefix.py
Outdated
@prefix.setter | ||
def prefix(self, prefix): | ||
if not isinstance(prefix, bytes): | ||
raise TypeError('Prefix must be a Buffer, was: {}'.format(prefix)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
must be a byte string
cryptoconditions/types/prefix.py
Outdated
""" | ||
self._prefix = b'' | ||
self._subcondition = None | ||
self.max_message_length = 16384 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be self._max_message_length
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why 16384
?
TYPE_ASN1_FULFILLMENT = 'prefixSha256Fulfillment' | ||
TYPE_CATEGORY = 'compound' | ||
|
||
CONSTANT_BASE_COST = 16384 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are these two constants used anywhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
true, they are not used right now
verify this fulfillment to begin with. | ||
|
||
""" | ||
return {t for t in chain(self.subcondition.subtypes, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will this work with arbitrarily deep subconditions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if I am not wrong, the subcondition.subtypes
should list all subtypes, regardless of the nesting level
cryptoconditions/types/prefix.py
Outdated
cost = (len(self.prefix) + | ||
self.max_message_length + subcondition_cost + 1024) | ||
return cost | ||
#(len(self.prefix) + |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remove?
Some next steps that can be done after this as been merged. I don't think we should expose methods like to/from asn1_dict/asn1_json since they are on the spec and are specific to different asn1 implementations. We should cleanup The crypto module does not seem to be used by cryptoconditions anymore. BigchainDB probably still uses it. Ideally we would move the code we need into BigchainDB and remove the crypto module from cryptoconditions. |
9e61063
to
858f93a
Compare
- Fixed a bug in fulfillment_to_condition
- Added a custom exception for validate_fulfillment - Fixed validate_fulfillment test
c9038d4
to
846b0cd
Compare
Note to reviewers
Since this is a relatively big PR, just ask me if you have questions, chances are that it'll be faster.
I have tried to minimize any changes except those needed for the upgrade.
Moreover the changes are more or less a translation of the JS implementation.
Known things left to do
- [ ] Duplicate tests undermoved to issue Do something about the duplicate tests #78tests/test_generated_valid_spec.py