This repository has been archived by the owner on Aug 5, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
19 changed files
with
757 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
"""Models P2SH_ Addresses. | ||
.. _P2SH: https://en.bitcoin.it/wiki/Transaction#Pay-to-Script-Hash | ||
A P2SH_ address is used by user to send bitcoins to a Bitcoin smart | ||
contract | ||
Most information was extracted from the `Bitcoin Wiki`__. | ||
__ P2SH_ | ||
""" | ||
|
||
# Constants | ||
SH_LENGTH = 20 | ||
""" | ||
int: length of the script hashes stored in addresses | ||
""" | ||
|
||
|
||
# Errors | ||
class AddressSHLengthError(ValueError): | ||
"""Error raised when the redeem script hash has an incorrect length""" | ||
pass | ||
|
||
|
||
# Classes | ||
class P2SHAddress: | ||
"""Models a legacy P2SH Address | ||
Attributes: | ||
_script_hash (bytes): public key hash of the address | ||
""" | ||
__slots__ = ["_script_hash"] | ||
|
||
def __init__(self, script_hash: bytes) -> None: | ||
"""Initializes a P2SH given its script hash. | ||
Modifies the attribute :py:attr:`_script_hash` | ||
Args: | ||
script_hash: script hash to set | ||
Raises: | ||
AssertionError: script_hash argument is not a bytes object | ||
AddressSHLengthError: script_hash argument has not the | ||
proper length of a script hash | ||
""" | ||
self._script_hash = None | ||
self.script_hash = script_hash | ||
|
||
def do_things(self, obj: str) -> bytes: | ||
"""Does some things. | ||
Args: | ||
obj: object of options | ||
Returns: | ||
things modified by :py:obj:`obj` as bytes | ||
""" | ||
return bytes() | ||
|
||
@property | ||
def script_hash(self) -> bytes: | ||
"""Returns the public key hash stored in the address. | ||
:getter: Returns the script hash as bytes object | ||
:setter: Sets the script hash, while verifying the length and | ||
type | ||
""" | ||
return self._script_hash | ||
|
||
@script_hash.setter | ||
def script_hash(self, public_key_hash: bytes) -> None: | ||
assert isinstance(public_key_hash, bytes), \ | ||
"Script hash must be exactly %d bytes" % SH_LENGTH | ||
|
||
if len(public_key_hash) != SH_LENGTH: | ||
raise AddressSHLengthError | ||
|
||
self._script_hash = public_key_hash | ||
|
||
|
||
# Methods | ||
def from_hex(script_hash_hex: str) -> P2SHAddress: | ||
"""Creates a P2SH address from script hash in hexadecimal. | ||
Converts the hexadecimal string into bytes and then uses those bytes to | ||
create a P2SH address. | ||
>>> 2+3 | ||
5 | ||
Args: | ||
script_hash_hex: string containing script hash in hexadecimal | ||
Returns: | ||
pay to script hash address | ||
""" | ||
return P2SHAddress(bytes.fromhex(script_hash_hex)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
"""Provides methods to allow encoding and decoding Bitcoin addresses.""" | ||
from .base58 import encode as b58encode, decode as b58decode | ||
from .bech32 import encode as bech32encode, decode as bech32decode | ||
|
||
__all__ = ["b58encode", "b58decode", "bech32encode", "bech32decode"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
"""Provides methods to encode/decode to/from base58.""" | ||
|
||
# Libraries | ||
import math | ||
|
||
# Constants | ||
ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' | ||
""" | ||
str: base58 domain | ||
""" | ||
|
||
|
||
def encode(value: bytes) -> str: | ||
"""Encodes bytes using base58 convention into an human-readable string. | ||
**Sources** | ||
- `Bitcoin Wiki: base 58 \ | ||
<https://en.bitcoin.it/wiki/Base58Check_encoding>`_ | ||
""" | ||
assert isinstance(value, bytes), "Expected bytes, given: " + type(value) | ||
output = "" | ||
# Convert bytes to integer | ||
number = int.from_bytes(value, byteorder='big') | ||
|
||
while number > 0: | ||
number, residuum = divmod(number, 58) | ||
output += ALPHABET[residuum] | ||
|
||
# Convert leading zeros | ||
leading_zeros = 0 | ||
|
||
for element in value: | ||
if element == 0: | ||
leading_zeros += 1 | ||
else: | ||
break | ||
|
||
output += ALPHABET[0] * leading_zeros | ||
|
||
return output[::-1] | ||
|
||
|
||
def decode(value: str) -> bytes: | ||
"""Decodes a base58 string into a bytes object.""" | ||
assert isinstance(value, str), "Expected a string object, given: " + \ | ||
type(value) | ||
number = 0 | ||
|
||
for char in value: | ||
number *= 58 | ||
if char not in ALPHABET: | ||
raise ValueError("Character not valid to decode from Base58") | ||
number += ALPHABET.index(char) | ||
|
||
# Convert integer to bytes | ||
number_bytes = number.to_bytes(math.ceil(number.bit_length()/8), "big") | ||
|
||
# Check leading zeros to put them back again | ||
leading_zeros = 0 | ||
|
||
for char in value: | ||
if char == ALPHABET[0]: | ||
leading_zeros += 1 | ||
else: | ||
break | ||
number_bytes = b'\x00'*leading_zeros + number_bytes | ||
|
||
return number_bytes |
Oops, something went wrong.