-
Notifications
You must be signed in to change notification settings - Fork 14
Implements BOLT1 and BOLT9 #208
base: master
Are you sure you want to change the base?
Changes from all commits
73a74d4
abda49d
06e680e
613ee82
8b96771
8ff6f5a
4f09f50
74fb734
89717fd
647962b
785502a
b9f06f4
fe914bc
2690660
dc4655b
13b5e13
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,117 @@ | ||
| def encode(value): | ||
| """ | ||
| Encodes a value to BigSize. | ||
|
|
||
| Args: | ||
| value (:obj:`int`): the integer value to be encoded. | ||
|
|
||
| Returns: | ||
| :obj:`bytes`: the BigSize encoding of the given value. | ||
|
|
||
| Raises: | ||
| :obj:`TypeError`: If the provided value is not an integer. | ||
| :obj:`ValueError`: If the provided value is negative or bigger than ``pow(2, 64) - 1``. | ||
| """ | ||
|
|
||
| if not isinstance(value, int): | ||
| raise TypeError(f"value must be integer, {type(value)} received") | ||
|
|
||
| if value < 0: | ||
| raise ValueError(f"value must be a positive integer, {value} received") | ||
|
|
||
| if value < 253: | ||
| return value.to_bytes(1, "big") | ||
| elif value < pow(2, 16): | ||
| return b"\xfd" + value.to_bytes(2, "big") | ||
| elif value < pow(2, 32): | ||
| return b"\xfe" + value.to_bytes(4, "big") | ||
| elif value < pow(2, 64): | ||
| return b"\xff" + value.to_bytes(8, "big") | ||
| else: | ||
| raise ValueError("BigSize can only encode up to 8-byte values") | ||
|
|
||
|
|
||
| def decode(value): | ||
| """ | ||
| Decodes a value from BigSize. | ||
|
|
||
| Args: | ||
| value (:obj:`bytes`): the value to be decoded. | ||
|
|
||
| Returns: | ||
| :obj:`int`: the integer decoding of the provided value. | ||
|
|
||
| Raises: | ||
| :obj:`TypeError`: If the provided value is not in bytes. | ||
| :obj:`ValueError`: If the provided value is bigger than 9-bytes or the value is not properly encoded. | ||
| """ | ||
|
|
||
| if not isinstance(value, bytes): | ||
| raise TypeError(f"value must be bytes, {type(value)} received") | ||
|
|
||
| if len(value) == 0: | ||
| raise ValueError("Unexpected EOF while decoding BigSize") | ||
|
|
||
| if len(value) > 9: | ||
| raise ValueError(f"value must be, at most, 9-bytes long, {len(value)} received") | ||
bigspider marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| if len(value) == 1 and value[0] < 253: | ||
| return value[0] | ||
|
|
||
| prefix = value[0] | ||
| decoded_value = int.from_bytes(value[1:], "big") | ||
|
|
||
| if prefix == 253: | ||
| length = 3 | ||
| min_v = 253 | ||
| max_v = pow(2, 16) | ||
| elif prefix == 254: | ||
| length = 5 | ||
| min_v = pow(2, 16) | ||
| max_v = pow(2, 32) | ||
| else: | ||
| length = 9 | ||
| min_v = pow(2, 32) | ||
| max_v = pow(2, 64) | ||
|
|
||
| if not len(value) == length: | ||
| raise ValueError("Unexpected EOF while decoding BigSize") | ||
| elif not min_v <= decoded_value < max_v: | ||
| raise ValueError("Encoded BigSize is non-canonical") | ||
| else: | ||
| return decoded_value | ||
|
|
||
|
|
||
| def parse(value): | ||
| """ | ||
| Parses a BigSize from a bytearray. | ||
|
|
||
| Args: | ||
| value (:obj:`bytes`): the bytearray from where the BigSize value will be parsed. | ||
|
|
||
| Returns: | ||
| :obj:`tuple`: A 2 items tuple containing the parsed BigSize and its encoded length. | ||
|
|
||
| Raises: | ||
| :obj:`TypeError`: If the provided value is not in bytes. | ||
| :obj:`ValueError`: If the provided value is not, at least, 1-byte long or if the value cannot be parsed. | ||
| """ | ||
|
|
||
| if not isinstance(value, bytes): | ||
| raise TypeError("value must be bytes") | ||
| if len(value) < 1: | ||
| raise ValueError("value must be at least 1-byte long") | ||
|
|
||
| prefix = value[0] | ||
|
|
||
| # message length is not explicitly checked here, but wrong length will fail at decode. | ||
| if prefix < 253: | ||
| # prefix is actually the value to be parsed | ||
| return decode(value[0:1]), 1 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One problem with this take-what-you-need-and-return-its-length is that it is not well-suited for unbuffered streams, since we can't take 9 bytes, parse them, and then reset the stream back by the remainder of the bytes. I usually use |
||
| else: | ||
| if prefix == 253: | ||
| return decode(value[0:3]), 3 | ||
| elif prefix == 254: | ||
| return decode(value[0:5]), 5 | ||
| else: | ||
| return decode(value[0:9]), 9 | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For the cases with a prefix, these calls will pass to
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd add a comment. This was intentional to avoid double checking the same thing. |
||
Uh oh!
There was an error while loading. Please reload this page.