Initial commit: 首次建仓,建立目录结构
This commit is contained in:
@ -0,0 +1,6 @@
|
||||
from .bip32 import *
|
||||
from .bip44 import *
|
||||
from .utils import *
|
||||
from .conf import *
|
||||
|
||||
__all__ = [ 'bip32', 'bip44', 'utils', 'conf' ]
|
||||
Binary file not shown.
@ -0,0 +1,205 @@
|
||||
# Copyright (c) 2021 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""Module for P2PKH address encoding/decoding."""
|
||||
|
||||
# Imports
|
||||
from enum import Enum, auto, unique
|
||||
from typing import Any, Union
|
||||
|
||||
from .addr_dec_utils import AddrDecUtils
|
||||
from .addr_key_validator import AddrKeyValidator
|
||||
from .iaddr_decoder import IAddrDecoder
|
||||
from .iaddr_encoder import IAddrEncoder
|
||||
from ..base58 import Base58Alphabets, Base58ChecksumError, Base58Decoder, Base58Encoder
|
||||
from ..bech32 import BchBech32Decoder, BchBech32Encoder, Bech32ChecksumError
|
||||
from ..ecc import IPublicKey
|
||||
from ..utils.crypto import Hash160
|
||||
from ..utils.misc import BytesUtils
|
||||
|
||||
|
||||
@unique
|
||||
class P2PKHPubKeyModes(Enum):
|
||||
"""Enumerative for P2PKH public key modes."""
|
||||
|
||||
COMPRESSED = auto()
|
||||
UNCOMPRESSED = auto()
|
||||
|
||||
|
||||
class P2PKHAddrDecoder(IAddrDecoder):
|
||||
"""
|
||||
P2PKH address decoder class.
|
||||
It allows the Pay-to-Public-Key-Hash address decoding.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def DecodeAddr(addr: str,
|
||||
**kwargs: Any) -> bytes:
|
||||
"""
|
||||
Decode a P2PKH address to bytes.
|
||||
|
||||
Args:
|
||||
addr (str): Address string
|
||||
|
||||
Other Parameters:
|
||||
net_ver (bytes) : Expected net address version
|
||||
base58_alph (Base58Alphabets, optional): Base58 alphabet (default: Bitcoin alphabet)
|
||||
|
||||
Returns:
|
||||
bytes: Public key hash bytes
|
||||
|
||||
Raises:
|
||||
ValueError: If the address encoding is not valid
|
||||
"""
|
||||
net_ver_bytes = kwargs["net_ver"]
|
||||
base58_alph = kwargs.get("base58_alph", Base58Alphabets.BITCOIN)
|
||||
|
||||
try:
|
||||
addr_dec_bytes = Base58Decoder.CheckDecode(addr, base58_alph)
|
||||
except Base58ChecksumError as ex:
|
||||
raise ValueError("Invalid base58 checksum") from ex
|
||||
|
||||
# Validate length
|
||||
AddrDecUtils.ValidateLength(addr_dec_bytes,
|
||||
Hash160.DigestSize() + len(net_ver_bytes))
|
||||
# Validate and remove prefix
|
||||
return AddrDecUtils.ValidateAndRemovePrefix(addr_dec_bytes, net_ver_bytes)
|
||||
|
||||
|
||||
class P2PKHAddrEncoder(IAddrEncoder):
|
||||
"""
|
||||
P2PKH address encoder class.
|
||||
It allows the Pay-to-Public-Key-Hash address encoding.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def EncodeKey(pub_key: Union[bytes, IPublicKey],
|
||||
**kwargs: Any) -> str:
|
||||
"""
|
||||
Encode a public key to P2PKH address.
|
||||
|
||||
Args:
|
||||
pub_key (bytes or IPublicKey): Public key bytes or object
|
||||
|
||||
Other Parameters:
|
||||
net_ver (bytes) : Net address version
|
||||
base58_alph (Base58Alphabets, optional) : Base58 alphabet, Bitcoin alphabet by default
|
||||
pub_key_mode (P2PKHPubKeyModes, optional): Public key mode, compressed key by default
|
||||
|
||||
Returns:
|
||||
str: Address string
|
||||
|
||||
Raises:
|
||||
ValueError: If the public key is not valid
|
||||
TypeError: If the public key is not secp256k1
|
||||
"""
|
||||
net_ver_bytes = kwargs["net_ver"]
|
||||
base58_alph = kwargs.get("base58_alph", Base58Alphabets.BITCOIN)
|
||||
pub_key_mode = kwargs.get("pub_key_mode", P2PKHPubKeyModes.COMPRESSED)
|
||||
|
||||
pub_key_obj = AddrKeyValidator.ValidateAndGetSecp256k1Key(pub_key)
|
||||
pub_key_bytes = (pub_key_obj.RawCompressed().ToBytes()
|
||||
if pub_key_mode == P2PKHPubKeyModes.COMPRESSED
|
||||
else pub_key_obj.RawUncompressed().ToBytes())
|
||||
|
||||
return Base58Encoder.CheckEncode(net_ver_bytes + Hash160.QuickDigest(pub_key_bytes), base58_alph)
|
||||
|
||||
|
||||
class BchP2PKHAddrDecoder(IAddrDecoder):
|
||||
"""
|
||||
Bitcoin Cash P2PKH address decoder class.
|
||||
It allows the Bitcoin Cash P2PKH decoding.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def DecodeAddr(addr: str,
|
||||
**kwargs: Any) -> bytes:
|
||||
"""
|
||||
Decode a Bitcoin Cash P2PKH address to bytes.
|
||||
|
||||
Args:
|
||||
addr (str): Address string
|
||||
|
||||
Other Parameters:
|
||||
hrp (str) : Expected HRP
|
||||
net_ver (bytes): Expected net address version
|
||||
|
||||
Returns:
|
||||
bytes: Public key hash bytes
|
||||
|
||||
Raises:
|
||||
ValueError: If the address encoding is not valid
|
||||
"""
|
||||
hrp = kwargs["hrp"]
|
||||
net_ver_bytes = kwargs["net_ver"]
|
||||
|
||||
try:
|
||||
net_ver_bytes_got, addr_dec_bytes = BchBech32Decoder.Decode(hrp, addr)
|
||||
except Bech32ChecksumError as ex:
|
||||
raise ValueError("Invalid bech32 checksum") from ex
|
||||
|
||||
# Check net version
|
||||
if net_ver_bytes != net_ver_bytes_got:
|
||||
raise ValueError(f"Invalid net version (expected {BytesUtils.ToHexString(net_ver_bytes)}, "
|
||||
f"got {BytesUtils.ToHexString(net_ver_bytes_got)})")
|
||||
# Validate length
|
||||
AddrDecUtils.ValidateLength(addr_dec_bytes,
|
||||
Hash160.DigestSize())
|
||||
return addr_dec_bytes
|
||||
|
||||
|
||||
class BchP2PKHAddrEncoder(IAddrEncoder):
|
||||
"""
|
||||
Bitcoin Cash P2PKH address encoder class.
|
||||
It allows the Bitcoin Cash P2PKH encoding.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def EncodeKey(pub_key: Union[bytes, IPublicKey],
|
||||
**kwargs: Any) -> str:
|
||||
"""
|
||||
Encode a public key to Bitcoin Cash P2PKH address.
|
||||
|
||||
Args:
|
||||
pub_key (bytes or IPublicKey): Public key bytes or object
|
||||
|
||||
Other Parameters:
|
||||
hrp (str) : HRP
|
||||
net_ver (bytes): Net address version
|
||||
|
||||
Returns:
|
||||
str: Address string
|
||||
|
||||
Raises:
|
||||
ValueError: If the public key is not valid
|
||||
TypeError: If the public key is not secp256k1
|
||||
"""
|
||||
hrp = kwargs["hrp"]
|
||||
net_ver_bytes = kwargs["net_ver"]
|
||||
|
||||
pub_key_obj = AddrKeyValidator.ValidateAndGetSecp256k1Key(pub_key)
|
||||
return BchBech32Encoder.Encode(hrp,
|
||||
net_ver_bytes,
|
||||
Hash160.QuickDigest(pub_key_obj.RawCompressed().ToBytes()))
|
||||
|
||||
|
||||
# Deprecated: only for compatibility, Encoder classes shall be used instead
|
||||
P2PKHAddr = P2PKHAddrEncoder
|
||||
BchP2PKHAddr = BchP2PKHAddrEncoder
|
||||
@ -0,0 +1,5 @@
|
||||
from .iaddr_encoder import IAddrEncoder
|
||||
from .P2PKH_addr import (
|
||||
BchP2PKHAddr, BchP2PKHAddrDecoder, BchP2PKHAddrEncoder, P2PKHAddr, P2PKHAddrDecoder, P2PKHAddrEncoder,
|
||||
P2PKHPubKeyModes
|
||||
)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,125 @@
|
||||
# Copyright (c) 2021 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""Module with utility functions for address decoding."""
|
||||
|
||||
# Imports
|
||||
from typing import Callable, Tuple, Type, TypeVar, Union
|
||||
|
||||
from ..ecc import IPublicKey
|
||||
from ..utils.misc import BytesUtils
|
||||
|
||||
|
||||
BytesOrStr = TypeVar("BytesOrStr", bytes, str)
|
||||
|
||||
|
||||
class AddrDecUtils:
|
||||
"""Class container for address decoding utility functions."""
|
||||
|
||||
@staticmethod
|
||||
def ValidateAndRemovePrefix(addr: BytesOrStr,
|
||||
prefix: BytesOrStr) -> BytesOrStr:
|
||||
"""
|
||||
Validate and remove prefix from an address.
|
||||
|
||||
Args:
|
||||
addr (bytes or str) : Address string or bytes
|
||||
prefix (bytes or str): Address prefix
|
||||
|
||||
Returns:
|
||||
bytes or str: Address string or bytes with prefix removed
|
||||
|
||||
Raises:
|
||||
ValueError: If the prefix is not valid
|
||||
"""
|
||||
prefix_got = addr[:len(prefix)]
|
||||
if prefix != prefix_got:
|
||||
raise ValueError(f"Invalid prefix (expected {prefix!r}, got {prefix_got!r})")
|
||||
return addr[len(prefix):]
|
||||
|
||||
@staticmethod
|
||||
def ValidateLength(addr: Union[bytes, str],
|
||||
len_exp: int) -> None:
|
||||
"""
|
||||
Validate address length.
|
||||
|
||||
Args:
|
||||
addr (str) : Address string or bytes
|
||||
len_exp (int): Expected address length
|
||||
|
||||
Raises:
|
||||
ValueError: If the length is not valid
|
||||
"""
|
||||
if len(addr) != len_exp:
|
||||
raise ValueError(f"Invalid length (expected {len_exp}, got {len(addr)})")
|
||||
|
||||
@staticmethod
|
||||
def ValidatePubKey(pub_key_bytes: bytes,
|
||||
pub_key_cls: Type[IPublicKey]) -> None:
|
||||
"""
|
||||
Validate address length.
|
||||
|
||||
Args:
|
||||
pub_key_bytes (bytes) : Public key bytes
|
||||
pub_key_cls (IPublicKey): Public key class type
|
||||
|
||||
Raises:
|
||||
ValueError: If the public key is not valid
|
||||
"""
|
||||
if not pub_key_cls.IsValidBytes(pub_key_bytes):
|
||||
raise ValueError(f"Invalid {pub_key_cls.CurveType()} "
|
||||
f"public key {BytesUtils.ToHexString(pub_key_bytes)}")
|
||||
|
||||
@staticmethod
|
||||
def ValidateChecksum(payload_bytes: bytes,
|
||||
checksum_bytes_exp: bytes,
|
||||
checksum_fct: Callable[[bytes], bytes]) -> None:
|
||||
"""
|
||||
Validate address checksum.
|
||||
|
||||
Args:
|
||||
payload_bytes (bytes) : Payload bytes
|
||||
checksum_bytes_exp (bytes): Expected checksum bytes
|
||||
checksum_fct (function) : Function for computing checksum
|
||||
|
||||
Raises:
|
||||
ValueError: If the computed checksum is not equal tot he specified one
|
||||
"""
|
||||
checksum_bytes_got = checksum_fct(payload_bytes)
|
||||
if checksum_bytes_exp != checksum_bytes_got:
|
||||
raise ValueError(f"Invalid checksum (expected {BytesUtils.ToHexString(checksum_bytes_exp)}, "
|
||||
f"got {BytesUtils.ToHexString(checksum_bytes_got)})")
|
||||
|
||||
@staticmethod
|
||||
def SplitPartsByChecksum(addr_bytes: bytes,
|
||||
checksum_len: int) -> Tuple[bytes, bytes]:
|
||||
"""
|
||||
Split address in two parts, considering the checksum at the end of it.
|
||||
|
||||
Args:
|
||||
addr_bytes (bytes): Address bytes
|
||||
checksum_len (int): Checksum length
|
||||
|
||||
Returns:
|
||||
tuple[bytes, bytes]: Payload bytes (index 0) and checksum bytes (index 1)
|
||||
"""
|
||||
checksum_bytes = addr_bytes[-1 * checksum_len:]
|
||||
payload_bytes = addr_bytes[:-1 * checksum_len]
|
||||
return payload_bytes, checksum_bytes
|
||||
@ -0,0 +1,162 @@
|
||||
# Copyright (c) 2021 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""Module with utility functions for validating address public keys."""
|
||||
|
||||
# Imports
|
||||
from typing import Type, Union
|
||||
|
||||
from ..ecc import (
|
||||
Secp256k1PublicKey, IPublicKey,
|
||||
# Ed25519Blake2bPublicKey, Ed25519MoneroPublicKey, Ed25519PublicKey, EllipticCurveGetter,
|
||||
# Nist256p1PublicKey, Secp256k1PublicKey, Sr25519PublicKey
|
||||
)
|
||||
|
||||
|
||||
class AddrKeyValidator:
|
||||
"""Class container for address utility functions."""
|
||||
|
||||
# @staticmethod
|
||||
# def ValidateAndGetEd25519Key(pub_key: Union[bytes, IPublicKey]) -> IPublicKey:
|
||||
# """
|
||||
# Validate and get a ed25519 public key.
|
||||
|
||||
# Args:
|
||||
# pub_key (bytes or IPublicKey object): Public key bytes or object
|
||||
|
||||
# Returns:
|
||||
# IPublicKey object: IPublicKey object
|
||||
|
||||
# Raises:
|
||||
# TypeError: If the public key is not ed25519
|
||||
# ValueError: If the public key is not valid
|
||||
# """
|
||||
# return AddrKeyValidator.__ValidateAndGetGenericKey(pub_key, Ed25519PublicKey)
|
||||
|
||||
# @staticmethod
|
||||
# def ValidateAndGetEd25519Blake2bKey(pub_key: Union[bytes, IPublicKey]) -> IPublicKey:
|
||||
# """
|
||||
# Validate and get a ed25519-blake2b public key.
|
||||
|
||||
# Args:
|
||||
# pub_key (bytes or IPublicKey object): Public key bytes or object
|
||||
|
||||
# Returns:
|
||||
# IPublicKey object: IPublicKey object
|
||||
|
||||
# Raises:
|
||||
# TypeError: If the public key is not ed25519-blake2b
|
||||
# ValueError: If the public key is not valid
|
||||
# """
|
||||
# return AddrKeyValidator.__ValidateAndGetGenericKey(pub_key, Ed25519Blake2bPublicKey)
|
||||
|
||||
# @staticmethod
|
||||
# def ValidateAndGetEd25519MoneroKey(pub_key: Union[bytes, IPublicKey]) -> IPublicKey:
|
||||
# """
|
||||
# Validate and get a ed25519-monero public key.
|
||||
|
||||
# Args:
|
||||
# pub_key (bytes or IPublicKey object): Public key bytes or object
|
||||
|
||||
# Returns:
|
||||
# IPublicKey object: IPublicKey object
|
||||
|
||||
# Raises:
|
||||
# TypeError: If the public key is not ed25519-monero
|
||||
# ValueError: If the public key is not valid
|
||||
# """
|
||||
# return AddrKeyValidator.__ValidateAndGetGenericKey(pub_key, Ed25519MoneroPublicKey)
|
||||
|
||||
# @staticmethod
|
||||
# def ValidateAndGetNist256p1Key(pub_key: Union[bytes, IPublicKey]) -> IPublicKey:
|
||||
# """
|
||||
# Validate and get a nist256p1 public key.
|
||||
|
||||
# Args:
|
||||
# pub_key (bytes or IPublicKey object): Public key bytes or object
|
||||
|
||||
# Returns:
|
||||
# IPublicKey object: IPublicKey object
|
||||
|
||||
# Raises:
|
||||
# TypeError: If the public key is not nist256p1
|
||||
# ValueError: If the public key is not valid
|
||||
# """
|
||||
# return AddrKeyValidator.__ValidateAndGetGenericKey(pub_key, Nist256p1PublicKey)
|
||||
|
||||
@staticmethod
|
||||
def ValidateAndGetSecp256k1Key(pub_key: Union[bytes, IPublicKey]) -> IPublicKey:
|
||||
"""
|
||||
Validate and get a secp256k1 public key.
|
||||
|
||||
Args:
|
||||
pub_key (bytes or IPublicKey object): Public key bytes or object
|
||||
|
||||
Returns:
|
||||
IPublicKey object: IPublicKey object
|
||||
|
||||
Raises:
|
||||
TypeError: If the public key is not secp256k1
|
||||
ValueError: If the public key is not valid
|
||||
"""
|
||||
return AddrKeyValidator.__ValidateAndGetGenericKey(pub_key, Secp256k1PublicKey)
|
||||
|
||||
# @staticmethod
|
||||
# def ValidateAndGetSr25519Key(pub_key: Union[bytes, IPublicKey]) -> IPublicKey:
|
||||
# """
|
||||
# Validate and get a sr25519 public key.
|
||||
|
||||
# Args:
|
||||
# pub_key (bytes or IPublicKey object): Public key bytes or object
|
||||
|
||||
# Returns:
|
||||
# IPublicKey object: IPublicKey object
|
||||
|
||||
# Raises:
|
||||
# TypeError: If the public key is not sr25519
|
||||
# ValueError: If the public key is not valid
|
||||
# """
|
||||
# return AddrKeyValidator.__ValidateAndGetGenericKey(pub_key, Sr25519PublicKey)
|
||||
|
||||
@staticmethod
|
||||
def __ValidateAndGetGenericKey(pub_key: Union[bytes, IPublicKey],
|
||||
pub_key_cls: Type[IPublicKey]) -> IPublicKey:
|
||||
"""
|
||||
Validate and get a generic public key.
|
||||
|
||||
Args:
|
||||
pub_key (bytes or IPublicKey object): Public key bytes or object
|
||||
pub_key_cls (IPublicKey) : Public key class type
|
||||
|
||||
Returns:
|
||||
IPublicKey object: IPublicKey object
|
||||
|
||||
Raises:
|
||||
TypeError: If the public key is not of the correct class type
|
||||
ValueError: If the public key is not valid
|
||||
"""
|
||||
if isinstance(pub_key, bytes):
|
||||
pub_key = pub_key_cls.FromBytes(pub_key)
|
||||
elif not isinstance(pub_key, pub_key_cls):
|
||||
curve = EllipticCurveGetter.FromType(pub_key_cls.CurveType())
|
||||
raise TypeError(f"A {curve.Name()} public key is required"
|
||||
f"(expected: {pub_key_cls}, got: {type(pub_key)}")
|
||||
|
||||
return pub_key
|
||||
@ -0,0 +1,48 @@
|
||||
# Copyright (c) 2021 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""Module with interface for address encoding classes."""
|
||||
|
||||
# Imports
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Any
|
||||
|
||||
|
||||
class IAddrDecoder(ABC):
|
||||
"""Address decoder interface."""
|
||||
|
||||
@staticmethod
|
||||
@abstractmethod
|
||||
def DecodeAddr(addr: str,
|
||||
**kwargs: Any) -> bytes:
|
||||
"""
|
||||
Decode an address to bytes.
|
||||
Depending on the coin, the result can be a public key or a public key hash bytes.
|
||||
|
||||
Args:
|
||||
addr (str): Address string
|
||||
**kwargs : Arbitrary arguments depending on the address type
|
||||
|
||||
Returns:
|
||||
bytes: Public key bytes or public key hash
|
||||
|
||||
Raises:
|
||||
ValueError: If the address encoding is not valid
|
||||
"""
|
||||
@ -0,0 +1,50 @@
|
||||
# Copyright (c) 2021 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""Module with interface for address encoding classes."""
|
||||
|
||||
# Imports
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Any, Union
|
||||
|
||||
from ..ecc import IPublicKey
|
||||
|
||||
|
||||
class IAddrEncoder(ABC):
|
||||
"""Address encoder interface."""
|
||||
|
||||
@staticmethod
|
||||
@abstractmethod
|
||||
def EncodeKey(pub_key: Union[bytes, IPublicKey],
|
||||
**kwargs: Any) -> str:
|
||||
"""
|
||||
Encode public key to address.
|
||||
|
||||
Args:
|
||||
pub_key (bytes or IPublicKey): Public key bytes or object
|
||||
**kwargs : Arbitrary arguments depending on the address type
|
||||
|
||||
Returns:
|
||||
str: Address string
|
||||
|
||||
Raised:
|
||||
ValueError: If the public key is not valid
|
||||
TypeError: If the public key is not of the correct type (it depends on the address type)
|
||||
"""
|
||||
@ -0,0 +1,3 @@
|
||||
from .base58 import Base58Alphabets, Base58Decoder, Base58Encoder
|
||||
from .base58_ex import Base58ChecksumError
|
||||
from .base58_xmr import Base58XmrDecoder, Base58XmrEncoder
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,207 @@
|
||||
# Copyright (c) 2021 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""Module for base58 decoding/encoding."""
|
||||
|
||||
# Imports
|
||||
from enum import Enum, auto, unique
|
||||
from typing import Dict
|
||||
|
||||
from .base58_ex import Base58ChecksumError
|
||||
from ..utils.crypto import DoubleSha256
|
||||
from ..utils.misc import BytesUtils
|
||||
|
||||
|
||||
@unique
|
||||
class Base58Alphabets(Enum):
|
||||
"""Enumerative for Base58 alphabet."""
|
||||
|
||||
BITCOIN = auto()
|
||||
RIPPLE = auto()
|
||||
|
||||
|
||||
class Base58Const:
|
||||
"""Class container for Base58 constants."""
|
||||
|
||||
# Base58 radix
|
||||
RADIX: int = 58
|
||||
# Checksum length in bytes
|
||||
CHECKSUM_BYTE_LEN: int = 4
|
||||
# Alphabets
|
||||
ALPHABETS: Dict[Base58Alphabets, str] = {
|
||||
Base58Alphabets.BITCOIN: "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz",
|
||||
Base58Alphabets.RIPPLE: "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz",
|
||||
}
|
||||
|
||||
|
||||
class Base58Utils:
|
||||
"""Class container for Base58 utility functions."""
|
||||
|
||||
@staticmethod
|
||||
def ComputeChecksum(data_bytes: bytes) -> bytes:
|
||||
"""
|
||||
Compute Base58 checksum.
|
||||
|
||||
Args:
|
||||
data_bytes (bytes): Data bytes
|
||||
|
||||
Returns:
|
||||
bytes: Computed checksum
|
||||
"""
|
||||
return DoubleSha256.QuickDigest(data_bytes)[:Base58Const.CHECKSUM_BYTE_LEN]
|
||||
|
||||
|
||||
class Base58Encoder:
|
||||
"""Base58 encoder class. It provides methods for encoding and checksum encoding to Base58 format."""
|
||||
|
||||
@staticmethod
|
||||
def Encode(data_bytes: bytes,
|
||||
alph_idx: Base58Alphabets = Base58Alphabets.BITCOIN) -> str:
|
||||
"""
|
||||
Encode bytes into a Base58 string.
|
||||
|
||||
Args:
|
||||
data_bytes (bytes) : Data bytes
|
||||
alph_idx (Base58Alphabets, optional): Alphabet index, Bitcoin by default
|
||||
|
||||
Returns:
|
||||
str: Encoded string
|
||||
|
||||
Raises:
|
||||
TypeError: If alphabet index is not a Base58Alphabets enumerative
|
||||
"""
|
||||
if not isinstance(alph_idx, Base58Alphabets):
|
||||
raise TypeError("Alphabet index is not an enumerative of Base58Alphabets")
|
||||
|
||||
enc = ""
|
||||
|
||||
# Get alphabet
|
||||
alphabet = Base58Const.ALPHABETS[alph_idx]
|
||||
|
||||
# Convert bytes to integer
|
||||
val = BytesUtils.ToInteger(data_bytes)
|
||||
|
||||
# Algorithm implementation
|
||||
while val > 0:
|
||||
val, mod = divmod(val, Base58Const.RADIX)
|
||||
enc = alphabet[mod] + enc
|
||||
|
||||
# Get number of leading zeros
|
||||
n = len(data_bytes) - len(data_bytes.lstrip(b"\x00"))
|
||||
# Add padding
|
||||
return (alphabet[0] * n) + enc
|
||||
|
||||
@staticmethod
|
||||
def CheckEncode(data_bytes: bytes,
|
||||
alph_idx: Base58Alphabets = Base58Alphabets.BITCOIN) -> str:
|
||||
"""
|
||||
Encode bytes into Base58 string with checksum.
|
||||
|
||||
Args:
|
||||
data_bytes (bytes) : Data bytes
|
||||
alph_idx (Base58Alphabets, optional): Alphabet index, Bitcoin by default
|
||||
|
||||
Returns:
|
||||
str: Encoded string with checksum
|
||||
|
||||
Raises:
|
||||
TypeError: If alphabet index is not a Base58Alphabets enumerative
|
||||
"""
|
||||
|
||||
# Append checksum and encode all together
|
||||
return Base58Encoder.Encode(data_bytes + Base58Utils.ComputeChecksum(data_bytes), alph_idx)
|
||||
|
||||
|
||||
class Base58Decoder:
|
||||
"""Base58 decoder class. It provides methods for decoding and checksum decoding Base58 format."""
|
||||
|
||||
@staticmethod
|
||||
def Decode(data_str: str,
|
||||
alph_idx: Base58Alphabets = Base58Alphabets.BITCOIN) -> bytes:
|
||||
"""
|
||||
Decode bytes from a Base58 string.
|
||||
|
||||
Args:
|
||||
data_str (str) : Data string
|
||||
alph_idx (Base58Alphabets, optional): Alphabet index, Bitcoin by default
|
||||
|
||||
Returns:
|
||||
bytes: Decoded bytes
|
||||
|
||||
Raises:
|
||||
TypeError: If alphabet index is not a Base58Alphabets enumerative
|
||||
"""
|
||||
if not isinstance(alph_idx, Base58Alphabets):
|
||||
raise TypeError("Alphabet index is not an enumerative of Base58Alphabets")
|
||||
|
||||
# Get alphabet
|
||||
alphabet = Base58Const.ALPHABETS[alph_idx]
|
||||
|
||||
# Convert string to integer
|
||||
val = 0
|
||||
for i, c in enumerate(data_str[::-1]):
|
||||
val += alphabet.index(c) * (Base58Const.RADIX ** i)
|
||||
|
||||
dec = bytearray()
|
||||
while val > 0:
|
||||
val, mod = divmod(val, 2**8)
|
||||
dec.append(mod)
|
||||
|
||||
# Get padding length
|
||||
pad_len = len(data_str) - len(data_str.lstrip(alphabet[0]))
|
||||
# Add padding
|
||||
return (b"\x00" * pad_len) + bytes(dec[::-1])
|
||||
|
||||
@staticmethod
|
||||
def CheckDecode(data_str: str,
|
||||
alph_idx: Base58Alphabets = Base58Alphabets.BITCOIN) -> bytes:
|
||||
"""
|
||||
Decode bytes from a Base58 string with checksum.
|
||||
|
||||
Args:
|
||||
data_str (str) : Data string
|
||||
alph_idx (Base58Alphabets, optional): Alphabet index, Bitcoin by default
|
||||
|
||||
Returns:
|
||||
bytes: Decoded bytes (checksum removed)
|
||||
|
||||
Raises:
|
||||
ValueError: If the string is not a valid Base58 format
|
||||
TypeError: If alphabet index is not a Base58Alphabets enumerative
|
||||
Base58ChecksumError: If checksum is not valid
|
||||
"""
|
||||
|
||||
# Decode string
|
||||
dec_bytes = Base58Decoder.Decode(data_str, alph_idx)
|
||||
# Get data and checksum bytes
|
||||
data_bytes = dec_bytes[:-Base58Const.CHECKSUM_BYTE_LEN]
|
||||
checksum_bytes = dec_bytes[-Base58Const.CHECKSUM_BYTE_LEN:]
|
||||
|
||||
# Compute checksum
|
||||
checksum_bytes_got = Base58Utils.ComputeChecksum(data_bytes)
|
||||
|
||||
# Verify checksum
|
||||
if checksum_bytes != checksum_bytes_got:
|
||||
raise Base58ChecksumError(
|
||||
f"Invalid checksum (expected {BytesUtils.ToHexString(checksum_bytes_got)}, "
|
||||
f"got {BytesUtils.ToHexString(checksum_bytes)})"
|
||||
)
|
||||
|
||||
return data_bytes
|
||||
@ -0,0 +1,25 @@
|
||||
# Copyright (c) 2021 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""Module for base58 exceptions."""
|
||||
|
||||
|
||||
class Base58ChecksumError(Exception):
|
||||
"""Exception in case of checksum error."""
|
||||
@ -0,0 +1,155 @@
|
||||
# Copyright (c) 2021 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""Module for base58-monero decoding/encoding."""
|
||||
|
||||
# Imports
|
||||
from typing import List
|
||||
|
||||
from .base58 import Base58Alphabets, Base58Const, Base58Decoder, Base58Encoder
|
||||
|
||||
|
||||
class Base58XmrConst:
|
||||
"""Class container for Base58 Monero constants."""
|
||||
|
||||
# Alphabet
|
||||
ALPHABET: str = Base58Const.ALPHABETS[Base58Alphabets.BITCOIN]
|
||||
|
||||
# Block decoded maximum length in bytes
|
||||
BLOCK_DEC_MAX_BYTE_LEN: int = 8
|
||||
|
||||
# Block encoded maximum length in bytes
|
||||
BLOCK_ENC_MAX_BYTE_LEN: int = 11
|
||||
# Block encoded lengths in bytes
|
||||
BLOCK_ENC_BYTE_LENS: List[int] = [0, 2, 3, 5, 6, 7, 9, 10, 11]
|
||||
|
||||
|
||||
class Base58XmrEncoder:
|
||||
"""
|
||||
Base58 Monero encoder class.
|
||||
It provides methods for encoding to Base58 format with Monero variation (encoding by blocks of 8-byte).
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def Encode(data_bytes: bytes) -> str:
|
||||
"""
|
||||
Encode bytes into a Base58 string with Monero variation.
|
||||
|
||||
Args:
|
||||
data_bytes (bytes): Data bytes
|
||||
|
||||
Returns:
|
||||
str: Encoded string
|
||||
"""
|
||||
enc = ""
|
||||
|
||||
# Get lengths
|
||||
data_len = len(data_bytes)
|
||||
block_dec_len = Base58XmrConst.BLOCK_DEC_MAX_BYTE_LEN
|
||||
|
||||
# Compute total block count and last block length
|
||||
tot_block_cnt, last_block_enc_len = divmod(data_len, block_dec_len)
|
||||
|
||||
# Encode each single block and pad
|
||||
for i in range(tot_block_cnt):
|
||||
block_enc = Base58Encoder.Encode(data_bytes[i * block_dec_len:(i + 1) * block_dec_len])
|
||||
enc += Base58XmrEncoder.__Pad(block_enc, Base58XmrConst.BLOCK_ENC_MAX_BYTE_LEN)
|
||||
|
||||
# Encode last block and pad
|
||||
if last_block_enc_len > 0:
|
||||
block_enc = Base58Encoder.Encode(
|
||||
data_bytes[tot_block_cnt * block_dec_len:(tot_block_cnt * block_dec_len) + last_block_enc_len])
|
||||
enc += Base58XmrEncoder.__Pad(block_enc, Base58XmrConst.BLOCK_ENC_BYTE_LENS[last_block_enc_len])
|
||||
|
||||
return enc
|
||||
|
||||
@staticmethod
|
||||
def __Pad(enc_str: str,
|
||||
pad_len: int) -> str:
|
||||
"""
|
||||
Pad the encoded string to the specified length.
|
||||
|
||||
Args:
|
||||
enc_str (str): Encoded string
|
||||
pad_len (int): Pad length
|
||||
|
||||
Returns:
|
||||
str: Padded string
|
||||
"""
|
||||
return enc_str.rjust(pad_len, Base58XmrConst.ALPHABET[0])
|
||||
|
||||
|
||||
class Base58XmrDecoder:
|
||||
"""
|
||||
Base58 Monero decoder class.
|
||||
It provides methods for decoding Base58 format with Monero variation (encoding by blocks of 8-byte).
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def Decode(data_str: str) -> bytes:
|
||||
"""
|
||||
Decode bytes from a Base58 string with Monero variation.
|
||||
|
||||
Args:
|
||||
data_str (str): Data string
|
||||
|
||||
Returns:
|
||||
bytes: Decoded bytes
|
||||
"""
|
||||
dec = b""
|
||||
|
||||
# Get lengths
|
||||
data_len = len(data_str)
|
||||
block_dec_len = Base58XmrConst.BLOCK_DEC_MAX_BYTE_LEN
|
||||
block_enc_len = Base58XmrConst.BLOCK_ENC_MAX_BYTE_LEN
|
||||
|
||||
# Compute block count and last block length
|
||||
tot_block_cnt, last_block_enc_len = divmod(data_len, block_enc_len)
|
||||
|
||||
# Get last block decoded length
|
||||
last_block_dec_len = Base58XmrConst.BLOCK_ENC_BYTE_LENS.index(last_block_enc_len)
|
||||
|
||||
# Decode each single block and unpad
|
||||
for i in range(tot_block_cnt):
|
||||
block_dec = Base58Decoder.Decode(data_str[(i * block_enc_len):((i + 1) * block_enc_len)])
|
||||
dec += Base58XmrDecoder.__UnPad(block_dec, block_dec_len)
|
||||
|
||||
# Decode last block and unpad
|
||||
if last_block_enc_len > 0:
|
||||
block_dec = Base58Decoder.Decode(
|
||||
data_str[(tot_block_cnt * block_enc_len):((tot_block_cnt * block_enc_len) + last_block_enc_len)])
|
||||
dec += Base58XmrDecoder.__UnPad(block_dec, last_block_dec_len)
|
||||
|
||||
return dec
|
||||
|
||||
@staticmethod
|
||||
def __UnPad(dec_bytes: bytes,
|
||||
unpad_len: int) -> bytes:
|
||||
"""
|
||||
Unpad the decoded string to the specified length.
|
||||
|
||||
Args:
|
||||
dec_bytes (bytes): Decoded bytes
|
||||
unpad_len (int): Unpad length
|
||||
|
||||
Returns:
|
||||
bytes: Unpadded string
|
||||
"""
|
||||
return dec_bytes[len(dec_bytes) - unpad_len:len(dec_bytes)]
|
||||
@ -0,0 +1,4 @@
|
||||
from .bch_bech32 import BchBech32Decoder, BchBech32Encoder
|
||||
from .bech32 import Bech32Decoder, Bech32Encoder
|
||||
from .bech32_ex import Bech32ChecksumError
|
||||
from .segwit_bech32 import SegwitBech32Decoder, SegwitBech32Encoder
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,220 @@
|
||||
# Copyright (c) 2021 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""
|
||||
Module for BitcoinCash bech32 decoding/encoding.
|
||||
Reference: https://github.com/bitcoincashorg/bitcoincash.org/blob/master/spec/cashaddr.md
|
||||
"""
|
||||
|
||||
# Imports
|
||||
from typing import List, Tuple
|
||||
|
||||
from .bech32_base import Bech32BaseUtils, Bech32DecoderBase, Bech32EncoderBase
|
||||
from ..utils.misc import BytesUtils, IntegerUtils
|
||||
|
||||
|
||||
class BchBech32Const:
|
||||
"""Class container for Bitcoin Cash Bech32 constants."""
|
||||
|
||||
# Separator
|
||||
SEPARATOR: str = ":"
|
||||
# Checksum length
|
||||
CHECKSUM_STR_LEN: int = 8
|
||||
|
||||
|
||||
class BchBech32Utils:
|
||||
"""Class container for Bitcoin Cash utility functions."""
|
||||
|
||||
@staticmethod
|
||||
def PolyMod(values: List[int]) -> int:
|
||||
"""
|
||||
Computes the polynomial modulus.
|
||||
|
||||
Args:
|
||||
values (list[int]): List of polynomial coefficients
|
||||
|
||||
Returns:
|
||||
int: Computed modulus
|
||||
"""
|
||||
|
||||
# Generator polynomial
|
||||
generator = [
|
||||
(0x01, 0x98f2bc8e61),
|
||||
(0x02, 0x79b76d99e2),
|
||||
(0x04, 0xf33e5fb3c4),
|
||||
(0x08, 0xae2eabe2a8),
|
||||
(0x10, 0x1e4f43e470)
|
||||
]
|
||||
# Compute modulus
|
||||
chk = 1
|
||||
for value in values:
|
||||
top = chk >> 35
|
||||
chk = ((chk & 0x07ffffffff) << 5) ^ value
|
||||
for i in generator:
|
||||
if top & i[0] != 0:
|
||||
chk ^= i[1]
|
||||
|
||||
return chk ^ 1
|
||||
|
||||
@staticmethod
|
||||
def HrpExpand(hrp: str) -> List[int]:
|
||||
"""
|
||||
Expand the HRP into values for checksum computation.
|
||||
|
||||
Args:
|
||||
hrp (str): HRP
|
||||
|
||||
Returns:
|
||||
list[int]: Expanded HRP values
|
||||
"""
|
||||
# [lower 5 bits of each character] + [0]
|
||||
return [ord(x) & 0x1f for x in hrp] + [0]
|
||||
|
||||
@staticmethod
|
||||
def ComputeChecksum(hrp: str,
|
||||
data: List[int]) -> List[int]:
|
||||
"""
|
||||
Compute the checksum from the specified HRP and data.
|
||||
|
||||
Args:
|
||||
hrp (str) : HRP
|
||||
data (list[int]): Data part
|
||||
|
||||
Returns:
|
||||
list[int]: Computed checksum
|
||||
"""
|
||||
values = BchBech32Utils.HrpExpand(hrp) + data
|
||||
polymod = BchBech32Utils.PolyMod(values + [0, 0, 0, 0, 0, 0, 0, 0])
|
||||
return [(polymod >> 5 * (7 - i)) & 0x1f for i in range(BchBech32Const.CHECKSUM_STR_LEN)]
|
||||
|
||||
@staticmethod
|
||||
def VerifyChecksum(hrp: str,
|
||||
data: List[int]) -> bool:
|
||||
"""
|
||||
Verify the checksum from the specified HRP and converted data characters.
|
||||
|
||||
Args:
|
||||
hrp (str) : HRP
|
||||
data (list[int]): Data part
|
||||
|
||||
Returns:
|
||||
bool: True if valid, false otherwise
|
||||
"""
|
||||
return BchBech32Utils.PolyMod(BchBech32Utils.HrpExpand(hrp) + data) == 0
|
||||
|
||||
|
||||
class BchBech32Encoder(Bech32EncoderBase):
|
||||
"""
|
||||
Bitcoin Cash Bech32 encoder class.
|
||||
It provides methods for encoding to Bitcoin Cash Bech32 format.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def Encode(cls,
|
||||
hrp: str,
|
||||
net_ver: bytes,
|
||||
data: bytes) -> str:
|
||||
"""
|
||||
Encode to Bitcoin Cash Bech32.
|
||||
|
||||
Args:
|
||||
hrp (str) : HRP
|
||||
net_ver (bytes): Net version
|
||||
data (bytes) : Data
|
||||
|
||||
Returns:
|
||||
str: Encoded address
|
||||
|
||||
Raises:
|
||||
ValueError: If the data is not valid
|
||||
"""
|
||||
return cls._EncodeBech32(hrp,
|
||||
Bech32BaseUtils.ConvertToBase32(net_ver + data),
|
||||
BchBech32Const.SEPARATOR)
|
||||
|
||||
@staticmethod
|
||||
def _ComputeChecksum(hrp: str,
|
||||
data: List[int]) -> List[int]:
|
||||
"""
|
||||
Compute the checksum from the specified HRP and data.
|
||||
|
||||
Args:
|
||||
hrp (str) : HRP
|
||||
data (list[int]): Data part
|
||||
|
||||
Returns:
|
||||
list[int]: Computed checksum
|
||||
"""
|
||||
return BchBech32Utils.ComputeChecksum(hrp, data)
|
||||
|
||||
|
||||
class BchBech32Decoder(Bech32DecoderBase):
|
||||
"""
|
||||
Bitcoin Cash Bech32 decoder class.
|
||||
It provides methods for decoding Bitcoin Cash Bech32 format.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def Decode(cls,
|
||||
hrp: str,
|
||||
addr: str) -> Tuple[bytes, bytes]:
|
||||
"""
|
||||
Decode from Bitcoin Cash Bech32.
|
||||
|
||||
Args:
|
||||
hrp (str) : Human readable part
|
||||
addr (str): Address
|
||||
|
||||
Returns:
|
||||
tuple[bytes, bytes]: Net version (index 0) and data (index 1)
|
||||
|
||||
Raises:
|
||||
ValueError: If the bech32 string is not valid
|
||||
Bech32ChecksumError: If the checksum is not valid
|
||||
"""
|
||||
|
||||
# Decode string
|
||||
hrp_got, data = cls._DecodeBech32(addr,
|
||||
BchBech32Const.SEPARATOR,
|
||||
BchBech32Const.CHECKSUM_STR_LEN)
|
||||
|
||||
# Check HRP
|
||||
if hrp != hrp_got:
|
||||
raise ValueError(f"Invalid format (HRP not valid, expected {hrp}, got {hrp_got})")
|
||||
|
||||
# Convert back from base32
|
||||
conv_data = Bech32BaseUtils.ConvertFromBase32(data)
|
||||
|
||||
return IntegerUtils.ToBytes(conv_data[0]), BytesUtils.FromList(conv_data[1:])
|
||||
|
||||
@staticmethod
|
||||
def _VerifyChecksum(hrp: str,
|
||||
data: List[int]) -> bool:
|
||||
"""
|
||||
Verify the checksum from the specified HRP and converted data characters.
|
||||
|
||||
Args:
|
||||
hrp (str) : HRP
|
||||
data (list[int]): Data part
|
||||
|
||||
Returns:
|
||||
bool: True if valid, false otherwise
|
||||
"""
|
||||
return BchBech32Utils.VerifyChecksum(hrp, data)
|
||||
@ -0,0 +1,235 @@
|
||||
# Copyright (c) 2021 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""
|
||||
Module for bech32/bech32m decoding/encoding.
|
||||
|
||||
References:
|
||||
https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki
|
||||
https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki
|
||||
https://github.com/sipa/bech32/blob/master/ref/python/segwit_addr.py
|
||||
"""
|
||||
|
||||
# Imports
|
||||
from enum import Enum, auto, unique
|
||||
from typing import Dict, List
|
||||
|
||||
from .bech32_base import Bech32BaseUtils, Bech32DecoderBase, Bech32EncoderBase
|
||||
from ..utils.misc import BytesUtils
|
||||
|
||||
|
||||
@unique
|
||||
class Bech32Encodings(Enum):
|
||||
"""Enumerative for Bech32 encoding types."""
|
||||
|
||||
BECH32 = auto()
|
||||
BECH32M = auto()
|
||||
|
||||
|
||||
class Bech32Const:
|
||||
"""Class container for Bech32 constants."""
|
||||
|
||||
# Separator
|
||||
SEPARATOR: str = "1"
|
||||
# Checksum length
|
||||
CHECKSUM_STR_LEN: int = 6
|
||||
# Encoding checksum constants
|
||||
ENCODING_CHECKSUM_CONST: Dict[Bech32Encodings, int] = {
|
||||
Bech32Encodings.BECH32: 1,
|
||||
Bech32Encodings.BECH32M: 0x2bc830a3,
|
||||
}
|
||||
|
||||
|
||||
class Bech32Utils:
|
||||
"""Class container for Bech32 utility functions."""
|
||||
|
||||
@staticmethod
|
||||
def PolyMod(values: List[int]) -> int:
|
||||
"""
|
||||
Computes the polynomial modulus.
|
||||
|
||||
Args:
|
||||
values (list[int]): List of polynomial coefficients
|
||||
|
||||
Returns:
|
||||
int: Computed modulus
|
||||
"""
|
||||
|
||||
# Generator polynomial
|
||||
generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]
|
||||
|
||||
# Compute modulus
|
||||
chk = 1
|
||||
for value in values:
|
||||
top = chk >> 25
|
||||
chk = (chk & 0x1ffffff) << 5 ^ value
|
||||
for i in range(5):
|
||||
chk ^= generator[i] if ((top >> i) & 1) else 0
|
||||
return chk
|
||||
|
||||
@staticmethod
|
||||
def HrpExpand(hrp: str) -> List[int]:
|
||||
"""
|
||||
Expand the HRP into values for checksum computation.
|
||||
|
||||
Args:
|
||||
hrp (str): HRP
|
||||
|
||||
Returns:
|
||||
list[int]: Expanded HRP values
|
||||
"""
|
||||
# [upper 3 bits of each character] + [0] + [lower 5 bits of each character]
|
||||
return [ord(x) >> 5 for x in hrp] + [0] + [ord(x) & 0x1f for x in hrp]
|
||||
|
||||
@staticmethod
|
||||
def ComputeChecksum(hrp: str,
|
||||
data: List[int],
|
||||
encoding: Bech32Encodings = Bech32Encodings.BECH32) -> List[int]:
|
||||
"""
|
||||
Compute the checksum from the specified HRP and data.
|
||||
|
||||
Args:
|
||||
hrp (str) : HRP
|
||||
data (list[int]) : Data part
|
||||
encoding (Bech32Encodings, optional): Encoding type (BECH32 by default)
|
||||
|
||||
Returns:
|
||||
list[int]: Computed checksum
|
||||
"""
|
||||
values = Bech32Utils.HrpExpand(hrp) + data
|
||||
polymod = Bech32Utils.PolyMod(values + [0, 0, 0, 0, 0, 0]) ^ Bech32Const.ENCODING_CHECKSUM_CONST[encoding]
|
||||
return [(polymod >> 5 * (5 - i)) & 0x1f for i in range(Bech32Const.CHECKSUM_STR_LEN)]
|
||||
|
||||
@staticmethod
|
||||
def VerifyChecksum(hrp: str,
|
||||
data: List[int],
|
||||
encoding: Bech32Encodings = Bech32Encodings.BECH32) -> bool:
|
||||
"""
|
||||
Verify the checksum from the specified HRP and converted data characters.
|
||||
|
||||
Args:
|
||||
hrp (str) : HRP
|
||||
data (list[int]) : Data part
|
||||
encoding (Bech32Encodings, optional): Encoding type (BECH32 by default)
|
||||
|
||||
Returns:
|
||||
bool: True if valid, false otherwise
|
||||
"""
|
||||
polymod = Bech32Utils.PolyMod(Bech32Utils.HrpExpand(hrp) + data)
|
||||
return polymod == Bech32Const.ENCODING_CHECKSUM_CONST[encoding]
|
||||
|
||||
|
||||
class Bech32Encoder(Bech32EncoderBase):
|
||||
"""
|
||||
Bech32 encoder class.
|
||||
It provides methods for encoding to Bech32 format.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def Encode(cls,
|
||||
hrp: str,
|
||||
data: bytes) -> str:
|
||||
"""
|
||||
Encode to Bech32.
|
||||
|
||||
Args:
|
||||
hrp (str) : HRP
|
||||
data (bytes): Data
|
||||
|
||||
Returns:
|
||||
str: Encoded address
|
||||
|
||||
Raises:
|
||||
ValueError: If the data is not valid
|
||||
"""
|
||||
return cls._EncodeBech32(hrp,
|
||||
Bech32BaseUtils.ConvertToBase32(data),
|
||||
Bech32Const.SEPARATOR)
|
||||
|
||||
@staticmethod
|
||||
def _ComputeChecksum(hrp: str,
|
||||
data: List[int]) -> List[int]:
|
||||
"""
|
||||
Compute the checksum from the specified HRP and data.
|
||||
|
||||
Args:
|
||||
hrp (str) : HRP
|
||||
data (list[int]): Data part
|
||||
|
||||
Returns:
|
||||
list[int]: Computed checksum
|
||||
"""
|
||||
|
||||
# Same as Segwit
|
||||
return Bech32Utils.ComputeChecksum(hrp, data)
|
||||
|
||||
|
||||
class Bech32Decoder(Bech32DecoderBase):
|
||||
"""
|
||||
Bech32 decoder class.
|
||||
It provides methods for decoding Bech32 format.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def Decode(cls,
|
||||
hrp: str,
|
||||
addr: str) -> bytes:
|
||||
"""
|
||||
Decode from Bech32.
|
||||
|
||||
Args:
|
||||
hrp (str) : Human readable part
|
||||
addr (str): Address
|
||||
|
||||
Returns:
|
||||
bytes: Decoded address
|
||||
|
||||
Raises:
|
||||
ValueError: If the bech32 string is not valid
|
||||
Bech32ChecksumError: If the checksum is not valid
|
||||
"""
|
||||
|
||||
# Decode string
|
||||
hrp_got, data = cls._DecodeBech32(addr,
|
||||
Bech32Const.SEPARATOR,
|
||||
Bech32Const.CHECKSUM_STR_LEN)
|
||||
# Check HRP
|
||||
if hrp != hrp_got:
|
||||
raise ValueError(f"Invalid format (HRP not valid, expected {hrp}, got {hrp_got})")
|
||||
|
||||
# Convert back from base32
|
||||
return BytesUtils.FromList(
|
||||
Bech32BaseUtils.ConvertFromBase32(data)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _VerifyChecksum(hrp: str,
|
||||
data: List[int]) -> bool:
|
||||
"""
|
||||
Verify the checksum from the specified HRP and converted data characters.
|
||||
|
||||
Args:
|
||||
hrp (str) : HRP
|
||||
data (list[int]): Data part
|
||||
|
||||
Returns:
|
||||
bool: True if valid, false otherwise
|
||||
"""
|
||||
return Bech32Utils.VerifyChecksum(hrp, data)
|
||||
@ -0,0 +1,246 @@
|
||||
# Copyright (c) 2021 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""Module for base bech32 decoding/encoding."""
|
||||
|
||||
# Imports
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import List, Optional, Tuple, Union
|
||||
|
||||
from .bech32_ex import Bech32ChecksumError
|
||||
from ..utils.misc import AlgoUtils
|
||||
|
||||
|
||||
class Bech32BaseConst:
|
||||
"""Class container for Bech32 constants."""
|
||||
|
||||
# Character set
|
||||
CHARSET: str = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
|
||||
|
||||
|
||||
class Bech32BaseUtils:
|
||||
"""Class container for Bech32 utility functions."""
|
||||
|
||||
@staticmethod
|
||||
def ConvertToBase32(data: Union[List[int], bytes]) -> List[int]:
|
||||
"""
|
||||
Convert data to base32.
|
||||
|
||||
Args:
|
||||
data (list[int] or bytes): Data to be converted
|
||||
|
||||
Returns:
|
||||
list[int]: Converted data
|
||||
|
||||
Raises:
|
||||
ValueError: If the string is not valid
|
||||
"""
|
||||
|
||||
# Convert to base32
|
||||
conv_data = Bech32BaseUtils.ConvertBits(data, 8, 5)
|
||||
if conv_data is None:
|
||||
raise ValueError("Invalid data, cannot perform conversion to base32")
|
||||
|
||||
return conv_data
|
||||
|
||||
@staticmethod
|
||||
def ConvertFromBase32(data: Union[List[int], bytes]) -> List[int]:
|
||||
"""
|
||||
Convert data from base32.
|
||||
|
||||
Args:
|
||||
data (list[int] or bytes): Data to be converted
|
||||
|
||||
Returns:
|
||||
list[int]: Converted data
|
||||
|
||||
Raises:
|
||||
ValueError: If the string is not valid
|
||||
"""
|
||||
|
||||
# Convert from base32
|
||||
conv_data = Bech32BaseUtils.ConvertBits(data, 5, 8, False)
|
||||
if conv_data is None:
|
||||
raise ValueError("Invalid data, cannot perform conversion from base32")
|
||||
|
||||
return conv_data
|
||||
|
||||
@staticmethod
|
||||
def ConvertBits(data: Union[bytes, List[int]],
|
||||
from_bits: int,
|
||||
to_bits: int,
|
||||
pad: bool = True) -> Optional[List[int]]:
|
||||
"""
|
||||
Perform bit conversion.
|
||||
The function takes the input data (list of integers or byte sequence) and convert every value from
|
||||
the specified number of bits to the specified one.
|
||||
It returns a list of integer where every number is less than 2^to_bits.
|
||||
|
||||
Args:
|
||||
data (list[int] or bytes): Data to be converted
|
||||
from_bits (int) : Number of bits to start from
|
||||
to_bits (int) : Number of bits to end with
|
||||
pad (bool, optional) : True if data must be padded with zeros, false otherwise
|
||||
|
||||
Returns:
|
||||
list[int]: List of converted values, None in case of errors
|
||||
"""
|
||||
max_out_val = (1 << to_bits) - 1
|
||||
max_acc = (1 << (from_bits + to_bits - 1)) - 1
|
||||
|
||||
acc = 0
|
||||
bits = 0
|
||||
ret = []
|
||||
|
||||
for value in data:
|
||||
# Value shall not be less than zero or greater than 2^from_bits
|
||||
if value < 0 or (value >> from_bits):
|
||||
return None
|
||||
# Continue accumulating until greater than to_bits
|
||||
acc = ((acc << from_bits) | value) & max_acc
|
||||
bits += from_bits
|
||||
while bits >= to_bits:
|
||||
bits -= to_bits
|
||||
ret.append((acc >> bits) & max_out_val)
|
||||
if pad:
|
||||
if bits:
|
||||
# Pad the value with zeros to reach to_bits
|
||||
ret.append((acc << (to_bits - bits)) & max_out_val)
|
||||
elif bits >= from_bits or ((acc << (to_bits - bits)) & max_out_val):
|
||||
return None
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
class Bech32EncoderBase(ABC):
|
||||
"""
|
||||
Bech32 encoder base class.
|
||||
It provides methods for encoding to Bech32 format.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def _EncodeBech32(cls,
|
||||
hrp: str,
|
||||
data: List[int],
|
||||
sep: str) -> str:
|
||||
"""
|
||||
Encode a Bech32 string from the specified HRP and data.
|
||||
|
||||
Args:
|
||||
hrp (str) : HRP
|
||||
data (list[int]): Data part
|
||||
sep (str) : Bech32 separator
|
||||
|
||||
Returns:
|
||||
str: Encoded data
|
||||
"""
|
||||
|
||||
# Add checksum to data
|
||||
data += cls._ComputeChecksum(hrp, data)
|
||||
# Encode to alphabet
|
||||
return hrp + sep + "".join([Bech32BaseConst.CHARSET[d] for d in data])
|
||||
|
||||
@staticmethod
|
||||
@abstractmethod
|
||||
def _ComputeChecksum(hrp: str,
|
||||
data: List[int]) -> List[int]:
|
||||
"""
|
||||
Compute the checksum from the specified HRP and data.
|
||||
|
||||
Args:
|
||||
hrp (str) : HRP
|
||||
data (list[int]): Data part
|
||||
|
||||
Returns:
|
||||
list[int]: Computed checksum
|
||||
"""
|
||||
|
||||
|
||||
class Bech32DecoderBase(ABC):
|
||||
"""
|
||||
Bech32 decoder base class.
|
||||
It provides methods for decoding Bech32 format.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def _DecodeBech32(cls,
|
||||
bech_str: str,
|
||||
sep: str,
|
||||
checksum_len: int) -> Tuple[str, List[int]]:
|
||||
"""
|
||||
Decode and validate a Bech32 string, determining its HRP and data.
|
||||
|
||||
Args:
|
||||
bech_str (str) : Bech32 string
|
||||
sep (str) : Bech32 separator
|
||||
checksum_len (int): Checksum length
|
||||
|
||||
Returns:
|
||||
tuple[str, list[int]]: HRP (index 0) and data part (index 1)
|
||||
|
||||
Raises:
|
||||
ValueError: If the string is not valid
|
||||
Bech32ChecksumError: If the checksum is not valid
|
||||
"""
|
||||
|
||||
# Check string length and case
|
||||
if AlgoUtils.IsStringMixed(bech_str):
|
||||
raise ValueError("Invalid bech32 format (string is mixed case)")
|
||||
|
||||
# Lower string
|
||||
bech_str = bech_str.lower()
|
||||
|
||||
# Find separator and check its position
|
||||
sep_pos = bech_str.rfind(sep)
|
||||
if sep_pos == -1:
|
||||
raise ValueError("Invalid bech32 format (no separator found)")
|
||||
|
||||
# Get HRP and check it
|
||||
hrp = bech_str[:sep_pos]
|
||||
if len(hrp) == 0 or any(ord(x) < 33 or ord(x) > 126 for x in hrp):
|
||||
raise ValueError(f"Invalid bech32 format (HRP not valid: {hrp})")
|
||||
|
||||
# Get data and check it
|
||||
data_part = bech_str[sep_pos + 1:]
|
||||
if (len(data_part) < (checksum_len + 1)
|
||||
or not all(x in Bech32BaseConst.CHARSET for x in data_part)):
|
||||
raise ValueError("Invalid bech32 format (data part not valid)")
|
||||
|
||||
# Convert back from alphabet and verify checksum
|
||||
int_data = [Bech32BaseConst.CHARSET.find(x) for x in data_part]
|
||||
if not cls._VerifyChecksum(hrp, int_data):
|
||||
raise Bech32ChecksumError("Invalid bech32 checksum")
|
||||
|
||||
return hrp, int_data[:-checksum_len]
|
||||
|
||||
@staticmethod
|
||||
@abstractmethod
|
||||
def _VerifyChecksum(hrp: str,
|
||||
data: List[int]) -> bool:
|
||||
"""
|
||||
Verify the checksum from the specified HRP and converted data characters.
|
||||
|
||||
Args:
|
||||
hrp (str) : HRP
|
||||
data (list[int]): Data part
|
||||
|
||||
Returns:
|
||||
bool: True if valid, false otherwise
|
||||
"""
|
||||
@ -0,0 +1,25 @@
|
||||
# Copyright (c) 2021 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""Module for bech32 exceptions."""
|
||||
|
||||
|
||||
class Bech32ChecksumError(Exception):
|
||||
"""Exception in case of checksum error."""
|
||||
@ -0,0 +1,173 @@
|
||||
# Copyright (c) 2021 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""
|
||||
Module for segwit bech32/bech32m decoding/encoding.
|
||||
|
||||
References:
|
||||
https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki
|
||||
https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki
|
||||
"""
|
||||
|
||||
# Imports
|
||||
from typing import List, Tuple
|
||||
|
||||
from .bech32 import Bech32Const, Bech32Encodings, Bech32Utils
|
||||
from .bech32_base import Bech32BaseUtils, Bech32DecoderBase, Bech32EncoderBase
|
||||
from ..utils.misc import BytesUtils
|
||||
|
||||
|
||||
class SegwitBech32Const:
|
||||
"""Class container for Segwit Bech32 constants."""
|
||||
|
||||
# Separator
|
||||
SEPARATOR: str = Bech32Const.SEPARATOR
|
||||
# Checksum length
|
||||
CHECKSUM_STR_LEN: int = Bech32Const.CHECKSUM_STR_LEN
|
||||
# Minimum witness program length in bytes
|
||||
WITNESS_PROG_MIN_BYTE_LEN: int = 2
|
||||
# Maximum witness program length in bytes
|
||||
WITNESS_PROG_MAX_BYTE_LEN: int = 40
|
||||
# Witness version for Bech32 encoding
|
||||
WITNESS_VER_BECH32: int = 0
|
||||
# Witness version maximum value
|
||||
WITNESS_VER_MAX_VAL: int = 16
|
||||
# Accepted data lengths when witness version is zero
|
||||
WITNESS_VER_ZERO_DATA_BYTE_LEN: Tuple[int, int] = (20, 32)
|
||||
|
||||
|
||||
class SegwitBech32Encoder(Bech32EncoderBase):
|
||||
"""
|
||||
Segwit Bech32 encoder class.
|
||||
It provides methods for encoding to Segwit Bech32 format.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def Encode(cls,
|
||||
hrp: str,
|
||||
wit_ver: int,
|
||||
wit_prog: bytes) -> str:
|
||||
"""
|
||||
Encode to Segwit Bech32.
|
||||
|
||||
Args:
|
||||
hrp (str) : HRP
|
||||
wit_ver (int) : Witness version
|
||||
wit_prog (bytes): Witness program
|
||||
|
||||
Returns:
|
||||
str: Encoded address
|
||||
|
||||
Raises:
|
||||
ValueError: If the data is not valid
|
||||
"""
|
||||
return cls._EncodeBech32(hrp,
|
||||
[wit_ver] + Bech32BaseUtils.ConvertToBase32(wit_prog),
|
||||
SegwitBech32Const.SEPARATOR)
|
||||
|
||||
@staticmethod
|
||||
def _ComputeChecksum(hrp: str,
|
||||
data: List[int]) -> List[int]:
|
||||
"""
|
||||
Compute the checksum from the specified HRP and data.
|
||||
|
||||
Args:
|
||||
hrp (str) : HRP
|
||||
data (list[int]): Data part
|
||||
|
||||
Returns:
|
||||
list[int]: Computed checksum
|
||||
"""
|
||||
encoding = (Bech32Encodings.BECH32
|
||||
if data[0] == SegwitBech32Const.WITNESS_VER_BECH32
|
||||
else Bech32Encodings.BECH32M)
|
||||
|
||||
return Bech32Utils.ComputeChecksum(hrp, data, encoding)
|
||||
|
||||
|
||||
class SegwitBech32Decoder(Bech32DecoderBase):
|
||||
"""
|
||||
Segwit Bech32 decoder class.
|
||||
It provides methods for decoding Segwit Bech32 format.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def Decode(cls,
|
||||
hrp: str,
|
||||
addr: str) -> Tuple[int, bytes]:
|
||||
"""
|
||||
Decode from Segwit Bech32.
|
||||
|
||||
Args:
|
||||
hrp (str) : Human readable part
|
||||
addr (str): Address
|
||||
|
||||
Returns:
|
||||
tuple[int, bytes]: Witness version (index 0) and witness program (index 1)
|
||||
|
||||
Raises:
|
||||
Bech32ChecksumError: If the checksum is not valid
|
||||
ValueError: If the bech32 string is not valid
|
||||
"""
|
||||
|
||||
# Decode string
|
||||
hrp_got, data = cls._DecodeBech32(addr,
|
||||
SegwitBech32Const.SEPARATOR,
|
||||
SegwitBech32Const.CHECKSUM_STR_LEN)
|
||||
# Check HRP
|
||||
if hrp != hrp_got:
|
||||
raise ValueError(
|
||||
f"Invalid format (HRP not valid, expected {hrp}, got {hrp_got})"
|
||||
)
|
||||
|
||||
# Convert back from base32 (remove witness version)
|
||||
conv_data = Bech32BaseUtils.ConvertFromBase32(data[1:])
|
||||
|
||||
# Check data length
|
||||
if (len(conv_data) < SegwitBech32Const.WITNESS_PROG_MIN_BYTE_LEN
|
||||
or len(conv_data) > SegwitBech32Const.WITNESS_PROG_MAX_BYTE_LEN):
|
||||
raise ValueError(f"Invalid format (witness program length not valid: {len(conv_data)})")
|
||||
# Check witness version
|
||||
wit_ver = data[0]
|
||||
if wit_ver > SegwitBech32Const.WITNESS_VER_MAX_VAL:
|
||||
raise ValueError(f"Invalid format (witness version not valid: {wit_ver})")
|
||||
if wit_ver == 0 and not len(conv_data) in SegwitBech32Const.WITNESS_VER_ZERO_DATA_BYTE_LEN:
|
||||
raise ValueError(f"Invalid format (length not valid: {len(conv_data)})")
|
||||
|
||||
return wit_ver, BytesUtils.FromList(conv_data)
|
||||
|
||||
@staticmethod
|
||||
def _VerifyChecksum(hrp: str,
|
||||
data: List[int]) -> bool:
|
||||
"""
|
||||
Verify the checksum from the specified HRP and converted data characters.
|
||||
|
||||
Args:
|
||||
hrp (str) : HRP
|
||||
data (list[int]): Data part
|
||||
|
||||
Returns:
|
||||
bool: True if valid, false otherwise
|
||||
"""
|
||||
encoding = (Bech32Encodings.BECH32
|
||||
if data[0] == SegwitBech32Const.WITNESS_VER_BECH32
|
||||
else Bech32Encodings.BECH32M)
|
||||
|
||||
return Bech32Utils.VerifyChecksum(hrp, data, encoding)
|
||||
@ -0,0 +1,14 @@
|
||||
from .base import Bip32Base, IBip32KeyDerivator, IBip32MstKeyGenerator
|
||||
from .bip32_const import Bip32Const
|
||||
from .bip32_ex import Bip32KeyError, Bip32PathError
|
||||
from .bip32_key_data import Bip32ChainCode, Bip32Depth, Bip32FingerPrint, Bip32KeyData, Bip32KeyIndex
|
||||
from .bip32_key_net_ver import Bip32KeyNetVersions
|
||||
from .bip32_key_ser import (
|
||||
Bip32DeserializedKey, Bip32KeyDeserializer, Bip32PrivateKeySerializer, Bip32PublicKeySerializer
|
||||
)
|
||||
from .bip32_keys import Bip32PrivateKey, Bip32PublicKey
|
||||
from .bip32_path import Bip32Path, Bip32PathParser
|
||||
from .bip32_utils import Bip32Utils
|
||||
from .slip10 import (
|
||||
Bip32Slip10Secp256k1
|
||||
)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
from .bip32_base import Bip32Base
|
||||
from .ibip32_key_derivator import IBip32KeyDerivator
|
||||
from .ibip32_mst_key_generator import IBip32MstKeyGenerator
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,581 @@
|
||||
# Copyright (c) 2021 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""Module with BIP32 base class."""
|
||||
|
||||
# Imports
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Optional, Type, Union
|
||||
|
||||
from .ibip32_key_derivator import IBip32KeyDerivator
|
||||
from .ibip32_mst_key_generator import IBip32MstKeyGenerator
|
||||
from ..bip32_ex import Bip32KeyError
|
||||
from ..bip32_key_data import Bip32ChainCode, Bip32Depth, Bip32FingerPrint, Bip32KeyData, Bip32KeyIndex
|
||||
from ..bip32_key_net_ver import Bip32KeyNetVersions
|
||||
from ..bip32_key_ser import Bip32KeyDeserializer
|
||||
from ..bip32_keys import Bip32PrivateKey, Bip32PublicKey
|
||||
from ..bip32_path import Bip32Path, Bip32PathParser
|
||||
from ...ecc import EllipticCurve, EllipticCurveGetter, EllipticCurveTypes, IPoint, IPrivateKey, IPublicKey
|
||||
|
||||
|
||||
class Bip32Base(ABC):
|
||||
"""
|
||||
BIP32 base class.
|
||||
It allows master key generation and children keys derivation in according to BIP-0032/SLIP-0010.
|
||||
It shall be derived to implement derivation for a specific elliptic curve.
|
||||
"""
|
||||
|
||||
m_priv_key: Optional[Bip32PrivateKey]
|
||||
m_pub_key: Bip32PublicKey
|
||||
|
||||
#
|
||||
# Class methods for construction
|
||||
#
|
||||
|
||||
@classmethod
|
||||
def FromSeed(cls,
|
||||
seed_bytes: bytes,
|
||||
key_net_ver: Optional[Bip32KeyNetVersions] = None) -> Bip32Base:
|
||||
"""
|
||||
Create a Bip32 object from the specified seed (e.g. BIP39 seed).
|
||||
|
||||
Args:
|
||||
seed_bytes (bytes) : Seed bytes
|
||||
key_net_ver (Bip32KeyNetVersions object, optional): Bip32KeyNetVersions object
|
||||
(default: specific class key net version)
|
||||
|
||||
Returns:
|
||||
Bip32Base object: Bip32Base object
|
||||
|
||||
Raises:
|
||||
ValueError: If the seed is too short
|
||||
Bip32KeyError: If the seed is not suitable for master key generation
|
||||
"""
|
||||
priv_key_bytes, chain_code_bytes = cls._MasterKeyGenerator().GenerateFromSeed(seed_bytes)
|
||||
|
||||
return cls(
|
||||
priv_key=priv_key_bytes,
|
||||
pub_key=None,
|
||||
key_data=Bip32KeyData(chain_code=chain_code_bytes),
|
||||
key_net_ver=key_net_ver or cls._DefaultKeyNetVersion()
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def FromSeedAndPath(cls,
|
||||
seed_bytes: bytes,
|
||||
path: Union[str, Bip32Path],
|
||||
key_net_ver: Optional[Bip32KeyNetVersions] = None) -> Bip32Base:
|
||||
"""
|
||||
Create a Bip32 object from the specified seed (e.g. BIP39 seed) and path.
|
||||
|
||||
Args:
|
||||
seed_bytes (bytes) : Seed bytes
|
||||
path (str or Bip32Path object) : Path
|
||||
key_net_ver (Bip32KeyNetVersions object, optional): Bip32KeyNetVersions object
|
||||
(default: specific class key net version)
|
||||
|
||||
Returns:
|
||||
Bip32Base object: Bip32Base object
|
||||
|
||||
Raises:
|
||||
ValueError: If the seed length is too short
|
||||
Bip32PathError: If the path is not valid
|
||||
Bip32KeyError: If the seed is not suitable for master key generation
|
||||
"""
|
||||
key_net_ver = key_net_ver or cls._DefaultKeyNetVersion()
|
||||
return cls.FromSeed(seed_bytes, key_net_ver).DerivePath(path)
|
||||
|
||||
@classmethod
|
||||
def FromExtendedKey(cls,
|
||||
ex_key_str: str,
|
||||
key_net_ver: Optional[Bip32KeyNetVersions] = None) -> Bip32Base:
|
||||
"""
|
||||
Create a Bip32 object from the specified extended key.
|
||||
|
||||
Args:
|
||||
ex_key_str (str) : Extended key string
|
||||
key_net_ver (Bip32KeyNetVersions object, optional): Bip32KeyNetVersions object
|
||||
(default: specific class key net version)
|
||||
|
||||
Returns:
|
||||
Bip32Base object: Bip32Base object
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the key is not valid
|
||||
"""
|
||||
key_net_ver = key_net_ver or cls._DefaultKeyNetVersion()
|
||||
|
||||
# De-serialize key
|
||||
deser_key = Bip32KeyDeserializer.DeserializeKey(ex_key_str, key_net_ver)
|
||||
# Get key parts
|
||||
key_bytes, key_data, is_public = deser_key.KeyBytes(), deser_key.KeyData(), deser_key.IsPublic()
|
||||
|
||||
# If depth is zero, fingerprint shall be the master one and child index shall be zero
|
||||
if key_data.Depth() == 0:
|
||||
if not key_data.ParentFingerPrint().IsMasterKey():
|
||||
raise Bip32KeyError(
|
||||
f"Invalid extended master key (wrong fingerprint: {key_data.ParentFingerPrint().ToHex()})"
|
||||
)
|
||||
if key_data.Index() != 0:
|
||||
raise Bip32KeyError(f"Invalid extended master key (wrong child index: {key_data.Index().ToInt()})")
|
||||
|
||||
return cls(
|
||||
priv_key=key_bytes if not is_public else None,
|
||||
pub_key=key_bytes if is_public else None,
|
||||
key_data=key_data,
|
||||
key_net_ver=key_net_ver
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def FromPrivateKey(cls,
|
||||
priv_key: Union[bytes, IPrivateKey],
|
||||
key_data: Bip32KeyData = Bip32KeyData(),
|
||||
key_net_ver: Optional[Bip32KeyNetVersions] = None) -> Bip32Base:
|
||||
"""
|
||||
Create a Bip32 object from the specified private key and derivation data.
|
||||
If only the private key bytes are specified, the key will be considered a master key with
|
||||
the chain code set to zero, since there is no way to recover the key derivation data.
|
||||
|
||||
Args:
|
||||
priv_key (bytes or IPrivateKey) : Private key
|
||||
key_data (Bip32KeyData object, optional) : Key data (default: all zeros)
|
||||
key_net_ver (Bip32KeyNetVersions object, optional): Bip32KeyNetVersions object
|
||||
(default: specific class key net version)
|
||||
|
||||
Returns:
|
||||
Bip32Base object: Bip32Base object
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the key is not valid
|
||||
"""
|
||||
return cls(
|
||||
priv_key=priv_key,
|
||||
pub_key=None,
|
||||
key_data=key_data,
|
||||
key_net_ver=key_net_ver or cls._DefaultKeyNetVersion()
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def FromPublicKey(cls,
|
||||
pub_key: Union[bytes, IPoint, IPublicKey],
|
||||
key_data: Bip32KeyData = Bip32KeyData(),
|
||||
key_net_ver: Optional[Bip32KeyNetVersions] = None) -> Bip32Base:
|
||||
"""
|
||||
Create a Bip32 object from the specified public key and derivation data.
|
||||
If only the public key bytes are specified, the key will be considered a master key with
|
||||
the chain code set to zero, since there is no way to recover the key derivation data.
|
||||
|
||||
Args:
|
||||
pub_key (bytes, IPoint or IPublicKey) : Public key
|
||||
key_data (Bip32KeyData object, optional) : Key data (default: all zeros)
|
||||
key_net_ver (Bip32KeyNetVersions object, optional): Bip32KeyNetVersions object
|
||||
(default: specific class key net version)
|
||||
|
||||
Returns:
|
||||
Bip32Base object: Bip32Base object
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the key is not valid
|
||||
"""
|
||||
return cls(
|
||||
priv_key=None,
|
||||
pub_key=pub_key,
|
||||
key_data=key_data,
|
||||
key_net_ver=key_net_ver or cls._DefaultKeyNetVersion()
|
||||
)
|
||||
|
||||
#
|
||||
# Public methods
|
||||
#
|
||||
|
||||
def __init__(self,
|
||||
priv_key: Optional[Union[bytes, IPrivateKey]],
|
||||
pub_key: Optional[Union[bytes, IPoint, IPublicKey]],
|
||||
key_data: Bip32KeyData,
|
||||
key_net_ver: Bip32KeyNetVersions) -> None:
|
||||
"""
|
||||
Construct class.
|
||||
|
||||
Args:
|
||||
priv_key (bytes or IPrivateKey) : Private key (None for a public-only object)
|
||||
pub_key (bytes, IPoint or IPublicKey) : Public key (only needed for a public-only object)
|
||||
If priv_key is not None, it'll be discarded
|
||||
key_data (Bip32KeyData object) : Key data
|
||||
key_net_ver (Bip32KeyNetVersions object): Bip32KeyNetVersions object
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the constructed key is not valid
|
||||
"""
|
||||
curve = self.Curve()
|
||||
|
||||
# Private key object
|
||||
if priv_key is not None:
|
||||
# Check that key type matches the Bip curve
|
||||
if not isinstance(priv_key, bytes) and not isinstance(priv_key, curve.PrivateKeyClass()):
|
||||
raise Bip32KeyError(f"Invalid private key class, a {curve.Name()} key is required")
|
||||
|
||||
self.m_priv_key = Bip32PrivateKey.FromBytesOrKeyObject(priv_key,
|
||||
key_data,
|
||||
key_net_ver,
|
||||
self.CurveType())
|
||||
self.m_pub_key = self.m_priv_key.PublicKey()
|
||||
# Public-only object
|
||||
else:
|
||||
# Check that key type matches the Bip curve
|
||||
if (not isinstance(pub_key, bytes)
|
||||
and not isinstance(pub_key, curve.PointClass())
|
||||
and not isinstance(pub_key, curve.PublicKeyClass())):
|
||||
raise Bip32KeyError(f"Invalid public key class, a {curve.Name()} key or point is required")
|
||||
|
||||
self.m_priv_key = None
|
||||
self.m_pub_key = Bip32PublicKey.FromBytesOrKeyObject(pub_key,
|
||||
key_data,
|
||||
key_net_ver,
|
||||
self.CurveType())
|
||||
|
||||
def ChildKey(self,
|
||||
index: Union[int, Bip32KeyIndex]) -> Bip32Base:
|
||||
"""
|
||||
Create and return a child key of the current one with the specified index.
|
||||
The index shall be hardened using HardenIndex method to use the private derivation algorithm.
|
||||
|
||||
Args:
|
||||
index (int or Bip32KeyIndex object): Index
|
||||
|
||||
Returns:
|
||||
Bip32Base object: Bip32Base object
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the index results in an invalid key
|
||||
"""
|
||||
index = self.__GetIndex(index)
|
||||
return self.__ValidateAndCkdPriv(index) if not self.IsPublicOnly() else self.__ValidateAndCkdPub(index)
|
||||
|
||||
def DerivePath(self,
|
||||
path: Union[str, Bip32Path]) -> Bip32Base:
|
||||
"""
|
||||
Derive children keys from the specified path.
|
||||
|
||||
Args:
|
||||
path (str or Bip32Path object): Path
|
||||
|
||||
Returns:
|
||||
Bip32Base object: Bip32Base object
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the index results in an invalid key
|
||||
Bip32PathError: If the path is not valid
|
||||
ValueError: If the path is a master path and the key is a child key
|
||||
"""
|
||||
path = self.__GetPath(path)
|
||||
if self.Depth() > 0 and path.IsAbsolute():
|
||||
raise ValueError("Absolute paths can only be derived from a master key, not child ones")
|
||||
|
||||
bip32_obj = self
|
||||
# Derive children keys
|
||||
for path_elem in path:
|
||||
bip32_obj = bip32_obj.ChildKey(path_elem)
|
||||
|
||||
return bip32_obj
|
||||
|
||||
def ConvertToPublic(self) -> None:
|
||||
"""Convert the object into a public one."""
|
||||
self.m_priv_key = None
|
||||
|
||||
def IsPublicOnly(self) -> bool:
|
||||
"""
|
||||
Get if it's public-only.
|
||||
|
||||
Returns:
|
||||
bool: True if public-only, false otherwise
|
||||
"""
|
||||
return self.m_priv_key is None
|
||||
|
||||
def PrivateKey(self) -> Bip32PrivateKey:
|
||||
"""
|
||||
Return private key object.
|
||||
|
||||
Returns:
|
||||
Bip32PrivateKey object: Bip32PrivateKey object
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If internal key is public-only
|
||||
"""
|
||||
if self.IsPublicOnly():
|
||||
raise Bip32KeyError("Public-only deterministic keys have no private half")
|
||||
|
||||
assert isinstance(self.m_priv_key, Bip32PrivateKey)
|
||||
return self.m_priv_key
|
||||
|
||||
def PublicKey(self) -> Bip32PublicKey:
|
||||
"""
|
||||
Return public key object.
|
||||
|
||||
Returns:
|
||||
Bip32PublicKey object: Bip32PublicKey object
|
||||
"""
|
||||
return self.m_pub_key
|
||||
|
||||
def KeyNetVersions(self) -> Bip32KeyNetVersions:
|
||||
"""
|
||||
Get key net versions.
|
||||
|
||||
Returns:
|
||||
Bip32KeyNetVersions object: Bip32KeyNetVersions object
|
||||
"""
|
||||
return self.m_pub_key.KeyNetVersions()
|
||||
|
||||
def Depth(self) -> Bip32Depth:
|
||||
"""
|
||||
Get current depth.
|
||||
|
||||
Returns:
|
||||
Bip32Depth object: Current depth
|
||||
"""
|
||||
return self.m_pub_key.Data().Depth()
|
||||
|
||||
def Index(self) -> Bip32KeyIndex:
|
||||
"""
|
||||
Get current index.
|
||||
|
||||
Returns:
|
||||
Bip32KeyIndex object: Current index
|
||||
"""
|
||||
return self.m_pub_key.Data().Index()
|
||||
|
||||
def ChainCode(self) -> Bip32ChainCode:
|
||||
"""
|
||||
Get chain code.
|
||||
|
||||
Returns:
|
||||
Bip32ChainCode: Chain code
|
||||
"""
|
||||
return self.m_pub_key.ChainCode()
|
||||
|
||||
def FingerPrint(self) -> Bip32FingerPrint:
|
||||
"""
|
||||
Get public key fingerprint.
|
||||
|
||||
Returns:
|
||||
Bip32FingerPrint object: Public key fingerprint bytes
|
||||
"""
|
||||
return self.m_pub_key.FingerPrint()
|
||||
|
||||
def ParentFingerPrint(self) -> Bip32FingerPrint:
|
||||
"""
|
||||
Get parent fingerprint.
|
||||
|
||||
Returns:
|
||||
Bip32FingerPrint object: Parent fingerprint bytes
|
||||
"""
|
||||
return self.m_pub_key.Data().ParentFingerPrint()
|
||||
|
||||
@classmethod
|
||||
def Curve(cls) -> EllipticCurve:
|
||||
"""
|
||||
Return the elliptic curve.
|
||||
|
||||
Returns:
|
||||
EllipticCurve object: EllipticCurve object
|
||||
"""
|
||||
return EllipticCurveGetter.FromType(cls.CurveType())
|
||||
|
||||
@classmethod
|
||||
def IsPublicDerivationSupported(cls) -> bool:
|
||||
"""
|
||||
Get if public derivation is supported.
|
||||
|
||||
Returns:
|
||||
bool: True if supported, false otherwise.
|
||||
"""
|
||||
return cls._KeyDerivator().IsPublicDerivationSupported()
|
||||
|
||||
#
|
||||
# Private methods
|
||||
#
|
||||
|
||||
def __ValidateAndCkdPriv(self,
|
||||
index: Bip32KeyIndex) -> Bip32Base:
|
||||
"""
|
||||
Check the key index validity and create a child key with the specified index using private derivation.
|
||||
|
||||
Args:
|
||||
index (Bip32KeyIndex object): Key index
|
||||
|
||||
Returns:
|
||||
Bip32Base object: Bip32Base object
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the index results in an invalid key
|
||||
"""
|
||||
return self.__CkdPriv(index)
|
||||
|
||||
def __ValidateAndCkdPub(self,
|
||||
index: Bip32KeyIndex) -> Bip32Base:
|
||||
"""
|
||||
Check the key index validity and create a child key with the specified index using public derivation.
|
||||
|
||||
Args:
|
||||
index (Bip32KeyIndex object): Key index
|
||||
|
||||
Returns:
|
||||
Bip32Base object: Bip32Base object
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the index results in an invalid key
|
||||
"""
|
||||
|
||||
# Hardened index is not supported for public derivation
|
||||
if index.IsHardened():
|
||||
raise Bip32KeyError("Public child derivation cannot be used to create a hardened child key")
|
||||
|
||||
return self.__CkdPub(index)
|
||||
|
||||
def __CkdPriv(self,
|
||||
index: Bip32KeyIndex) -> Bip32Base:
|
||||
"""
|
||||
Derive a child key with the specified index using private derivation.
|
||||
|
||||
Args:
|
||||
index (Bip32KeyIndex object): Key index
|
||||
|
||||
Returns:
|
||||
Bip32Base object: Bip32Base object
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the index results in an invalid key
|
||||
"""
|
||||
assert self.m_priv_key is not None
|
||||
|
||||
|
||||
priv_key_bytes, chain_code_bytes = self._KeyDerivator().CkdPriv(self.m_priv_key,
|
||||
self.m_pub_key,
|
||||
index)
|
||||
return self.__class__(
|
||||
priv_key=priv_key_bytes,
|
||||
pub_key=None,
|
||||
key_data=Bip32KeyData(
|
||||
chain_code=chain_code_bytes,
|
||||
depth=self.Depth().Increase(),
|
||||
index=index,
|
||||
parent_fprint=self.FingerPrint()
|
||||
),
|
||||
key_net_ver=self.KeyNetVersions()
|
||||
)
|
||||
|
||||
def __CkdPub(self,
|
||||
index: Bip32KeyIndex) -> Bip32Base:
|
||||
"""
|
||||
Derive a child key with the specified index using public derivation.
|
||||
|
||||
Args:
|
||||
index (Bip32KeyIndex object): Key index
|
||||
|
||||
Returns:
|
||||
Bip32Base object: Bip32Base object
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the index results in an invalid key
|
||||
"""
|
||||
pub_key_bytes, chain_code_bytes = self._KeyDerivator().CkdPub(self.m_pub_key,
|
||||
index)
|
||||
return self.__class__(
|
||||
priv_key=None,
|
||||
pub_key=pub_key_bytes,
|
||||
key_data=Bip32KeyData(
|
||||
chain_code=chain_code_bytes,
|
||||
depth=self.Depth().Increase(),
|
||||
index=index,
|
||||
parent_fprint=self.FingerPrint()
|
||||
),
|
||||
key_net_ver=self.KeyNetVersions()
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def __GetIndex(index: Union[int, Bip32KeyIndex]) -> Bip32KeyIndex:
|
||||
"""
|
||||
Get index object.
|
||||
|
||||
Args:
|
||||
index (int or Bip32KeyIndex): Index
|
||||
|
||||
Returns:
|
||||
Bip32KeyIndex object: Bip32KeyIndex object
|
||||
"""
|
||||
return Bip32KeyIndex(index) if isinstance(index, int) else index
|
||||
|
||||
@staticmethod
|
||||
def __GetPath(path: Union[str, Bip32Path]) -> Bip32Path:
|
||||
"""
|
||||
Get path object.
|
||||
|
||||
Args:
|
||||
path (str or Bip32Path): Path
|
||||
|
||||
Returns:
|
||||
Bip32Path object: Bip32Path object
|
||||
"""
|
||||
return Bip32PathParser.Parse(path) if isinstance(path, str) else path
|
||||
|
||||
#
|
||||
# Abstract methods
|
||||
#
|
||||
|
||||
@staticmethod
|
||||
@abstractmethod
|
||||
def CurveType() -> EllipticCurveTypes:
|
||||
"""
|
||||
Return the elliptic curve type.
|
||||
|
||||
Returns:
|
||||
EllipticCurveTypes: Curve type
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
@abstractmethod
|
||||
def _DefaultKeyNetVersion() -> Bip32KeyNetVersions:
|
||||
"""
|
||||
Return the default key net version.
|
||||
|
||||
Returns:
|
||||
Bip32KeyNetVersions object: Bip32KeyNetVersions object
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
@abstractmethod
|
||||
def _KeyDerivator() -> Type[IBip32KeyDerivator]:
|
||||
"""
|
||||
Return the key derivator class.
|
||||
|
||||
Returns:
|
||||
IBip32KeyDerivator class: Key derivator class
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
@abstractmethod
|
||||
def _MasterKeyGenerator() -> Type[IBip32MstKeyGenerator]:
|
||||
"""
|
||||
Return the master key generator class.
|
||||
|
||||
Returns:
|
||||
IBip32MstKeyGenerator class: Master key generator class
|
||||
"""
|
||||
@ -0,0 +1,83 @@
|
||||
# Copyright (c) 2021 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""Module for BIP32 SLIP-0010 keys derivation."""
|
||||
|
||||
# Imports
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Tuple, Union
|
||||
|
||||
from ..bip32_key_data import Bip32KeyIndex
|
||||
from ..bip32_keys import Bip32PrivateKey, Bip32PublicKey
|
||||
from ...ecc import IPoint
|
||||
|
||||
|
||||
class IBip32KeyDerivator(ABC):
|
||||
"""Interface for generic BIP32 key derivator."""
|
||||
|
||||
@staticmethod
|
||||
@abstractmethod
|
||||
def IsPublicDerivationSupported() -> bool:
|
||||
"""
|
||||
Get if public derivation is supported.
|
||||
|
||||
Returns:
|
||||
bool: True if supported, false otherwise.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def CkdPriv(cls,
|
||||
priv_key: Bip32PrivateKey,
|
||||
pub_key: Bip32PublicKey,
|
||||
index: Bip32KeyIndex) -> Tuple[bytes, bytes]:
|
||||
"""
|
||||
Derive a child key with the specified index using private derivation.
|
||||
|
||||
Args:
|
||||
priv_key (Bip32PrivateKey object): Bip32PrivateKey object
|
||||
pub_key (Bip32PublicKey object) : Bip32PublicKey object
|
||||
index (Bip32KeyIndex object) : Key index
|
||||
|
||||
Returns:
|
||||
tuple[bytes, bytes]: Private key bytes (index 0) and chain code bytes (index 1)
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the index results in an invalid key
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def CkdPub(cls,
|
||||
pub_key: Bip32PublicKey,
|
||||
index: Bip32KeyIndex) -> Tuple[Union[bytes, IPoint], bytes]:
|
||||
"""
|
||||
Derive a child key with the specified index using public derivation.
|
||||
|
||||
Args:
|
||||
pub_key (Bip32PublicKey object): Bip32PublicKey object
|
||||
index (Bip32KeyIndex object) : Key index
|
||||
|
||||
Returns:
|
||||
tuple[bytes or IPoint, bytes]: Public key bytes or point (index 0) and chain code bytes (index 1)
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the index results in an invalid key
|
||||
"""
|
||||
@ -0,0 +1,47 @@
|
||||
# Copyright (c) 2021 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""Module for BIP32 SLIP-0010 keys derivation."""
|
||||
|
||||
# Imports
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Tuple
|
||||
|
||||
|
||||
class IBip32MstKeyGenerator(ABC):
|
||||
"""Interface for generic BIP32 master key generator."""
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def GenerateFromSeed(cls,
|
||||
seed_bytes: bytes) -> Tuple[bytes, bytes]:
|
||||
"""
|
||||
Generate a master key from the specified seed.
|
||||
|
||||
Args:
|
||||
seed_bytes (bytes): Seed bytes
|
||||
|
||||
Returns:
|
||||
tuple[bytes, bytes]: Private key bytes (index 0) and chain code bytes (index 1)
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the seed is not suitable for master key generation
|
||||
ValueError: If seed length is not valid
|
||||
"""
|
||||
@ -0,0 +1,35 @@
|
||||
# Copyright (c) 2021 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""Module with BIP32 constants."""
|
||||
|
||||
# Imports
|
||||
from .bip32_key_net_ver import Bip32KeyNetVersions
|
||||
|
||||
|
||||
class Bip32Const:
|
||||
"""Class container for BIP32 constants."""
|
||||
|
||||
# Main net key net version (xpub / xprv)
|
||||
MAIN_NET_KEY_NET_VERSIONS: Bip32KeyNetVersions = Bip32KeyNetVersions(b"\x04\x88\xb2\x1e", b"\x04\x88\xad\xe4")
|
||||
# Test net key net version (tpub / tprv)
|
||||
TEST_NET_KEY_NET_VERSIONS: Bip32KeyNetVersions = Bip32KeyNetVersions(b"\x04\x35\x87\xcf", b"\x04\x35\x83\x94")
|
||||
# Key net version for BIP32 Kholaw that uses 64-byte private keys (xpub / xprv)
|
||||
# KHOLAW_KEY_NET_VERSIONS: Bip32KeyNetVersions = Bip32KeyNetVersions(b"\x04\x88\xb2\x1e", b"\x0f\x43\x31\xd4")
|
||||
@ -0,0 +1,29 @@
|
||||
# Copyright (c) 2021 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""Module with BIP32 exceptions."""
|
||||
|
||||
|
||||
class Bip32KeyError(Exception):
|
||||
"""Exception in case of key error."""
|
||||
|
||||
|
||||
class Bip32PathError(Exception):
|
||||
"""Exception in case of path error."""
|
||||
@ -0,0 +1,500 @@
|
||||
# Copyright (c) 2021 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""Module with helper classes for BIP32 key data."""
|
||||
|
||||
# Imports
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Union
|
||||
|
||||
from ..utils.misc import BitUtils, BytesUtils, DataBytes, IntegerUtils
|
||||
from ..utils.typing import Literal
|
||||
|
||||
|
||||
class Bip32KeyDataConst:
|
||||
"""Class container for BIP32 key data constants."""
|
||||
|
||||
# Chaincode length in bytes
|
||||
CHAINCODE_BYTE_LEN: int = 32
|
||||
# Depth length in bytes
|
||||
DEPTH_BYTE_LEN: int = 1
|
||||
# Fingerprint length in bytes
|
||||
FINGERPRINT_BYTE_LEN: int = 4
|
||||
# Fingerprint of master key
|
||||
FINGERPRINT_MASTER_KEY: bytes = b"\x00\x00\x00\x00"
|
||||
# Key index length in bytes
|
||||
KEY_INDEX_BYTE_LEN: int = 4
|
||||
# Key index maximum value
|
||||
KEY_INDEX_MAX_VAL: int = 2**32 - 1
|
||||
# Key index hardened bit number
|
||||
KEY_INDEX_HARDENED_BIT_NUM: int = 31
|
||||
|
||||
|
||||
class Bip32ChainCode(DataBytes):
|
||||
"""
|
||||
BIP32 chaincode class.
|
||||
It represents a BIP32 chaincode.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
chaincode: bytes = b"\x00" * Bip32KeyDataConst.CHAINCODE_BYTE_LEN) -> None:
|
||||
"""
|
||||
Construct class.
|
||||
|
||||
Args:
|
||||
chaincode (bytes, optional): Fingerprint bytes (default: zero)
|
||||
|
||||
Raises:
|
||||
ValueError: If the chain code length is not valid
|
||||
"""
|
||||
if len(chaincode) != self.FixedLength():
|
||||
raise ValueError(f"Invalid chaincode length ({len(chaincode)})")
|
||||
super().__init__(chaincode)
|
||||
|
||||
@staticmethod
|
||||
def FixedLength() -> int:
|
||||
"""
|
||||
Get the fixed length in bytes.
|
||||
|
||||
Returns:
|
||||
int: Length in bytes
|
||||
"""
|
||||
return Bip32KeyDataConst.CHAINCODE_BYTE_LEN
|
||||
|
||||
|
||||
class Bip32FingerPrint(DataBytes):
|
||||
"""
|
||||
BIP32 fingerprint class.
|
||||
It represents a BIP32 fingerprint.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
fprint: bytes = Bip32KeyDataConst.FINGERPRINT_MASTER_KEY) -> None:
|
||||
"""
|
||||
Construct class.
|
||||
|
||||
Args:
|
||||
fprint (bytes, optional): Fingerprint bytes (default: master key)
|
||||
|
||||
Raises:
|
||||
ValueError: If the chain code length is not valid
|
||||
"""
|
||||
if len(fprint) < self.FixedLength():
|
||||
raise ValueError(f"Invalid fingerprint length ({len(fprint)})")
|
||||
super().__init__(fprint[:Bip32KeyDataConst.FINGERPRINT_BYTE_LEN])
|
||||
|
||||
@staticmethod
|
||||
def FixedLength() -> int:
|
||||
"""
|
||||
Get the fixed length in bytes.
|
||||
|
||||
Returns:
|
||||
int: Length in bytes
|
||||
"""
|
||||
return Bip32KeyDataConst.FINGERPRINT_BYTE_LEN
|
||||
|
||||
def IsMasterKey(self) -> bool:
|
||||
"""
|
||||
Get if the fingerprint corresponds to a master key.
|
||||
|
||||
Returns:
|
||||
bool: True if it corresponds to a master key, false otherwise
|
||||
"""
|
||||
return self.ToBytes() == Bip32KeyDataConst.FINGERPRINT_MASTER_KEY
|
||||
|
||||
|
||||
class Bip32Depth:
|
||||
"""
|
||||
BIP32 depth class.
|
||||
It represents a BIP32 depth.
|
||||
"""
|
||||
|
||||
m_depth: int
|
||||
|
||||
def __init__(self,
|
||||
depth: int) -> None:
|
||||
"""
|
||||
Construct class.
|
||||
|
||||
Args:
|
||||
depth (int): Depth
|
||||
|
||||
Raises:
|
||||
ValueError: If the depth value is not valid
|
||||
"""
|
||||
if depth < 0:
|
||||
raise ValueError(f"Invalid depth ({depth})")
|
||||
self.m_depth = depth
|
||||
|
||||
@staticmethod
|
||||
def FixedLength() -> int:
|
||||
"""
|
||||
Get the fixed length in bytes.
|
||||
|
||||
Returns:
|
||||
int: Length in bytes
|
||||
"""
|
||||
return Bip32KeyDataConst.DEPTH_BYTE_LEN
|
||||
|
||||
def Increase(self) -> Bip32Depth:
|
||||
"""
|
||||
Get a new object with increased depth.
|
||||
|
||||
Returns:
|
||||
Bip32Depth object: Bip32Depth object
|
||||
"""
|
||||
return Bip32Depth(self.m_depth + 1)
|
||||
|
||||
def ToBytes(self) -> bytes:
|
||||
"""
|
||||
Get the depth as bytes.
|
||||
|
||||
Returns:
|
||||
bytes: Depth bytes
|
||||
"""
|
||||
return IntegerUtils.ToBytes(self.m_depth, bytes_num=self.FixedLength())
|
||||
|
||||
def ToInt(self) -> int:
|
||||
"""
|
||||
Get the depth as integer.
|
||||
|
||||
Returns:
|
||||
int: Depth index
|
||||
"""
|
||||
return int(self.m_depth)
|
||||
|
||||
def __int__(self) -> int:
|
||||
"""
|
||||
Get the depth as integer.
|
||||
|
||||
Returns:
|
||||
int: Depth index
|
||||
"""
|
||||
return self.ToInt()
|
||||
|
||||
def __bytes__(self) -> bytes:
|
||||
"""
|
||||
Get the depth as bytes.
|
||||
|
||||
Returns:
|
||||
bytes: Depth bytes
|
||||
"""
|
||||
return self.ToBytes()
|
||||
|
||||
def __eq__(self,
|
||||
other: object) -> bool:
|
||||
"""
|
||||
Equality operator.
|
||||
|
||||
Args:
|
||||
other (int or Bip32Depth object): Other object to compare
|
||||
|
||||
Returns:
|
||||
bool: True if equal false otherwise
|
||||
|
||||
Raises:
|
||||
TypeError: If the other object is not of the correct type
|
||||
"""
|
||||
if not isinstance(other, (int, Bip32Depth)):
|
||||
raise TypeError(f"Invalid type for checking equality ({type(other)})")
|
||||
|
||||
if isinstance(other, int):
|
||||
return self.m_depth == other
|
||||
return self.m_depth == other.m_depth
|
||||
|
||||
def __gt__(self,
|
||||
other: Union[int, Bip32Depth]) -> bool:
|
||||
"""
|
||||
Greater than operator.
|
||||
|
||||
Args:
|
||||
other (int or Bip32Depth object): Other value to compare
|
||||
|
||||
Returns:
|
||||
bool: True if greater false otherwise
|
||||
"""
|
||||
if isinstance(other, int):
|
||||
return self.m_depth > other
|
||||
return self.m_depth > other.m_depth
|
||||
|
||||
def __lt__(self,
|
||||
other: Union[int, Bip32Depth]) -> bool:
|
||||
"""
|
||||
Lower than operator.
|
||||
|
||||
Args:
|
||||
other (int or Bip32Depth object): Other value to compare
|
||||
|
||||
Returns:
|
||||
bool: True if lower false otherwise
|
||||
"""
|
||||
if isinstance(other, int):
|
||||
return self.m_depth < other
|
||||
return self.m_depth < other.m_depth
|
||||
|
||||
|
||||
class Bip32KeyIndex:
|
||||
"""
|
||||
BIP32 key index class.
|
||||
It represents a BIP32 key index.
|
||||
"""
|
||||
|
||||
m_idx: int
|
||||
|
||||
@staticmethod
|
||||
def HardenIndex(index: int) -> int:
|
||||
"""
|
||||
Harden the specified index and return it.
|
||||
|
||||
Args:
|
||||
index (int): Index
|
||||
|
||||
Returns:
|
||||
int: Hardened index
|
||||
"""
|
||||
return BitUtils.SetBit(index, Bip32KeyDataConst.KEY_INDEX_HARDENED_BIT_NUM)
|
||||
|
||||
@staticmethod
|
||||
def UnhardenIndex(index: int) -> int:
|
||||
"""
|
||||
Unharden the specified index and return it.
|
||||
|
||||
Args:
|
||||
index (int): Index
|
||||
|
||||
Returns:
|
||||
int: Unhardened index
|
||||
"""
|
||||
return BitUtils.ResetBit(index, Bip32KeyDataConst.KEY_INDEX_HARDENED_BIT_NUM)
|
||||
|
||||
@staticmethod
|
||||
def IsHardenedIndex(index: int) -> bool:
|
||||
"""
|
||||
Get if the specified index is hardened.
|
||||
|
||||
Args:
|
||||
index (int): Index
|
||||
|
||||
Returns:
|
||||
bool: True if hardened, false otherwise
|
||||
"""
|
||||
return BitUtils.IsBitSet(index, Bip32KeyDataConst.KEY_INDEX_HARDENED_BIT_NUM)
|
||||
|
||||
@classmethod
|
||||
def FromBytes(cls,
|
||||
index_bytes: bytes) -> Bip32KeyIndex:
|
||||
"""
|
||||
Construct class from bytes.
|
||||
|
||||
Args:
|
||||
index_bytes (bytes): Key index bytes
|
||||
|
||||
Returns:
|
||||
Bip32KeyIndex object: Bip32KeyIndex object
|
||||
|
||||
Raises:
|
||||
ValueError: If the index is not valid
|
||||
"""
|
||||
return cls(BytesUtils.ToInteger(index_bytes))
|
||||
|
||||
def __init__(self,
|
||||
idx: int) -> None:
|
||||
"""
|
||||
Construct class.
|
||||
|
||||
Args:
|
||||
idx (int): Key index
|
||||
|
||||
Raises:
|
||||
ValueError: If the index value is not valid
|
||||
"""
|
||||
if idx < 0 or idx > Bip32KeyDataConst.KEY_INDEX_MAX_VAL:
|
||||
raise ValueError(f"Invalid key index ({idx})")
|
||||
self.m_idx = idx
|
||||
|
||||
@staticmethod
|
||||
def FixedLength() -> int:
|
||||
"""
|
||||
Get the fixed length in bytes.
|
||||
|
||||
Returns:
|
||||
int: Length in bytes
|
||||
"""
|
||||
return Bip32KeyDataConst.KEY_INDEX_BYTE_LEN
|
||||
|
||||
def Harden(self) -> Bip32KeyIndex:
|
||||
"""
|
||||
Get a new Bip32KeyIndex object with the current key index hardened.
|
||||
|
||||
Returns:
|
||||
Bip32KeyIndex object: Bip32KeyIndex object
|
||||
"""
|
||||
return Bip32KeyIndex(self.HardenIndex(self.m_idx))
|
||||
|
||||
def Unharden(self) -> Bip32KeyIndex:
|
||||
"""
|
||||
Get a new Bip32KeyIndex object with the current key index unhardened.
|
||||
|
||||
Returns:
|
||||
Bip32KeyIndex object: Bip32KeyIndex object
|
||||
"""
|
||||
return Bip32KeyIndex(self.UnhardenIndex(self.m_idx))
|
||||
|
||||
def IsHardened(self) -> bool:
|
||||
"""
|
||||
Get if the key index is hardened.
|
||||
|
||||
Returns:
|
||||
bool: True if hardened, false otherwise
|
||||
"""
|
||||
return self.IsHardenedIndex(self.m_idx)
|
||||
|
||||
def ToBytes(self,
|
||||
endianness: Literal["little", "big"] = "big") -> bytes:
|
||||
"""
|
||||
Get the key index as bytes.
|
||||
|
||||
Args:
|
||||
endianness ("big" or "little", optional): Endianness (default: big)
|
||||
|
||||
Returns:
|
||||
bytes: Key bytes
|
||||
"""
|
||||
return IntegerUtils.ToBytes(self.m_idx,
|
||||
bytes_num=self.FixedLength(),
|
||||
endianness=endianness)
|
||||
|
||||
def ToInt(self) -> int:
|
||||
"""
|
||||
Get the key index as integer.
|
||||
|
||||
Returns:
|
||||
int: Key index
|
||||
"""
|
||||
return int(self.m_idx)
|
||||
|
||||
def __int__(self) -> int:
|
||||
"""
|
||||
Get the key index as integer.
|
||||
|
||||
Returns:
|
||||
int: Key index
|
||||
"""
|
||||
return self.ToInt()
|
||||
|
||||
def __bytes__(self) -> bytes:
|
||||
"""
|
||||
Get the key index as bytes.
|
||||
|
||||
Returns:
|
||||
bytes: Key bytes
|
||||
"""
|
||||
return self.ToBytes()
|
||||
|
||||
def __eq__(self,
|
||||
other: object) -> bool:
|
||||
"""
|
||||
Equality operator.
|
||||
|
||||
Args:
|
||||
other (int or Bip32KeyIndex object): Other value to compare
|
||||
|
||||
Returns:
|
||||
bool: True if equal false otherwise
|
||||
|
||||
Raises:
|
||||
TypeError: If the object is not of the correct type
|
||||
"""
|
||||
if not isinstance(other, (int, Bip32KeyIndex)):
|
||||
raise TypeError(f"Invalid type for checking equality ({type(other)})")
|
||||
|
||||
if isinstance(other, int):
|
||||
return self.m_idx == other
|
||||
return self.m_idx == other.m_idx
|
||||
|
||||
|
||||
class Bip32KeyData:
|
||||
"""
|
||||
BIP32 key data class.
|
||||
It contains all additional data related to a BIP32 key (e.g. depth, chain code, etc...).
|
||||
"""
|
||||
|
||||
m_depth: Bip32Depth
|
||||
m_index: Bip32KeyIndex
|
||||
m_chain_code: Bip32ChainCode
|
||||
m_parent_fprint: Bip32FingerPrint
|
||||
|
||||
def __init__(self,
|
||||
depth: Union[int, Bip32Depth] = Bip32Depth(0),
|
||||
index: Union[int, Bip32KeyIndex] = Bip32KeyIndex(0),
|
||||
chain_code: Union[bytes, Bip32ChainCode] = Bip32ChainCode(),
|
||||
parent_fprint: Union[bytes, Bip32FingerPrint] = Bip32FingerPrint()) -> None:
|
||||
"""
|
||||
Construct class.
|
||||
|
||||
Args:
|
||||
depth (Bip32Depth object) : Key depth
|
||||
index (Bip32KeyIndex object) : Key index
|
||||
chain_code (Bip32ChainCode object) : Key chain code
|
||||
parent_fprint (Bip32FingerPrint object) : Key parent fingerprint
|
||||
"""
|
||||
self.m_depth = depth if isinstance(depth, Bip32Depth) else Bip32Depth(depth)
|
||||
self.m_index = index if isinstance(index, Bip32KeyIndex) else Bip32KeyIndex(index)
|
||||
self.m_chain_code = chain_code if isinstance(chain_code, Bip32ChainCode) else Bip32ChainCode(chain_code)
|
||||
self.m_parent_fprint = (parent_fprint
|
||||
if isinstance(parent_fprint, Bip32FingerPrint)
|
||||
else Bip32FingerPrint(parent_fprint))
|
||||
|
||||
def Depth(self) -> Bip32Depth:
|
||||
"""
|
||||
Get current depth.
|
||||
|
||||
Returns:
|
||||
Bip32Depth object: Current depth
|
||||
"""
|
||||
return self.m_depth
|
||||
|
||||
def Index(self) -> Bip32KeyIndex:
|
||||
"""
|
||||
Get current index.
|
||||
|
||||
Returns:
|
||||
Bip32KeyIndex object: Current index
|
||||
"""
|
||||
return self.m_index
|
||||
|
||||
def ChainCode(self) -> Bip32ChainCode:
|
||||
"""
|
||||
Get current chain code.
|
||||
|
||||
Returns:
|
||||
Bip32ChainCode object: Chain code
|
||||
"""
|
||||
return self.m_chain_code
|
||||
|
||||
def ParentFingerPrint(self) -> Bip32FingerPrint:
|
||||
"""
|
||||
Get parent fingerprint.
|
||||
|
||||
Returns:
|
||||
Bip32FingerPrint object: Parent fingerprint
|
||||
"""
|
||||
return self.m_parent_fprint
|
||||
@ -0,0 +1,83 @@
|
||||
# Copyright (c) 2021 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""Module for BIP32 net version class."""
|
||||
|
||||
|
||||
class Bip32KeyNetVersionsConst:
|
||||
"""Class container for BIP32 key net versions constants."""
|
||||
|
||||
# Key net version length in bytes
|
||||
KEY_NET_VERSION_BYTE_LEN: int = 4
|
||||
|
||||
|
||||
class Bip32KeyNetVersions:
|
||||
"""
|
||||
BIP32 key net versions class.
|
||||
It represents a BIP32 key net versions.
|
||||
"""
|
||||
|
||||
m_pub_net_ver: bytes
|
||||
m_priv_net_ver: bytes
|
||||
|
||||
def __init__(self,
|
||||
pub_net_ver: bytes,
|
||||
priv_net_ver: bytes) -> None:
|
||||
"""
|
||||
Construct class.
|
||||
|
||||
Args:
|
||||
pub_net_ver (bytes) : Public net version
|
||||
priv_net_ver (bytes): Private net version
|
||||
"""
|
||||
if (len(pub_net_ver) != self.Length()
|
||||
or len(priv_net_ver) != self.Length()):
|
||||
raise ValueError("Invalid key net version length")
|
||||
|
||||
self.m_pub_net_ver = pub_net_ver
|
||||
self.m_priv_net_ver = priv_net_ver
|
||||
|
||||
@staticmethod
|
||||
def Length() -> int:
|
||||
"""
|
||||
Get the key net version length.
|
||||
|
||||
Returns:
|
||||
int: Key net version length
|
||||
"""
|
||||
return Bip32KeyNetVersionsConst.KEY_NET_VERSION_BYTE_LEN
|
||||
|
||||
def Public(self) -> bytes:
|
||||
"""
|
||||
Get public net version.
|
||||
|
||||
Returns:
|
||||
bytes: Public net version
|
||||
"""
|
||||
return self.m_pub_net_ver
|
||||
|
||||
def Private(self) -> bytes:
|
||||
"""
|
||||
Get private net version.
|
||||
|
||||
Returns:
|
||||
bytes: Private net version
|
||||
"""
|
||||
return self.m_priv_net_ver
|
||||
@ -0,0 +1,294 @@
|
||||
# Copyright (c) 2021 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""Module for BIP32 extended key serialization/deserialization."""
|
||||
|
||||
# Imports
|
||||
from typing import Tuple
|
||||
|
||||
from ..base58 import Base58Decoder, Base58Encoder
|
||||
from .bip32_const import Bip32Const
|
||||
from .bip32_ex import Bip32KeyError
|
||||
from .bip32_key_data import Bip32ChainCode, Bip32Depth, Bip32FingerPrint, Bip32KeyData, Bip32KeyIndex
|
||||
from .bip32_key_net_ver import Bip32KeyNetVersions
|
||||
from ..ecc import IPrivateKey, IPublicKey
|
||||
from ..utils.misc import BytesUtils
|
||||
|
||||
|
||||
class Bip32KeySerConst:
|
||||
"""Class container for BIP32 key serialize constants."""
|
||||
|
||||
# Serialized public key length in bytes
|
||||
SERIALIZED_PUB_KEY_BYTE_LEN: int = 78
|
||||
# Serialized private key length in bytes
|
||||
SERIALIZED_PRIV_KEY_BYTE_LEN: Tuple[int, int] = (78, 110)
|
||||
|
||||
|
||||
class _Bip32KeySerializer:
|
||||
"""
|
||||
BIP32 key serializer class.
|
||||
It serializes private/public keys.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def Serialize(key_bytes: bytes,
|
||||
key_data: Bip32KeyData,
|
||||
key_net_ver_bytes: bytes) -> str:
|
||||
"""
|
||||
Serialize the specified key bytes.
|
||||
|
||||
Args:
|
||||
key_bytes (bytes) : Key bytes
|
||||
key_data (BipKeyData object): Key data
|
||||
key_net_ver_bytes (bytes) : Key net version bytes
|
||||
|
||||
Returns:
|
||||
str: Serialized key
|
||||
"""
|
||||
|
||||
# Serialize key
|
||||
ser_key = (
|
||||
key_net_ver_bytes
|
||||
+ bytes(key_data.Depth()) + bytes(key_data.ParentFingerPrint()) + bytes(key_data.Index())
|
||||
+ bytes(key_data.ChainCode()) + key_bytes
|
||||
)
|
||||
# Encode it
|
||||
return Base58Encoder.CheckEncode(ser_key)
|
||||
|
||||
|
||||
class Bip32PrivateKeySerializer:
|
||||
"""
|
||||
BIP32 private key serializer class.
|
||||
It serializes private keys.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def Serialize(priv_key: IPrivateKey,
|
||||
key_data: Bip32KeyData,
|
||||
key_net_ver: Bip32KeyNetVersions = Bip32Const.MAIN_NET_KEY_NET_VERSIONS) -> str:
|
||||
"""
|
||||
Serialize a private key.
|
||||
|
||||
Args:
|
||||
priv_key (IPrivateKey object) : IPrivateKey object
|
||||
key_data (BipKeyData object) : Key data
|
||||
key_net_ver (Bip32KeyNetVersions object, optional): Key net versions (BIP32 main net version by default)
|
||||
|
||||
Returns:
|
||||
str: Serialized private key
|
||||
"""
|
||||
return _Bip32KeySerializer.Serialize(b"\x00" + priv_key.Raw().ToBytes(),
|
||||
key_data,
|
||||
key_net_ver.Private())
|
||||
|
||||
|
||||
class Bip32PublicKeySerializer:
|
||||
"""
|
||||
BIP32 public key serializer class.
|
||||
It serializes public keys.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def Serialize(pub_key: IPublicKey,
|
||||
key_data: Bip32KeyData,
|
||||
key_net_ver: Bip32KeyNetVersions = Bip32Const.MAIN_NET_KEY_NET_VERSIONS) -> str:
|
||||
"""
|
||||
Serialize a public key.
|
||||
|
||||
Args:
|
||||
pub_key (IPublicKey object) : IPublicKey object
|
||||
key_data (BipKeyData object) : Key data
|
||||
key_net_ver (Bip32KeyNetVersions object, optional): Key net versions (BIP32 main net version by default)
|
||||
|
||||
Returns:
|
||||
str: Serialized public key
|
||||
"""
|
||||
return _Bip32KeySerializer.Serialize(pub_key.RawCompressed().ToBytes(),
|
||||
key_data,
|
||||
key_net_ver.Public())
|
||||
|
||||
|
||||
class Bip32DeserializedKey:
|
||||
"""
|
||||
BIP32 deserialized key class.
|
||||
It represents a key deserialized with the Bip32KeyDeserializer.
|
||||
"""
|
||||
|
||||
m_key_bytes: bytes
|
||||
m_key_data: Bip32KeyData
|
||||
m_is_public: bool
|
||||
|
||||
def __init__(self,
|
||||
key_bytes: bytes,
|
||||
key_data: Bip32KeyData,
|
||||
is_public: bool) -> None:
|
||||
"""
|
||||
Construct class.
|
||||
|
||||
Args:
|
||||
key_bytes (bytes) : Key bytes
|
||||
key_data (BipKeyData object): Key data
|
||||
is_public (bool) : True if the key is public, false otherwise
|
||||
|
||||
Returns:
|
||||
str: Serialized public key
|
||||
"""
|
||||
self.m_key_bytes = key_bytes
|
||||
self.m_key_data = key_data
|
||||
self.m_is_public = is_public
|
||||
|
||||
def KeyBytes(self) -> bytes:
|
||||
"""
|
||||
Get key bytes.
|
||||
|
||||
Returns:
|
||||
bytes: Key bytes
|
||||
"""
|
||||
return self.m_key_bytes
|
||||
|
||||
def KeyData(self) -> Bip32KeyData:
|
||||
"""
|
||||
Get key data.
|
||||
|
||||
Returns:
|
||||
Bip32KeyData object: Bip32KeyData object
|
||||
"""
|
||||
return self.m_key_data
|
||||
|
||||
def IsPublic(self) -> bool:
|
||||
"""
|
||||
Get if public.
|
||||
|
||||
Returns:
|
||||
bool: True if the key is public, false otherwise
|
||||
"""
|
||||
return self.m_is_public
|
||||
|
||||
|
||||
class Bip32KeyDeserializer:
|
||||
"""
|
||||
BIP32 key deserializer class.
|
||||
It deserializes an extended key.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def DeserializeKey(cls,
|
||||
ser_key_str: str,
|
||||
key_net_ver: Bip32KeyNetVersions = Bip32Const.MAIN_NET_KEY_NET_VERSIONS) -> Bip32DeserializedKey:
|
||||
"""
|
||||
Deserialize a key.
|
||||
|
||||
Args:
|
||||
ser_key_str (str) : Serialized key string
|
||||
key_net_ver (Bip32KeyNetVersions object, optional): Key net versions (BIP32 main net version by default)
|
||||
|
||||
Returns:
|
||||
Bip32DeserializedKey object: Bip32DeserializedKey object
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the key is not valid
|
||||
"""
|
||||
|
||||
# Decode key
|
||||
ser_key_bytes = Base58Decoder.CheckDecode(ser_key_str)
|
||||
|
||||
# Get if key is public/private depending on the net version
|
||||
is_public = cls.__GetIfPublic(ser_key_bytes, key_net_ver)
|
||||
|
||||
# Validate length
|
||||
if is_public and len(ser_key_bytes) != Bip32KeySerConst.SERIALIZED_PUB_KEY_BYTE_LEN:
|
||||
raise Bip32KeyError(f"Invalid extended public key (wrong length: {len(ser_key_bytes)})")
|
||||
if not is_public and len(ser_key_bytes) not in Bip32KeySerConst.SERIALIZED_PRIV_KEY_BYTE_LEN:
|
||||
raise Bip32KeyError(f"Invalid extended private key (wrong length: {len(ser_key_bytes)})")
|
||||
|
||||
# Get parts back
|
||||
key_bytes, key_data = cls.__GetPartsFromBytes(ser_key_bytes, is_public)
|
||||
|
||||
return Bip32DeserializedKey(key_bytes, key_data, is_public)
|
||||
|
||||
@staticmethod
|
||||
def __GetIfPublic(ser_key_bytes: bytes,
|
||||
key_net_ver: Bip32KeyNetVersions) -> bool:
|
||||
"""
|
||||
Get if the key is public.
|
||||
|
||||
Args:
|
||||
ser_key_bytes (bytes) : Serialized key bytes
|
||||
key_net_ver (Bip32KeyNetVersions object): Key net versions
|
||||
|
||||
Returns:
|
||||
bool: True if public, false otherwise
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the key net version is not valid
|
||||
"""
|
||||
key_net_ver_got = ser_key_bytes[:Bip32KeyNetVersions.Length()]
|
||||
if key_net_ver_got == key_net_ver.Public():
|
||||
is_public = True
|
||||
elif key_net_ver_got == key_net_ver.Private():
|
||||
is_public = False
|
||||
else:
|
||||
raise Bip32KeyError(
|
||||
f"Invalid extended key (wrong net version: {BytesUtils.ToHexString(key_net_ver_got)})"
|
||||
)
|
||||
return is_public
|
||||
|
||||
@staticmethod
|
||||
def __GetPartsFromBytes(ser_key_bytes: bytes,
|
||||
is_public: bool) -> Tuple[bytes, Bip32KeyData]:
|
||||
"""
|
||||
Get back key parts from serialized key bytes.
|
||||
|
||||
Args:
|
||||
ser_key_bytes (bytes): Serialized key bytes
|
||||
is_public (bool) : True if the key is public, false otherwise
|
||||
|
||||
Returns:
|
||||
tuple[bytes, Bip32KeyData]: key bytes (index 0) and key data (index 1)
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the private key first byte is not zero
|
||||
"""
|
||||
|
||||
# Compute indexes
|
||||
depth_idx = Bip32KeyNetVersions.Length()
|
||||
fprint_idx = depth_idx + Bip32Depth.FixedLength()
|
||||
key_index_idx = fprint_idx + Bip32FingerPrint.FixedLength()
|
||||
chain_code_idx = key_index_idx + Bip32KeyIndex.FixedLength()
|
||||
key_idx = chain_code_idx + Bip32ChainCode.FixedLength()
|
||||
|
||||
# Get parts
|
||||
depth = ser_key_bytes[depth_idx]
|
||||
fprint_bytes = ser_key_bytes[fprint_idx:key_index_idx]
|
||||
key_index_bytes = ser_key_bytes[key_index_idx:chain_code_idx]
|
||||
chain_code_bytes = ser_key_bytes[chain_code_idx:key_idx]
|
||||
key_bytes = ser_key_bytes[key_idx:]
|
||||
key_data = Bip32KeyData(Bip32Depth(depth),
|
||||
Bip32KeyIndex.FromBytes(key_index_bytes),
|
||||
Bip32ChainCode(chain_code_bytes),
|
||||
Bip32FingerPrint(fprint_bytes))
|
||||
|
||||
# If private key, the first byte shall be zero and shall be removed
|
||||
if not is_public:
|
||||
if key_bytes[0] != 0:
|
||||
raise Bip32KeyError(f"Invalid extended private key (wrong secret: {key_bytes[0]})")
|
||||
key_bytes = key_bytes[1:]
|
||||
|
||||
return key_bytes, key_data
|
||||
@ -0,0 +1,457 @@
|
||||
# Copyright (c) 2021 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""Module for BIP32 keys handling."""
|
||||
|
||||
# Imports
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from functools import lru_cache
|
||||
from typing import Union
|
||||
|
||||
from .bip32_ex import Bip32KeyError
|
||||
from .bip32_key_data import Bip32ChainCode, Bip32FingerPrint, Bip32KeyData
|
||||
from .bip32_key_net_ver import Bip32KeyNetVersions
|
||||
from .bip32_key_ser import Bip32PrivateKeySerializer, Bip32PublicKeySerializer
|
||||
from ..ecc import EllipticCurve, EllipticCurveGetter, EllipticCurveTypes, IPoint, IPrivateKey, IPublicKey
|
||||
from ..utils.crypto import Hash160
|
||||
from ..utils.misc import DataBytes
|
||||
|
||||
|
||||
class _Bip32KeyBase(ABC):
|
||||
"""Base class for a generic BIP32 key."""
|
||||
|
||||
m_curve: EllipticCurve
|
||||
m_curve_type: EllipticCurveTypes
|
||||
m_key_data: Bip32KeyData
|
||||
m_key_net_ver: Bip32KeyNetVersions
|
||||
|
||||
def __init__(self,
|
||||
key_data: Bip32KeyData,
|
||||
key_net_ver: Bip32KeyNetVersions,
|
||||
curve_type: EllipticCurveTypes) -> None:
|
||||
"""
|
||||
Construct class.
|
||||
|
||||
Args:
|
||||
key_data (Bip32KeyData object) : Key data
|
||||
key_net_ver (Bip32KeyNetVersions object): Key net versions
|
||||
curve_type (EllipticCurveTypes) : Elliptic curve type
|
||||
"""
|
||||
self.m_curve = EllipticCurveGetter.FromType(curve_type)
|
||||
self.m_curve_type = curve_type
|
||||
self.m_key_data = key_data
|
||||
self.m_key_net_ver = key_net_ver
|
||||
|
||||
def Curve(self) -> EllipticCurve:
|
||||
"""
|
||||
Return key elliptic curve.
|
||||
|
||||
Returns:
|
||||
EllipticCurve object: EllipticCurve object
|
||||
"""
|
||||
return self.m_curve
|
||||
|
||||
def CurveType(self) -> EllipticCurveTypes:
|
||||
"""
|
||||
Return key elliptic curve type.
|
||||
|
||||
Returns:
|
||||
EllipticCurveTypes: Elliptic curve type
|
||||
"""
|
||||
return self.m_curve_type
|
||||
|
||||
def Data(self) -> Bip32KeyData:
|
||||
"""
|
||||
Return key data.
|
||||
|
||||
Returns:
|
||||
BipKeyData object: BipKeyData object
|
||||
"""
|
||||
return self.m_key_data
|
||||
|
||||
def ChainCode(self) -> Bip32ChainCode:
|
||||
"""
|
||||
Return the chain code.
|
||||
|
||||
Returns:
|
||||
Bip32ChainCode object: Bip32ChainCode object
|
||||
"""
|
||||
return self.Data().ChainCode()
|
||||
|
||||
def KeyNetVersions(self) -> Bip32KeyNetVersions:
|
||||
"""
|
||||
Get key net versions.
|
||||
|
||||
Returns:
|
||||
Bip32KeyNetVersions object: Bip32KeyNetVersions object
|
||||
"""
|
||||
return self.m_key_net_ver
|
||||
|
||||
@abstractmethod
|
||||
def ToExtended(self) -> str:
|
||||
"""
|
||||
Return key in serialized extended format.
|
||||
|
||||
Returns:
|
||||
str: Key in serialized extended format
|
||||
"""
|
||||
|
||||
|
||||
class Bip32PublicKey(_Bip32KeyBase):
|
||||
"""
|
||||
BIP32 public key class.
|
||||
It represents a public key used by BIP32 with all the related data (e.g. depth, chain code, etc...).
|
||||
"""
|
||||
|
||||
m_pub_key: IPublicKey
|
||||
|
||||
@classmethod
|
||||
def FromBytesOrKeyObject(cls,
|
||||
pub_key: Union[bytes, IPoint, IPublicKey],
|
||||
key_data: Bip32KeyData,
|
||||
key_net_ver: Bip32KeyNetVersions,
|
||||
curve_type: EllipticCurveTypes) -> Bip32PublicKey:
|
||||
"""
|
||||
Get the public key from key bytes or object.
|
||||
|
||||
Args:
|
||||
pub_key (bytes, IPoint or IPublicKey) : Public key
|
||||
key_data (Bip32KeyData object) : Key data
|
||||
key_net_ver (Bip32KeyNetVersions object): Key net versions
|
||||
curve_type (EllipticCurveTypes) : Elliptic curve type
|
||||
|
||||
Returns:
|
||||
Bip32PublicKey object: Bip32PublicKey object
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the key constructed from the bytes is not valid
|
||||
"""
|
||||
if isinstance(pub_key, bytes):
|
||||
return cls.FromBytes(pub_key, key_data, key_net_ver, curve_type)
|
||||
if isinstance(pub_key, IPoint):
|
||||
return cls.FromPoint(pub_key, key_data, key_net_ver)
|
||||
return cls(pub_key, key_data, key_net_ver)
|
||||
|
||||
@classmethod
|
||||
def FromBytes(cls,
|
||||
key_bytes: bytes,
|
||||
key_data: Bip32KeyData,
|
||||
key_net_ver: Bip32KeyNetVersions,
|
||||
curve_type: EllipticCurveTypes) -> Bip32PublicKey:
|
||||
"""
|
||||
Create from bytes.
|
||||
|
||||
Args:
|
||||
key_bytes (bytes) : Key bytes
|
||||
key_data (Bip32KeyData object) : Key data
|
||||
key_net_ver (Bip32KeyNetVersions object): Key net versions
|
||||
curve_type (EllipticCurveTypes) : Elliptic curve type
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the key constructed from the bytes is not valid
|
||||
"""
|
||||
return cls(cls.__KeyFromBytes(key_bytes, curve_type),
|
||||
key_data,
|
||||
key_net_ver)
|
||||
|
||||
@classmethod
|
||||
def FromPoint(cls,
|
||||
key_point: IPoint,
|
||||
key_data: Bip32KeyData,
|
||||
key_net_ver: Bip32KeyNetVersions) -> Bip32PublicKey:
|
||||
"""
|
||||
Create from point.
|
||||
|
||||
Args:
|
||||
key_point (IPoint object) : Key point
|
||||
key_data (Bip32KeyData object) : Key data
|
||||
key_net_ver (Bip32KeyNetVersions object): Key net versions
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the key constructed from the bytes is not valid
|
||||
"""
|
||||
return cls(cls.__KeyFromPoint(key_point),
|
||||
key_data,
|
||||
key_net_ver)
|
||||
|
||||
def __init__(self,
|
||||
pub_key: IPublicKey,
|
||||
key_data: Bip32KeyData,
|
||||
key_net_ver: Bip32KeyNetVersions) -> None:
|
||||
"""
|
||||
Construct class.
|
||||
|
||||
Args:
|
||||
pub_key (IPublicKey object) : Key object
|
||||
key_data (Bip32KeyData object) : Key data
|
||||
key_net_ver (Bip32KeyNetVersions object): Key net versions
|
||||
"""
|
||||
super().__init__(key_data, key_net_ver, pub_key.CurveType())
|
||||
self.m_pub_key = pub_key
|
||||
|
||||
def KeyObject(self) -> IPublicKey:
|
||||
"""
|
||||
Return the key object.
|
||||
|
||||
Returns:
|
||||
IPublicKey object: Key object
|
||||
"""
|
||||
return self.m_pub_key
|
||||
|
||||
@lru_cache()
|
||||
def RawCompressed(self) -> DataBytes:
|
||||
"""
|
||||
Return raw compressed public key.
|
||||
|
||||
Returns:
|
||||
DataBytes object: DataBytes object
|
||||
"""
|
||||
return self.m_pub_key.RawCompressed()
|
||||
|
||||
@lru_cache()
|
||||
def RawUncompressed(self) -> DataBytes:
|
||||
"""
|
||||
Return raw uncompressed public key.
|
||||
|
||||
Returns:
|
||||
DataBytes object: DataBytes object
|
||||
"""
|
||||
return self.m_pub_key.RawUncompressed()
|
||||
|
||||
def Point(self) -> IPoint:
|
||||
"""
|
||||
Get public key point.
|
||||
|
||||
Returns:
|
||||
IPoint object: IPoint object
|
||||
"""
|
||||
return self.m_pub_key.Point()
|
||||
|
||||
@lru_cache()
|
||||
def FingerPrint(self) -> Bip32FingerPrint:
|
||||
"""
|
||||
Get key fingerprint.
|
||||
|
||||
Returns:
|
||||
bytes: Key fingerprint bytes
|
||||
"""
|
||||
return Bip32FingerPrint(self.KeyIdentifier())
|
||||
|
||||
@lru_cache()
|
||||
def KeyIdentifier(self) -> bytes:
|
||||
"""
|
||||
Get key identifier.
|
||||
|
||||
Returns:
|
||||
bytes: Key identifier bytes
|
||||
"""
|
||||
return Hash160.QuickDigest(self.m_pub_key.RawCompressed().ToBytes())
|
||||
|
||||
@lru_cache()
|
||||
def ToExtended(self) -> str:
|
||||
"""
|
||||
Return key in serialized extended format.
|
||||
|
||||
Returns:
|
||||
str: Key in serialized extended format
|
||||
"""
|
||||
return Bip32PublicKeySerializer.Serialize(self.m_pub_key,
|
||||
self.m_key_data,
|
||||
self.m_key_net_ver)
|
||||
|
||||
@staticmethod
|
||||
def __KeyFromBytes(key_bytes: bytes,
|
||||
curve_type: EllipticCurveTypes) -> IPublicKey:
|
||||
"""
|
||||
Construct key from bytes.
|
||||
|
||||
Args:
|
||||
key_bytes (bytes) : Key bytes
|
||||
curve_type (EllipticCurveTypes): Elliptic curve type
|
||||
|
||||
Returns:
|
||||
IPublicKey object: IPublicKey object
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the key constructed from the bytes is not valid
|
||||
"""
|
||||
try:
|
||||
curve = EllipticCurveGetter.FromType(curve_type)
|
||||
return curve.PublicKeyClass().FromBytes(key_bytes)
|
||||
except ValueError as ex:
|
||||
raise Bip32KeyError("Invalid public key bytes") from ex
|
||||
|
||||
@staticmethod
|
||||
def __KeyFromPoint(key_point: IPoint) -> IPublicKey:
|
||||
"""
|
||||
Construct key from point.
|
||||
|
||||
Args:
|
||||
key_point (IPoint object): Key point
|
||||
|
||||
Returns:
|
||||
IPublicKey object: IPublicKey object
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the key constructed from the bytes is not valid
|
||||
"""
|
||||
try:
|
||||
curve = EllipticCurveGetter.FromType(key_point.CurveType())
|
||||
return curve.PublicKeyClass().FromPoint(key_point)
|
||||
except ValueError as ex:
|
||||
raise Bip32KeyError("Invalid public key point") from ex
|
||||
|
||||
|
||||
class Bip32PrivateKey(_Bip32KeyBase):
|
||||
"""
|
||||
BIP32 private key class.
|
||||
It represents a private key used by BIP32 with all the related data (e.g. depth, chain code, etc...).
|
||||
"""
|
||||
|
||||
m_priv_key: IPrivateKey
|
||||
|
||||
@classmethod
|
||||
def FromBytesOrKeyObject(cls,
|
||||
priv_key: Union[bytes, IPrivateKey],
|
||||
key_data: Bip32KeyData,
|
||||
key_net_ver: Bip32KeyNetVersions,
|
||||
curve_type: EllipticCurveTypes) -> Bip32PrivateKey:
|
||||
"""
|
||||
Get the public key from key bytes or object.
|
||||
|
||||
Args:
|
||||
priv_key (bytes or IPrivateKey) : Private key
|
||||
key_data (Bip32KeyData object) : Key data
|
||||
key_net_ver (Bip32KeyNetVersions object): Key net versions
|
||||
curve_type (EllipticCurveTypes) : Elliptic curve type
|
||||
|
||||
Returns:
|
||||
Bip32PrivateKey object: Bip32PrivateKey object
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the key constructed from the bytes is not valid
|
||||
"""
|
||||
return (cls.FromBytes(priv_key, key_data, key_net_ver, curve_type)
|
||||
if isinstance(priv_key, bytes)
|
||||
else cls(priv_key, key_data, key_net_ver))
|
||||
|
||||
@classmethod
|
||||
def FromBytes(cls,
|
||||
key_bytes: bytes,
|
||||
key_data: Bip32KeyData,
|
||||
key_net_ver: Bip32KeyNetVersions,
|
||||
curve_type: EllipticCurveTypes) -> Bip32PrivateKey:
|
||||
"""
|
||||
Create from bytes.
|
||||
|
||||
Args:
|
||||
key_bytes (bytes) : Key bytes
|
||||
key_data (Bip32KeyData object) : Key data
|
||||
key_net_ver (Bip32KeyNetVersions object): Key net versions
|
||||
curve_type (EllipticCurveTypes) : Elliptic curve type
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the key constructed from the bytes is not valid
|
||||
"""
|
||||
return cls(cls.__KeyFromBytes(key_bytes, curve_type),
|
||||
key_data,
|
||||
key_net_ver)
|
||||
|
||||
def __init__(self,
|
||||
priv_key: IPrivateKey,
|
||||
key_data: Bip32KeyData,
|
||||
key_net_ver: Bip32KeyNetVersions) -> None:
|
||||
"""
|
||||
Construct class.
|
||||
|
||||
Args:
|
||||
priv_key (IPrivateKey object) : Key object
|
||||
key_data (Bip32KeyData object) : Key data
|
||||
key_net_ver (Bip32KeyNetVersions object): Key net versions
|
||||
"""
|
||||
super().__init__(key_data, key_net_ver, priv_key.CurveType())
|
||||
self.m_priv_key = priv_key
|
||||
|
||||
def KeyObject(self) -> IPrivateKey:
|
||||
"""
|
||||
Return the key object.
|
||||
|
||||
Returns:
|
||||
IPrivateKey object: Key object
|
||||
"""
|
||||
return self.m_priv_key
|
||||
|
||||
@lru_cache()
|
||||
def Raw(self) -> DataBytes:
|
||||
"""
|
||||
Return raw private key.
|
||||
|
||||
Returns:
|
||||
DataBytes object: DataBytes object
|
||||
"""
|
||||
return self.m_priv_key.Raw()
|
||||
|
||||
@lru_cache()
|
||||
def PublicKey(self) -> Bip32PublicKey:
|
||||
"""
|
||||
Get the public key correspondent to the private one.
|
||||
|
||||
Returns:
|
||||
Bip32PublicKey object: Bip32PublicKey object
|
||||
"""
|
||||
return Bip32PublicKey(self.m_priv_key.PublicKey(),
|
||||
self.m_key_data,
|
||||
self.m_key_net_ver)
|
||||
|
||||
@lru_cache()
|
||||
def ToExtended(self) -> str:
|
||||
"""
|
||||
Return key in serialized extended format.
|
||||
|
||||
Returns:
|
||||
str: Key in serialized extended format
|
||||
"""
|
||||
return Bip32PrivateKeySerializer.Serialize(self.m_priv_key,
|
||||
self.m_key_data,
|
||||
self.m_key_net_ver)
|
||||
|
||||
@staticmethod
|
||||
def __KeyFromBytes(key_bytes: bytes,
|
||||
curve_type: EllipticCurveTypes) -> IPrivateKey:
|
||||
"""
|
||||
Construct key from bytes.
|
||||
|
||||
Args:
|
||||
key_bytes (bytes) : Key bytes
|
||||
curve_type (EllipticCurveTypes): Elliptic curve type
|
||||
|
||||
Returns:
|
||||
IPrivateKey object: IPrivateKey object
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the key constructed from the bytes is not valid
|
||||
"""
|
||||
try:
|
||||
curve = EllipticCurveGetter.FromType(curve_type)
|
||||
return curve.PrivateKeyClass().FromBytes(key_bytes)
|
||||
except ValueError as ex:
|
||||
raise Bip32KeyError("Invalid private key bytes") from ex
|
||||
@ -0,0 +1,247 @@
|
||||
# Copyright (c) 2021 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""Module for BIP32 paths parsing and handling."""
|
||||
|
||||
# Import
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Iterator, List, Optional, Sequence, Tuple, Union
|
||||
|
||||
from .bip32_ex import Bip32PathError
|
||||
from .bip32_key_data import Bip32KeyIndex
|
||||
|
||||
|
||||
class Bip32PathConst:
|
||||
"""Class container for BIP32 path constants."""
|
||||
|
||||
# Hardened characters
|
||||
HARDENED_CHARS: Tuple[str, str, str] = ("'", "h", "p")
|
||||
# Master character
|
||||
MASTER_CHAR: str = "m"
|
||||
|
||||
|
||||
class Bip32Path:
|
||||
"""
|
||||
BIP32 path class.
|
||||
It represents a BIP-0032 path.
|
||||
"""
|
||||
|
||||
m_elems: List[Bip32KeyIndex]
|
||||
m_is_absolute: bool
|
||||
|
||||
def __init__(self,
|
||||
elems: Optional[Sequence[Union[int, Bip32KeyIndex]]] = None,
|
||||
is_absolute: bool = True) -> None:
|
||||
"""
|
||||
Construct class.
|
||||
|
||||
Args:
|
||||
elems (list, optional) : Path elements (default: empty)
|
||||
is_absolute (bool, optional): True if path is an absolute one, false otherwise (default: True)
|
||||
"""
|
||||
try:
|
||||
self.m_elems = ([]
|
||||
if elems is None
|
||||
else [Bip32KeyIndex(elem) if isinstance(elem, int) else elem for elem in elems])
|
||||
except ValueError as ex:
|
||||
raise Bip32PathError("The path contains some invalid key indexes") from ex
|
||||
|
||||
self.m_is_absolute = is_absolute
|
||||
|
||||
def AddElem(self,
|
||||
elem: Union[int, Bip32KeyIndex]) -> Bip32Path:
|
||||
"""
|
||||
Return a new path object with the specified element added.
|
||||
|
||||
Args:
|
||||
elem (str or Bip32KeyIndex): Path element
|
||||
|
||||
Returns:
|
||||
Bip32Path object: Bip32Path object
|
||||
|
||||
Raises:
|
||||
Bip32PathError: If the path element is not valid
|
||||
"""
|
||||
if isinstance(elem, int):
|
||||
elem = Bip32KeyIndex(elem)
|
||||
return Bip32Path(self.m_elems + [elem], self.m_is_absolute)
|
||||
|
||||
def IsAbsolute(self) -> bool:
|
||||
"""
|
||||
Get if absolute path.
|
||||
|
||||
Returns:
|
||||
bool: True if absolute path, false otherwise
|
||||
"""
|
||||
return self.m_is_absolute
|
||||
|
||||
def Length(self) -> int:
|
||||
"""
|
||||
Get the number of elements of the path.
|
||||
|
||||
Returns:
|
||||
int: Number of elements
|
||||
"""
|
||||
return len(self.m_elems)
|
||||
|
||||
def ToList(self) -> List[int]:
|
||||
"""
|
||||
Get the path as a list of integers.
|
||||
|
||||
Returns:
|
||||
list[int]: Path as a list of integers
|
||||
"""
|
||||
return [int(elem) for elem in self.m_elems]
|
||||
|
||||
def ToStr(self) -> str:
|
||||
"""
|
||||
Get the path as a string.
|
||||
|
||||
Returns:
|
||||
str: Path as a string
|
||||
"""
|
||||
path_str = "" if not self.m_is_absolute else f"{Bip32PathConst.MASTER_CHAR}/"
|
||||
for elem in self.m_elems:
|
||||
if not elem.IsHardened():
|
||||
path_str += f"{str(elem.ToInt())}/"
|
||||
else:
|
||||
path_str += f"{str(Bip32KeyIndex.UnhardenIndex(elem.ToInt()))}'/"
|
||||
|
||||
return path_str[:-1]
|
||||
|
||||
def __str__(self) -> str:
|
||||
"""
|
||||
Get the path as a string.
|
||||
|
||||
Returns:
|
||||
str: Path as a string
|
||||
"""
|
||||
return self.ToStr()
|
||||
|
||||
def __getitem__(self,
|
||||
idx: int) -> Bip32KeyIndex:
|
||||
"""
|
||||
Get the specified element index.
|
||||
|
||||
Args:
|
||||
idx (int): Element index
|
||||
|
||||
Returns:
|
||||
Bip32KeyIndex object: Bip32KeyIndex object
|
||||
"""
|
||||
return self.m_elems[idx]
|
||||
|
||||
def __iter__(self) -> Iterator[Bip32KeyIndex]:
|
||||
"""
|
||||
Get the iterator to the current element.
|
||||
|
||||
Returns:
|
||||
Iterator object: Iterator to the current element
|
||||
"""
|
||||
yield from self.m_elems
|
||||
|
||||
|
||||
class Bip32PathParser:
|
||||
"""
|
||||
BIP32 path parser class.
|
||||
It parses a BIP-0032 path and returns a Bip32Path object.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def Parse(path: str) -> Bip32Path:
|
||||
"""
|
||||
Parse a path and return a Bip32Path object.
|
||||
|
||||
Args:
|
||||
path (str): Path
|
||||
|
||||
Returns:
|
||||
Bip32Path object: Bip32Path object
|
||||
|
||||
Raises:
|
||||
Bip32PathError: If the path is not valid
|
||||
"""
|
||||
|
||||
# Remove trailing "/" if any
|
||||
if path.endswith("/"):
|
||||
path = path[:-1]
|
||||
|
||||
# Parse elements
|
||||
return Bip32PathParser.__ParseElements(
|
||||
list(filter(None, path.split("/")))
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def __ParseElements(path_elems: List[str]) -> Bip32Path:
|
||||
"""
|
||||
Parse path elements and return a Bip32Path object.
|
||||
|
||||
Args:
|
||||
path_elems (list[str]): Path elements
|
||||
|
||||
Returns:
|
||||
Bip32Path object: Bip32Path object
|
||||
|
||||
Raises:
|
||||
Bip32PathError: If the path is not valid
|
||||
"""
|
||||
|
||||
# Remove the initial "m" character if any
|
||||
if len(path_elems) > 0 and path_elems[0] == Bip32PathConst.MASTER_CHAR:
|
||||
path_elems = path_elems[1:]
|
||||
is_absolute = True
|
||||
else:
|
||||
is_absolute = False
|
||||
|
||||
# Parse elements
|
||||
parsed_elems = list(map(Bip32PathParser.__ParseElem, path_elems))
|
||||
return Bip32Path(parsed_elems, is_absolute)
|
||||
|
||||
@staticmethod
|
||||
def __ParseElem(path_elem: str) -> int:
|
||||
"""
|
||||
Parse path element and get the correspondent index.
|
||||
|
||||
Args:
|
||||
path_elem (str): Path element
|
||||
|
||||
Returns:
|
||||
int: Index of the element, None if the element is not a valid index
|
||||
|
||||
Raises:
|
||||
Bip32PathError: If the path is not valid
|
||||
"""
|
||||
|
||||
# Strip spaces
|
||||
path_elem = path_elem.strip()
|
||||
|
||||
# Get if hardened
|
||||
is_hardened = path_elem.endswith(Bip32PathConst.HARDENED_CHARS)
|
||||
|
||||
# If hardened, remove the last character from the string
|
||||
if is_hardened:
|
||||
path_elem = path_elem[:-1]
|
||||
|
||||
# The remaining string shall be numeric
|
||||
if not path_elem.isnumeric():
|
||||
raise Bip32PathError(f"Invalid path element ({path_elem})")
|
||||
|
||||
return int(path_elem) if not is_hardened else Bip32KeyIndex.HardenIndex(int(path_elem))
|
||||
@ -0,0 +1,72 @@
|
||||
# Copyright (c) 2021 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""Module with BIP32 utility functions."""
|
||||
|
||||
# Imports
|
||||
from .bip32_key_data import Bip32KeyIndex
|
||||
|
||||
|
||||
class Bip32Utils:
|
||||
"""
|
||||
BIP32 utility class.
|
||||
It contains some helper methods for Bip32 indexes.
|
||||
|
||||
Deprecated: only for compatibility, methods were moved to Bip32KeyIndex.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def HardenIndex(index: int) -> int:
|
||||
"""
|
||||
Harden the specified index and return it.
|
||||
|
||||
Args:
|
||||
index (int): Index
|
||||
|
||||
Returns:
|
||||
int: Hardened index
|
||||
"""
|
||||
return Bip32KeyIndex.HardenIndex(index)
|
||||
|
||||
@staticmethod
|
||||
def UnhardenIndex(index: int) -> int:
|
||||
"""
|
||||
Unharden the specified index and return it.
|
||||
|
||||
Args:
|
||||
index (int): Index
|
||||
|
||||
Returns:
|
||||
int: Unhardened index
|
||||
"""
|
||||
return Bip32KeyIndex.UnhardenIndex(index)
|
||||
|
||||
@staticmethod
|
||||
def IsHardenedIndex(index: int) -> bool:
|
||||
"""
|
||||
Get if the specified index is hardened.
|
||||
|
||||
Args:
|
||||
index (int): Index
|
||||
|
||||
Returns:
|
||||
bool: True if hardened, false otherwise
|
||||
"""
|
||||
return Bip32KeyIndex.IsHardenedIndex(index)
|
||||
@ -0,0 +1,4 @@
|
||||
# from .kholaw.bip32_kholaw_ed25519 import Bip32Ed25519Kholaw, Bip32KholawEd25519
|
||||
# from .kholaw.bip32_kholaw_ed25519_key_derivator import Bip32KholawEd25519KeyDerivator
|
||||
# from .kholaw.bip32_kholaw_key_derivator_base import Bip32KholawEd25519KeyDerivatorBase
|
||||
# from .kholaw.bip32_kholaw_mst_key_generator import Bip32KholawEd25519MstKeyGenerator
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,82 @@
|
||||
# Copyright (c) 2021 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""Module for keys derivation based on ed25519 curve as defined by BIP32 Khovratovich/Law."""
|
||||
|
||||
# Imports
|
||||
from typing import Type
|
||||
|
||||
from .base import Bip32Base, IBip32KeyDerivator, IBip32MstKeyGenerator
|
||||
from .bip32_const import Bip32Const
|
||||
from .bip32_key_net_ver import Bip32KeyNetVersions
|
||||
from .kholaw.bip32_kholaw_ed25519_key_derivator import Bip32KholawEd25519KeyDerivator
|
||||
from .kholaw.bip32_kholaw_mst_key_generator import Bip32KholawEd25519MstKeyGenerator
|
||||
from bip_utils.ecc import EllipticCurveTypes
|
||||
|
||||
|
||||
class Bip32KholawEd25519(Bip32Base):
|
||||
"""
|
||||
BIP32 Khovratovich/Law ed25519 class.
|
||||
It allows master keys generation and keys derivation using ed25519 curve.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def CurveType() -> EllipticCurveTypes:
|
||||
"""
|
||||
Return the elliptic curve type.
|
||||
|
||||
Returns:
|
||||
EllipticCurveTypes: Curve type
|
||||
"""
|
||||
return EllipticCurveTypes.ED25519_KHOLAW
|
||||
|
||||
@staticmethod
|
||||
def _DefaultKeyNetVersion() -> Bip32KeyNetVersions:
|
||||
"""
|
||||
Return the default key net version.
|
||||
|
||||
Returns:
|
||||
Bip32KeyNetVersions object: Bip32KeyNetVersions object
|
||||
"""
|
||||
return Bip32Const.KHOLAW_KEY_NET_VERSIONS
|
||||
|
||||
@staticmethod
|
||||
def _KeyDerivator() -> Type[IBip32KeyDerivator]:
|
||||
"""
|
||||
Return the key derivator class.
|
||||
|
||||
Returns:
|
||||
IBip32KeyDerivator class: Key derivator class
|
||||
"""
|
||||
return Bip32KholawEd25519KeyDerivator
|
||||
|
||||
@staticmethod
|
||||
def _MasterKeyGenerator() -> Type[IBip32MstKeyGenerator]:
|
||||
"""
|
||||
Return the master key generator class.
|
||||
|
||||
Returns:
|
||||
IBip32MstKeyGenerator class: Master key generator class
|
||||
"""
|
||||
return Bip32KholawEd25519MstKeyGenerator
|
||||
|
||||
|
||||
# Deprecated: only for compatibility
|
||||
Bip32Ed25519Kholaw = Bip32KholawEd25519
|
||||
@ -0,0 +1,118 @@
|
||||
# Copyright (c) 2022 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""
|
||||
Module for BIP32 Khovratovich/Law keys derivation.
|
||||
Reference: https://github.com/LedgerHQ/orakolo/blob/master/papers/Ed25519_BIP%20Final.pdf
|
||||
"""
|
||||
|
||||
# Imports
|
||||
from .bip32_ex import Bip32KeyError
|
||||
from .bip32_key_data import Bip32KeyIndex
|
||||
from .bip32_keys import Bip32PublicKey
|
||||
from .kholaw.bip32_kholaw_key_derivator_base import Bip32KholawEd25519KeyDerivatorBase
|
||||
from bip_utils.ecc import Ed25519KholawPrivateKey, EllipticCurve, IPoint
|
||||
from ..utils.misc import BytesUtils, IntegerUtils
|
||||
|
||||
|
||||
class Bip32KholawEd25519KeyDerivator(Bip32KholawEd25519KeyDerivatorBase):
|
||||
"""
|
||||
BIP32 Khovratovich/Law ed25519 key derivator class.
|
||||
It allows keys derivation for ed25519 curves in according to BIP32 Khovratovich/Law.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def _SerializeIndex(index: Bip32KeyIndex) -> bytes:
|
||||
"""
|
||||
Serialize key index.
|
||||
|
||||
Args:
|
||||
index (Bip32KeyIndex object): Key index
|
||||
|
||||
Returns:
|
||||
bytes: Serialized index
|
||||
"""
|
||||
return index.ToBytes(endianness="little")
|
||||
|
||||
@staticmethod
|
||||
def _NewPrivateKeyLeftPart(zl_bytes: bytes,
|
||||
kl_bytes: bytes,
|
||||
curve: EllipticCurve) -> bytes:
|
||||
"""
|
||||
Compute the new private key left part for private derivation.
|
||||
|
||||
Args:
|
||||
zl_bytes (bytes) : Leftmost Z 32-byte
|
||||
kl_bytes (bytes) : Leftmost private key 32-byte
|
||||
curve (EllipticCurve object): EllipticCurve object
|
||||
|
||||
Returns:
|
||||
bytes: Leftmost new private key 32-byte
|
||||
"""
|
||||
zl_int = BytesUtils.ToInteger(zl_bytes[:28], endianness="little")
|
||||
kl_int = BytesUtils.ToInteger(kl_bytes, endianness="little")
|
||||
|
||||
prvl_int = (zl_int * 8) + kl_int
|
||||
# Discard child if multiple of curve order
|
||||
if prvl_int % curve.Order() == 0:
|
||||
raise Bip32KeyError("Computed child key is not valid, very unlucky index")
|
||||
|
||||
return IntegerUtils.ToBytes(prvl_int,
|
||||
bytes_num=Ed25519KholawPrivateKey.Length() // 2,
|
||||
endianness="little")
|
||||
|
||||
@staticmethod
|
||||
def _NewPrivateKeyRightPart(zr_bytes: bytes,
|
||||
kr_bytes: bytes) -> bytes:
|
||||
"""
|
||||
Compute the new private key right part for private derivation.
|
||||
|
||||
Args:
|
||||
zr_bytes (bytes): Rightmost Z 32-byte
|
||||
kr_bytes (bytes): Rightmost private key 32-byte
|
||||
|
||||
Returns:
|
||||
bytes: Rightmost new private key 32-byte
|
||||
"""
|
||||
zr_int = BytesUtils.ToInteger(zr_bytes, endianness="little")
|
||||
kpr_int = BytesUtils.ToInteger(kr_bytes, endianness="little")
|
||||
kr_int = (zr_int + kpr_int) % (2 ** 256)
|
||||
|
||||
return IntegerUtils.ToBytes(kr_int,
|
||||
bytes_num=Ed25519KholawPrivateKey.Length() // 2,
|
||||
endianness="little")
|
||||
|
||||
@staticmethod
|
||||
def _NewPublicKeyPoint(pub_key: Bip32PublicKey,
|
||||
zl_bytes: bytes) -> IPoint:
|
||||
"""
|
||||
Compute new public key point for public derivation.
|
||||
|
||||
Args:
|
||||
pub_key (Bip32PublicKey object): Bip32PublicKey object
|
||||
zl_bytes (bytes) : Leftmost Z 32-byte
|
||||
|
||||
Returns:
|
||||
IPoint object: IPoint object
|
||||
"""
|
||||
|
||||
# Compute the new public key point: PKEY + 8ZL * G
|
||||
zl_int = BytesUtils.ToInteger(zl_bytes[:28], endianness="little")
|
||||
return pub_key.Point() + ((zl_int * 8) * pub_key.Curve().Generator())
|
||||
@ -0,0 +1,204 @@
|
||||
# Copyright (c) 2022 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""
|
||||
Module for BIP32 Khovratovich/Law keys derivation (base).
|
||||
Reference: https://github.com/LedgerHQ/orakolo/blob/master/papers/Ed25519_BIP%20Final.pdf
|
||||
"""
|
||||
|
||||
# Imports
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Tuple, Union
|
||||
|
||||
from .base import IBip32KeyDerivator
|
||||
from .bip32_ex import Bip32KeyError
|
||||
from .bip32_key_data import Bip32KeyIndex
|
||||
from .bip32_keys import Bip32PrivateKey, Bip32PublicKey
|
||||
from bip_utils.ecc import EllipticCurve, IPoint
|
||||
from ..utils.crypto import HmacSha512
|
||||
|
||||
|
||||
class Bip32KholawEd25519KeyDerivatorBase(IBip32KeyDerivator, ABC):
|
||||
"""
|
||||
BIP32 Khovratovich/Law ed25519 key derivator base class.
|
||||
It allows keys derivation for ed25519 curves in according to BIP32 Khovratovich/Law.
|
||||
It shall be inherited by child classes to customize the derivation algorithm.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def IsPublicDerivationSupported() -> bool:
|
||||
"""
|
||||
Get if public derivation is supported.
|
||||
|
||||
Returns:
|
||||
bool: True if supported, false otherwise.
|
||||
"""
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def CkdPriv(cls,
|
||||
priv_key: Bip32PrivateKey,
|
||||
pub_key: Bip32PublicKey,
|
||||
index: Bip32KeyIndex) -> Tuple[bytes, bytes]:
|
||||
"""
|
||||
Derive a child key with the specified index using private derivation.
|
||||
|
||||
Args:
|
||||
priv_key (Bip32PrivateKey object): Bip32PrivateKey object
|
||||
pub_key (Bip32PublicKey object) : Bip32PublicKey object
|
||||
index (Bip32KeyIndex object) : Key index
|
||||
|
||||
Returns:
|
||||
tuple[bytes, bytes]: Private key bytes (index 0) and chain code bytes (index 1)
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the index results in an invalid key
|
||||
"""
|
||||
|
||||
# Get index and key bytes
|
||||
index_bytes = cls._SerializeIndex(index)
|
||||
chain_code_bytes = priv_key.ChainCode().ToBytes()
|
||||
priv_key_bytes = priv_key.Raw().ToBytes()
|
||||
pub_key_bytes = pub_key.RawCompressed().ToBytes()[1:]
|
||||
|
||||
# Compute Z and chain code
|
||||
if index.IsHardened():
|
||||
z_bytes = HmacSha512.QuickDigest(chain_code_bytes,
|
||||
b"\x00" + priv_key_bytes + index_bytes)
|
||||
chain_code_bytes = HmacSha512.QuickDigestHalves(chain_code_bytes,
|
||||
b"\x01" + priv_key_bytes + index_bytes)[1]
|
||||
else:
|
||||
z_bytes = HmacSha512.QuickDigest(chain_code_bytes,
|
||||
b"\x02" + pub_key_bytes + index_bytes)
|
||||
chain_code_bytes = HmacSha512.QuickDigestHalves(chain_code_bytes,
|
||||
b"\x03" + pub_key_bytes + index_bytes)[1]
|
||||
|
||||
# Compute the left and right part of the new private key
|
||||
hmac_half_len = HmacSha512.DigestSize() // 2
|
||||
kl_bytes = cls._NewPrivateKeyLeftPart(z_bytes[:hmac_half_len],
|
||||
priv_key_bytes[:hmac_half_len],
|
||||
pub_key.Curve())
|
||||
kr_bytes = cls._NewPrivateKeyRightPart(z_bytes[hmac_half_len:],
|
||||
priv_key_bytes[hmac_half_len:])
|
||||
|
||||
return kl_bytes + kr_bytes, chain_code_bytes
|
||||
|
||||
@classmethod
|
||||
def CkdPub(cls,
|
||||
pub_key: Bip32PublicKey,
|
||||
index: Bip32KeyIndex) -> Tuple[Union[bytes, IPoint], bytes]:
|
||||
"""
|
||||
Derive a child key with the specified index using public derivation.
|
||||
|
||||
Args:
|
||||
pub_key (Bip32PublicKey object): Bip32PublicKey object
|
||||
index (Bip32KeyIndex object) : Key index
|
||||
|
||||
Returns:
|
||||
tuple[bytes or IPoint, bytes]: Public key bytes or point (index 0) and chain code bytes (index 1)
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the index results in an invalid key
|
||||
"""
|
||||
|
||||
# Get index and key bytes
|
||||
index_bytes = cls._SerializeIndex(index)
|
||||
chain_code_bytes = pub_key.ChainCode().ToBytes()
|
||||
pub_key_bytes = pub_key.RawCompressed().ToBytes()[1:]
|
||||
|
||||
# Compute Z and chain code
|
||||
z_bytes = HmacSha512.QuickDigest(chain_code_bytes,
|
||||
b"\x02" + pub_key_bytes + index_bytes)
|
||||
chain_code_bytes = HmacSha512.QuickDigestHalves(chain_code_bytes,
|
||||
b"\x03" + pub_key_bytes + index_bytes)[1]
|
||||
|
||||
# Compute the new public key point
|
||||
hmac_half_len = HmacSha512.DigestSize() // 2
|
||||
new_pub_key_point = cls._NewPublicKeyPoint(pub_key,
|
||||
z_bytes[:hmac_half_len])
|
||||
# If the public key is the identity point (0, 1) discard the child
|
||||
if new_pub_key_point.X() == 0 and new_pub_key_point.Y() == 1:
|
||||
raise Bip32KeyError("Computed public child key is not valid, very unlucky index")
|
||||
|
||||
return new_pub_key_point, chain_code_bytes
|
||||
|
||||
#
|
||||
# Abstract methods
|
||||
#
|
||||
|
||||
@staticmethod
|
||||
@abstractmethod
|
||||
def _SerializeIndex(index: Bip32KeyIndex) -> bytes:
|
||||
"""
|
||||
Serialize key index.
|
||||
|
||||
Args:
|
||||
index (Bip32KeyIndex object): Key index
|
||||
|
||||
Returns:
|
||||
bytes: Serialized index
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
@abstractmethod
|
||||
def _NewPrivateKeyLeftPart(zl_bytes: bytes,
|
||||
kl_bytes: bytes,
|
||||
curve: EllipticCurve) -> bytes:
|
||||
"""
|
||||
Compute the new private key left part for private derivation.
|
||||
|
||||
Args:
|
||||
zl_bytes (bytes) : Leftmost Z 32-byte
|
||||
kl_bytes (bytes) : Leftmost private key 32-byte
|
||||
curve (EllipticCurve object): EllipticCurve object
|
||||
|
||||
Returns:
|
||||
bytes: Leftmost new private key 32-byte
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
@abstractmethod
|
||||
def _NewPrivateKeyRightPart(zr_bytes: bytes,
|
||||
kr_bytes: bytes) -> bytes:
|
||||
"""
|
||||
Compute the new private key right part for private derivation.
|
||||
|
||||
Args:
|
||||
zr_bytes (bytes): Rightmost Z 32-byte
|
||||
kr_bytes (bytes): Rightmost private key 32-byte
|
||||
|
||||
Returns:
|
||||
bytes: Rightmost new private key 32-byte
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
@abstractmethod
|
||||
def _NewPublicKeyPoint(pub_key: Bip32PublicKey,
|
||||
zl_bytes: bytes) -> IPoint:
|
||||
"""
|
||||
Compute new public key point for public derivation.
|
||||
|
||||
Args:
|
||||
pub_key (Bip32PublicKey object): Bip32PublicKey object
|
||||
zl_bytes (bytes) : Leftmost Z 32-byte
|
||||
|
||||
Returns:
|
||||
IPoint object: IPoint object
|
||||
"""
|
||||
@ -0,0 +1,119 @@
|
||||
# Copyright (c) 2022 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""
|
||||
Module for BIP32 Khovratovich/Law master key generation.
|
||||
Reference: https://github.com/LedgerHQ/orakolo/blob/master/papers/Ed25519_BIP%20Final.pdf
|
||||
"""
|
||||
|
||||
# Imports
|
||||
from typing import Tuple
|
||||
|
||||
from .base import IBip32MstKeyGenerator
|
||||
from .slip10.bip32_slip10_mst_key_generator import Bip32Slip10MstKeyGeneratorConst
|
||||
from ..utils.crypto import HmacSha256, HmacSha512
|
||||
from ..utils.misc import BitUtils
|
||||
|
||||
|
||||
class Bip32KholawMstKeyGeneratorConst:
|
||||
"""Class container for BIP32 Khovratovich/Law master key generator constants."""
|
||||
|
||||
# Minimum length in bytes for seed
|
||||
SEED_MIN_BYTE_LEN: int = Bip32Slip10MstKeyGeneratorConst.SEED_MIN_BYTE_LEN
|
||||
# HMAC key for generating master key
|
||||
MASTER_KEY_HMAC_KEY: bytes = Bip32Slip10MstKeyGeneratorConst.HMAC_KEY_ED25519_BYTES
|
||||
|
||||
|
||||
class Bip32KholawEd25519MstKeyGenerator(IBip32MstKeyGenerator):
|
||||
"""
|
||||
BIP32 Khovratovich/Law ed25519 master key generator class.
|
||||
It allows master keys generation in according to BIP32 Khovratovich/Law for ed25519 curve.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def GenerateFromSeed(cls,
|
||||
seed_bytes: bytes) -> Tuple[bytes, bytes]:
|
||||
"""
|
||||
Generate a master key from the specified seed.
|
||||
|
||||
Args:
|
||||
seed_bytes (bytes): Seed bytes
|
||||
|
||||
Returns:
|
||||
tuple[bytes, bytes]: Private key bytes (index 0) and chain code bytes (index 1)
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the seed is not suitable for master key generation
|
||||
ValueError: If seed length is not valid
|
||||
"""
|
||||
if len(seed_bytes) < Bip32KholawMstKeyGeneratorConst.SEED_MIN_BYTE_LEN:
|
||||
raise ValueError(f"Invalid seed length ({len(seed_bytes)})")
|
||||
|
||||
# Compute kL and kR
|
||||
kl_bytes, kr_bytes = cls.__HashRepeatedly(seed_bytes, Bip32KholawMstKeyGeneratorConst.MASTER_KEY_HMAC_KEY)
|
||||
# Tweak kL bytes
|
||||
kl_bytes = cls.__TweakMasterKeyBits(kl_bytes)
|
||||
|
||||
# Compute chain code
|
||||
chain_code_bytes = HmacSha256.QuickDigest(Bip32KholawMstKeyGeneratorConst.MASTER_KEY_HMAC_KEY,
|
||||
b"\x01" + seed_bytes)
|
||||
|
||||
return kl_bytes + kr_bytes, chain_code_bytes
|
||||
|
||||
@classmethod
|
||||
def __HashRepeatedly(cls,
|
||||
data_bytes: bytes,
|
||||
hmac_key_bytes: bytes) -> Tuple[bytes, bytes]:
|
||||
"""
|
||||
Continue to hash the data bytes until the third-highest bit of the last byte is not zero.
|
||||
|
||||
Args:
|
||||
data_bytes (bytes) : Data bytes
|
||||
hmac_key_bytes (bytes): HMAC key bytes
|
||||
|
||||
Returns:
|
||||
tuple[bytes, bytes]: Two halves of the computed hash
|
||||
"""
|
||||
kl_bytes, kr_bytes = HmacSha512.QuickDigestHalves(hmac_key_bytes,
|
||||
data_bytes)
|
||||
if BitUtils.AreBitsSet(kl_bytes[31], 0x20):
|
||||
return cls.__HashRepeatedly(kl_bytes + kr_bytes, hmac_key_bytes)
|
||||
return kl_bytes, kr_bytes
|
||||
|
||||
@staticmethod
|
||||
def __TweakMasterKeyBits(key_bytes: bytes) -> bytes:
|
||||
"""
|
||||
Tweak master key bits.
|
||||
|
||||
Args:
|
||||
key_bytes (bytes): Key bytes
|
||||
|
||||
Returns:
|
||||
bytes: Tweaked key bytes
|
||||
"""
|
||||
key_bytes = bytearray(key_bytes)
|
||||
# Clear the lowest 3 bits of the first byte of kL
|
||||
key_bytes[0] = BitUtils.ResetBits(key_bytes[0], 0x07)
|
||||
# Clear the highest bit of the last byte of kL
|
||||
key_bytes[31] = BitUtils.ResetBits(key_bytes[31], 0x80)
|
||||
# Set the second-highest bit of the last byte of kL
|
||||
key_bytes[31] = BitUtils.SetBits(key_bytes[31], 0x40)
|
||||
|
||||
return bytes(key_bytes)
|
||||
@ -0,0 +1 @@
|
||||
from .bip32_slip10_secp256k1 import Bip32Secp256k1, Bip32Slip10Secp256k1
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,200 @@
|
||||
# Copyright (c) 2021 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""
|
||||
Module for BIP32 SLIP-0010 keys derivation.
|
||||
|
||||
References:
|
||||
https://github.com/satoshilabs/slips/blob/master/slip-0010.md
|
||||
https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
|
||||
"""
|
||||
|
||||
# Imports
|
||||
from typing import Tuple, Union
|
||||
|
||||
from ..base import IBip32KeyDerivator
|
||||
from ..bip32_ex import Bip32KeyError
|
||||
from ..bip32_key_data import Bip32KeyIndex
|
||||
from ..bip32_keys import Bip32PrivateKey, Bip32PublicKey
|
||||
from ...ecc import IPoint
|
||||
from ...utils.crypto import HmacSha512
|
||||
from ...utils.misc import BytesUtils, IntegerUtils
|
||||
|
||||
|
||||
class Bip32Slip10DerivatorConst:
|
||||
"""Class container for BIP32 SLIP-0010 derivator constants."""
|
||||
|
||||
# Private key prefix
|
||||
PRIV_KEY_PREFIX: bytes = b"\x00"
|
||||
|
||||
|
||||
class Bip32Slip10EcdsaDerivator(IBip32KeyDerivator):
|
||||
"""
|
||||
BIP32 SLIP-0010 ECDSA key derivator class.
|
||||
It allows keys derivation for ECDSA curves in according to BIP32 SLIP-0010.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def IsPublicDerivationSupported() -> bool:
|
||||
"""
|
||||
Get if public derivation is supported.
|
||||
|
||||
Returns:
|
||||
bool: True if supported, false otherwise.
|
||||
"""
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def CkdPriv(cls,
|
||||
priv_key: Bip32PrivateKey,
|
||||
pub_key: Bip32PublicKey,
|
||||
index: Bip32KeyIndex) -> Tuple[bytes, bytes]:
|
||||
"""
|
||||
Derive a child key with the specified index using private derivation.
|
||||
|
||||
Args:
|
||||
priv_key (Bip32PrivateKey object): Bip32PrivateKey object
|
||||
pub_key (Bip32PublicKey object) : Bip32PublicKey object
|
||||
index (Bip32KeyIndex object) : Key index
|
||||
|
||||
Returns:
|
||||
tuple[bytes, bytes]: Private key bytes (index 0) and chain code bytes (index 1)
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the index results in an invalid key
|
||||
"""
|
||||
curve = pub_key.Curve()
|
||||
priv_key_bytes = priv_key.Raw().ToBytes()
|
||||
|
||||
# Data for HMAC
|
||||
if index.IsHardened():
|
||||
data_bytes = (Bip32Slip10DerivatorConst.PRIV_KEY_PREFIX
|
||||
+ priv_key_bytes
|
||||
+ index.ToBytes())
|
||||
else:
|
||||
data_bytes = pub_key.RawCompressed().ToBytes() + index.ToBytes()
|
||||
|
||||
# Compute HMAC halves
|
||||
il_bytes, ir_bytes = HmacSha512.QuickDigestHalves(priv_key.ChainCode().ToBytes(),
|
||||
data_bytes)
|
||||
|
||||
# Construct new key secret from iL and current private key
|
||||
il_int = BytesUtils.ToInteger(il_bytes)
|
||||
priv_key_int = BytesUtils.ToInteger(priv_key_bytes)
|
||||
new_priv_key_bytes = IntegerUtils.ToBytes((il_int + priv_key_int) % curve.Order(),
|
||||
bytes_num=curve.PrivateKeyClass().Length())
|
||||
|
||||
return new_priv_key_bytes, ir_bytes
|
||||
|
||||
@classmethod
|
||||
def CkdPub(cls,
|
||||
pub_key: Bip32PublicKey,
|
||||
index: Bip32KeyIndex) -> Tuple[Union[bytes, IPoint], bytes]:
|
||||
"""
|
||||
Derive a child key with the specified index using public derivation.
|
||||
|
||||
Args:
|
||||
pub_key (Bip32PublicKey object): Bip32PublicKey object
|
||||
index (Bip32KeyIndex object) : Key index
|
||||
|
||||
Returns:
|
||||
tuple[bytes or IPoint, bytes]: Public key bytes or point (index 0) and chain code bytes (index 1)
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the index results in an invalid key
|
||||
"""
|
||||
# Data for HMAC, same of CkdPriv() for public child key
|
||||
data_bytes = pub_key.RawCompressed().ToBytes() + index.ToBytes()
|
||||
|
||||
# Get HMAC of data
|
||||
il_bytes, ir_bytes = HmacSha512.QuickDigestHalves(pub_key.ChainCode().ToBytes(),
|
||||
data_bytes)
|
||||
il_int = BytesUtils.ToInteger(il_bytes)
|
||||
|
||||
# Get a new public key point: pub_key_point + G*iL
|
||||
new_pub_key_point = pub_key.Point() + (pub_key.Curve().Generator() * il_int)
|
||||
|
||||
return new_pub_key_point, ir_bytes
|
||||
|
||||
|
||||
class Bip32Slip10Ed25519Derivator(IBip32KeyDerivator):
|
||||
"""
|
||||
BIP32 SLIP-0010 ed25519 key derivator class.
|
||||
It allows keys derivation for ed25519 curves in according to BIP32 SLIP-0010.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def IsPublicDerivationSupported() -> bool:
|
||||
"""
|
||||
Get if public derivation is supported.
|
||||
|
||||
Returns:
|
||||
bool: True if supported, false otherwise.
|
||||
"""
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def CkdPriv(cls,
|
||||
priv_key: Bip32PrivateKey,
|
||||
pub_key: Bip32PublicKey,
|
||||
index: Bip32KeyIndex) -> Tuple[bytes, bytes]:
|
||||
"""
|
||||
Derive a child key with the specified index using private derivation.
|
||||
|
||||
Args:
|
||||
priv_key (Bip32PrivateKey object): Bip32PrivateKey object
|
||||
pub_key (Bip32PublicKey object) : Bip32PublicKey object
|
||||
index (Bip32KeyIndex object) : Key index
|
||||
|
||||
Returns:
|
||||
tuple[bytes, bytes]: Private key bytes (index 0) and chain code bytes (index 1)
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the index results in an invalid key
|
||||
"""
|
||||
if not index.IsHardened():
|
||||
raise Bip32KeyError("Private child derivation with not-hardened index is not supported")
|
||||
|
||||
# Data for HMAC
|
||||
data_bytes = (Bip32Slip10DerivatorConst.PRIV_KEY_PREFIX
|
||||
+ priv_key.Raw().ToBytes()
|
||||
+ index.ToBytes())
|
||||
# Compute HMAC halves
|
||||
return HmacSha512.QuickDigestHalves(priv_key.ChainCode().ToBytes(),
|
||||
data_bytes)
|
||||
|
||||
@classmethod
|
||||
def CkdPub(cls,
|
||||
pub_key: Bip32PublicKey,
|
||||
index: Bip32KeyIndex) -> Tuple[Union[bytes, IPoint], bytes]:
|
||||
"""
|
||||
Derive a child key with the specified index using public derivation.
|
||||
|
||||
Args:
|
||||
pub_key (Bip32PublicKey object): Bip32PublicKey object
|
||||
index (Bip32KeyIndex object) : Key index
|
||||
|
||||
Returns:
|
||||
tuple[bytes or IPoint, bytes]: Public key bytes or point (index 0) and chain code bytes (index 1)
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the index results in an invalid key
|
||||
"""
|
||||
raise Bip32KeyError("Public child derivation is not supported")
|
||||
@ -0,0 +1,168 @@
|
||||
# Copyright (c) 2021 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""
|
||||
Module for BIP32 SLIP-0010 master key generation.
|
||||
Reference: https://github.com/satoshilabs/slips/blob/master/slip-0010.md
|
||||
"""
|
||||
|
||||
# Imports
|
||||
from typing import Tuple
|
||||
|
||||
from ..base import IBip32MstKeyGenerator
|
||||
from ...ecc import EllipticCurveGetter, EllipticCurveTypes
|
||||
from ...utils.crypto import HmacSha512
|
||||
|
||||
|
||||
class Bip32Slip10MstKeyGeneratorConst:
|
||||
"""Class container for BIP32 SLIP-0010 master key generator constants."""
|
||||
|
||||
# Minimum length in bytes for seed
|
||||
SEED_MIN_BYTE_LEN: int = 16
|
||||
# HMAC keys for different curves
|
||||
HMAC_KEY_ED25519_BYTES: bytes = b"ed25519 seed"
|
||||
HMAC_KEY_NIST256P1_BYTES: bytes = b"Nist256p1 seed"
|
||||
HMAC_KEY_SECP256K1_BYTES: bytes = b"Bitcoin seed"
|
||||
|
||||
|
||||
class _Bip32Slip10MstKeyGenerator:
|
||||
"""
|
||||
BIP32 SLIP-0010 generic master key generator class.
|
||||
It allows master keys generation in according to BIP32 SLIP-0010.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def GenerateFromSeed(seed_bytes: bytes,
|
||||
hmac_key_bytes: bytes,
|
||||
curve_type: EllipticCurveTypes) -> Tuple[bytes, bytes]:
|
||||
"""
|
||||
Generate a master key from the specified seed and return a Bip32Base object.
|
||||
|
||||
Args:
|
||||
seed_bytes (bytes) : Seed bytes
|
||||
hmac_key_bytes (bytes) : HMAC key bytes
|
||||
|
||||
Returns:
|
||||
Bip32Base object: Bip32Base object
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the seed is not suitable for master key generation
|
||||
ValueError: If seed length is not valid
|
||||
"""
|
||||
if len(seed_bytes) < Bip32Slip10MstKeyGeneratorConst.SEED_MIN_BYTE_LEN:
|
||||
raise ValueError(f"Invalid seed length ({len(seed_bytes)})")
|
||||
|
||||
hmac_half_len = HmacSha512.DigestSize() // 2
|
||||
priv_key_cls = EllipticCurveGetter.FromType(curve_type).PrivateKeyClass()
|
||||
|
||||
# Compute HMAC, retry if the resulting private key is not valid
|
||||
hmac = b""
|
||||
hmac_data = seed_bytes
|
||||
success = False
|
||||
|
||||
while not success:
|
||||
hmac = HmacSha512.QuickDigest(hmac_key_bytes, hmac_data)
|
||||
# If private key is not valid, the new HMAC data is the current HMAC
|
||||
success = priv_key_cls.IsValidBytes(hmac[:hmac_half_len])
|
||||
if not success:
|
||||
hmac_data = hmac
|
||||
|
||||
return hmac[:hmac_half_len], hmac[hmac_half_len:]
|
||||
|
||||
|
||||
class Bip32Slip10Ed2519MstKeyGenerator(IBip32MstKeyGenerator):
|
||||
"""
|
||||
BIP32 SLIP-0010 ed25519 master key generator class.
|
||||
It allows master keys generation in according to BIP32 SLIP-0010 for ed25519 curve.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def GenerateFromSeed(cls,
|
||||
seed_bytes: bytes) -> Tuple[bytes, bytes]:
|
||||
"""
|
||||
Generate a master key from the specified seed.
|
||||
|
||||
Args:
|
||||
seed_bytes (bytes): Seed bytes
|
||||
|
||||
Returns:
|
||||
tuple[bytes, bytes]: Private key bytes (index 0) and chain code bytes (index 1)
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the seed is not suitable for master key generation
|
||||
ValueError: If seed length is not valid
|
||||
"""
|
||||
return _Bip32Slip10MstKeyGenerator.GenerateFromSeed(seed_bytes,
|
||||
Bip32Slip10MstKeyGeneratorConst.HMAC_KEY_ED25519_BYTES,
|
||||
EllipticCurveTypes.ED25519)
|
||||
|
||||
|
||||
class Bip32Slip10Nist256p1MstKeyGenerator(IBip32MstKeyGenerator):
|
||||
"""
|
||||
BIP32 SLIP-0010 nist256p1 master key generator class.
|
||||
It allows master keys generation in according to BIP32 SLIP-0010 for nist256p1 curve.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def GenerateFromSeed(cls,
|
||||
seed_bytes: bytes) -> Tuple[bytes, bytes]:
|
||||
"""
|
||||
Generate a master key from the specified seed.
|
||||
|
||||
Args:
|
||||
seed_bytes (bytes): Seed bytes
|
||||
|
||||
Returns:
|
||||
tuple[bytes, bytes]: Private key bytes (index 0) and chain code bytes (index 1)
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the seed is not suitable for master key generation
|
||||
ValueError: If seed length is not valid
|
||||
"""
|
||||
return _Bip32Slip10MstKeyGenerator.GenerateFromSeed(seed_bytes,
|
||||
Bip32Slip10MstKeyGeneratorConst.HMAC_KEY_NIST256P1_BYTES,
|
||||
EllipticCurveTypes.NIST256P1)
|
||||
|
||||
|
||||
class Bip32Slip10Secp256k1MstKeyGenerator(IBip32MstKeyGenerator):
|
||||
"""
|
||||
BIP32 SLIP-0010 secp256k1 master key generator class.
|
||||
It allows master keys generation in according to BIP32 SLIP-0010 for secp256k1 curve.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def GenerateFromSeed(cls,
|
||||
seed_bytes: bytes) -> Tuple[bytes, bytes]:
|
||||
"""
|
||||
Generate a master key from the specified seed.
|
||||
|
||||
Args:
|
||||
seed_bytes (bytes): Seed bytes
|
||||
|
||||
Returns:
|
||||
tuple[bytes, bytes]: Private key bytes (index 0) and chain code bytes (index 1)
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the seed is not suitable for master key generation
|
||||
ValueError: If seed length is not valid
|
||||
"""
|
||||
return _Bip32Slip10MstKeyGenerator.GenerateFromSeed(seed_bytes,
|
||||
Bip32Slip10MstKeyGeneratorConst.HMAC_KEY_SECP256K1_BYTES,
|
||||
EllipticCurveTypes.SECP256K1)
|
||||
@ -0,0 +1,82 @@
|
||||
# Copyright (c) 2021 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""Module for derivation scheme based on secp256k1 curve as defined by BIP32 SLIP-0010."""
|
||||
|
||||
# Imports
|
||||
from typing import Type
|
||||
|
||||
from ..base import Bip32Base, IBip32KeyDerivator, IBip32MstKeyGenerator
|
||||
from ..bip32_const import Bip32Const
|
||||
from ..bip32_key_net_ver import Bip32KeyNetVersions
|
||||
from .bip32_slip10_key_derivator import Bip32Slip10EcdsaDerivator
|
||||
from .bip32_slip10_mst_key_generator import Bip32Slip10Secp256k1MstKeyGenerator
|
||||
from ...ecc import EllipticCurveTypes
|
||||
|
||||
|
||||
class Bip32Slip10Secp256k1(Bip32Base):
|
||||
"""
|
||||
BIP32 SLIP-0010 secp256k1 v.
|
||||
It allows master keys generation and keys derivation using secp256k1 curve.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def CurveType() -> EllipticCurveTypes:
|
||||
"""
|
||||
Return the elliptic curve type.
|
||||
|
||||
Returns:
|
||||
EllipticCurveTypes: Curve type
|
||||
"""
|
||||
return EllipticCurveTypes.SECP256K1
|
||||
|
||||
@staticmethod
|
||||
def _DefaultKeyNetVersion() -> Bip32KeyNetVersions:
|
||||
"""
|
||||
Return the default key net version.
|
||||
|
||||
Returns:
|
||||
Bip32KeyNetVersions object: Bip32KeyNetVersions object
|
||||
"""
|
||||
return Bip32Const.MAIN_NET_KEY_NET_VERSIONS
|
||||
|
||||
@staticmethod
|
||||
def _KeyDerivator() -> Type[IBip32KeyDerivator]:
|
||||
"""
|
||||
Return the key derivator class.
|
||||
|
||||
Returns:
|
||||
IBip32KeyDerivator class: Key derivator class
|
||||
"""
|
||||
return Bip32Slip10EcdsaDerivator
|
||||
|
||||
@staticmethod
|
||||
def _MasterKeyGenerator() -> Type[IBip32MstKeyGenerator]:
|
||||
"""
|
||||
Return the master key generator class.
|
||||
|
||||
Returns:
|
||||
IBip32MstKeyGenerator class: Master key generator class
|
||||
"""
|
||||
return Bip32Slip10Secp256k1MstKeyGenerator
|
||||
|
||||
|
||||
# Deprecated: only for compatibility
|
||||
Bip32Secp256k1 = Bip32Slip10Secp256k1
|
||||
@ -0,0 +1 @@
|
||||
from .bip44 import Bip44
|
||||
Binary file not shown.
Binary file not shown.
@ -0,0 +1,265 @@
|
||||
# Copyright (c) 2021 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""
|
||||
Module for BIP44 keys derivation.
|
||||
Reference: https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
|
||||
"""
|
||||
|
||||
# Imports
|
||||
from typing import Union
|
||||
|
||||
from ..bip32 import Bip32KeyData, Bip32KeyIndex, Bip32KeyNetVersions, Bip32Const, Bip32Slip10Secp256k1
|
||||
from ..bip44_base import Bip44Base, Bip44Changes, Bip44Levels
|
||||
|
||||
_BIP44_BTC_KEY_NET_VER_MAIN: Bip32KeyNetVersions = Bip32Const.MAIN_NET_KEY_NET_VERSIONS
|
||||
|
||||
# from ..conf.bip44 import Bip44ConfGetter
|
||||
# from ..conf.bip44 import BipCoinConf
|
||||
from ..conf.common import BipCoins, BipCoinConf
|
||||
from ..coin_conf import CoinsConf
|
||||
from ..slip.slip44 import Slip44
|
||||
from ..conf.common import DER_PATH_NON_HARDENED_FULL
|
||||
# from bip_utils.ecc import IPrivateKey, IPublicKey
|
||||
|
||||
|
||||
class Bip44Const:
|
||||
"""Class container for BIP44 constants."""
|
||||
|
||||
# Specification name
|
||||
SPEC_NAME: str = "BIP-0044"
|
||||
# Purpose
|
||||
PURPOSE: int = Bip32KeyIndex.HardenIndex(44)
|
||||
|
||||
|
||||
class Bip44(Bip44Base):
|
||||
"""
|
||||
BIP44 class.
|
||||
It allows master key generation and children keys derivation in according to BIP-0044.
|
||||
"""
|
||||
|
||||
#
|
||||
# Class methods for construction
|
||||
#
|
||||
|
||||
@classmethod
|
||||
def FromSeed(cls,
|
||||
seed_bytes: bytes) -> Bip44Base:
|
||||
"""
|
||||
Create a Bip44Base object from the specified seed (e.g. BIP39 seed).
|
||||
|
||||
Args:
|
||||
seed_bytes (bytes) : Seed bytes
|
||||
coin_type (BipCoins): Coin type, shall be a Bip44Coins enum
|
||||
|
||||
Returns:
|
||||
Bip44Base object: Bip44Base object
|
||||
|
||||
Raises:
|
||||
TypeError: If coin type is not a Bip44Coins enum
|
||||
ValueError: If the seed is too short
|
||||
Bip32KeyError: If the seed is not suitable for master key generation
|
||||
"""
|
||||
|
||||
# Bip44ConfGetter already checks the enum type
|
||||
conf = BipCoinConf(
|
||||
coin_names=CoinsConf.Cosmos.CoinNames(),
|
||||
coin_idx=Slip44.ATOM,
|
||||
is_testnet=False,
|
||||
def_path=DER_PATH_NON_HARDENED_FULL,
|
||||
key_net_ver=_BIP44_BTC_KEY_NET_VER_MAIN,
|
||||
wif_net_ver=None,
|
||||
bip32_cls=Bip32Slip10Secp256k1,
|
||||
# addr_cls=AtomAddrEncoder,
|
||||
addr_params={
|
||||
"hrp": CoinsConf.Cosmos.ParamByKey("addr_hrp"),
|
||||
},
|
||||
)
|
||||
return cls._FromSeed(seed_bytes,
|
||||
conf)
|
||||
|
||||
@classmethod
|
||||
def FromExtendedKey(cls,
|
||||
ex_key_str: str) -> Bip44Base:
|
||||
"""
|
||||
Create a Bip44Base object from the specified extended key.
|
||||
|
||||
Args:
|
||||
ex_key_str (str) : Extended key string
|
||||
coin_type (BipCoins): Coin type, shall be a Bip44Coins enum
|
||||
|
||||
Returns:
|
||||
Bip44Base object: Bip44Base object
|
||||
|
||||
Raises:
|
||||
TypeError: If coin type is not a Bip44Coins enum
|
||||
Bip32KeyError: If the extended key is not valid
|
||||
"""
|
||||
|
||||
# Bip44ConfGetter already checks the enum type
|
||||
return cls._FromExtendedKey(ex_key_str, Bip44ConfGetter.GetConfig(coin_type))
|
||||
|
||||
# @classmethod
|
||||
# def FromPrivateKey(cls,
|
||||
# priv_key: Union[bytes, IPrivateKey],
|
||||
# coin_type: BipCoins,
|
||||
# key_data: Bip32KeyData = Bip32KeyData()) -> Bip44Base:
|
||||
# """
|
||||
# Create a Bip44Base object from the specified private key and derivation data.
|
||||
# If only the private key bytes are specified, the key will be considered a master key with
|
||||
# the chain code set to zero, since there is no way to recover the key derivation data.
|
||||
|
||||
# Args:
|
||||
# priv_key (bytes or IPrivateKey) : Private key
|
||||
# coin_type (BipCoins) : Coin type, shall be a Bip44Coins enum
|
||||
# key_data (Bip32KeyData object, optional): Key data (default: all zeros)
|
||||
|
||||
# Returns:
|
||||
# Bip44Base object: Bip44Base object
|
||||
|
||||
# Raises:
|
||||
# TypeError: If coin type is not a Bip44Coins enum
|
||||
# Bip32KeyError: If the key is not valid
|
||||
# """
|
||||
|
||||
# # Bip44ConfGetter already checks the enum type
|
||||
# return cls._FromPrivateKey(priv_key,
|
||||
# Bip44ConfGetter.GetConfig(coin_type),
|
||||
# key_data)
|
||||
|
||||
# @classmethod
|
||||
# def FromPublicKey(cls,
|
||||
# pub_key: Union[bytes, IPublicKey],
|
||||
# coin_type: BipCoins,
|
||||
# key_data: Bip32KeyData = Bip32KeyData(depth=Bip44Levels.ACCOUNT)) -> Bip44Base:
|
||||
# """
|
||||
# Create a Bip44Base object from the specified public key and derivation data.
|
||||
# If only the public key bytes are specified, the key will be considered an account key with
|
||||
# the chain code set to zero, since there is no way to recover the key derivation data.
|
||||
|
||||
# Args:
|
||||
# pub_key (bytes or IPublicKey) : Public key
|
||||
# coin_type (BipCoins) : Coin type, shall be a Bip44Coins enum
|
||||
# key_data (Bip32KeyData object, optional): Key data (default: all zeros with account depth)
|
||||
|
||||
# Returns:
|
||||
# Bip44Base object: Bip44Base object
|
||||
|
||||
# Raises:
|
||||
# TypeError: If coin type is not a Bip44Coins enum
|
||||
# Bip32KeyError: If the key is not valid
|
||||
# """
|
||||
|
||||
# # Bip44ConfGetter already checks the enum type
|
||||
# return cls._FromPublicKey(pub_key,
|
||||
# Bip44ConfGetter.GetConfig(coin_type),
|
||||
# key_data)
|
||||
|
||||
#
|
||||
# Overridden abstract methods
|
||||
#
|
||||
|
||||
def Purpose(self) -> Bip44Base:
|
||||
"""
|
||||
Derive a child key from the purpose and return a new Bip44Base object.
|
||||
|
||||
Returns:
|
||||
Bip44Base object: Bip44Base object
|
||||
|
||||
Raises:
|
||||
Bip44DepthError: If current depth is not suitable for deriving keys
|
||||
Bip32KeyError: If the derivation results in an invalid key
|
||||
"""
|
||||
return self._PurposeGeneric(Bip44Const.PURPOSE)
|
||||
|
||||
def Coin(self) -> Bip44Base:
|
||||
"""
|
||||
Derive a child key from the coin type specified at construction and return a new Bip44Base object.
|
||||
|
||||
Returns:
|
||||
Bip44Base object: Bip44Base object
|
||||
|
||||
Raises:
|
||||
Bip44DepthError: If current depth is not suitable for deriving keys
|
||||
Bip32KeyError: If the derivation results in an invalid key
|
||||
"""
|
||||
return self._CoinGeneric()
|
||||
|
||||
def Account(self,
|
||||
acc_idx: int) -> Bip44Base:
|
||||
"""
|
||||
Derive a child key from the specified account index and return a new Bip44Base object.
|
||||
|
||||
Args:
|
||||
acc_idx (int): Account index
|
||||
|
||||
Returns:
|
||||
Bip44Base object: Bip44Base object
|
||||
|
||||
Raises:
|
||||
Bip44DepthError: If current depth is not suitable for deriving keys
|
||||
Bip32KeyError: If the derivation results in an invalid key
|
||||
"""
|
||||
return self._AccountGeneric(acc_idx)
|
||||
|
||||
def Change(self,
|
||||
change_type: Bip44Changes) -> Bip44Base:
|
||||
"""
|
||||
Derive a child key from the specified change type and return a new Bip44Base object.
|
||||
|
||||
Args:
|
||||
change_type (Bip44Changes): Change type, must a Bip44Changes enum
|
||||
|
||||
Returns:
|
||||
Bip44Base object: Bip44Base object
|
||||
|
||||
Raises:
|
||||
TypeError: If change type is not a Bip44Changes enum
|
||||
Bip44DepthError: If current depth is not suitable for deriving keys
|
||||
Bip32KeyError: If the derivation results in an invalid key
|
||||
"""
|
||||
return self._ChangeGeneric(change_type)
|
||||
|
||||
def AddressIndex(self,
|
||||
addr_idx: int) -> Bip44Base:
|
||||
"""
|
||||
Derive a child key from the specified address index and return a new Bip44Base object.
|
||||
|
||||
Args:
|
||||
addr_idx (int): Address index
|
||||
|
||||
Returns:
|
||||
Bip44Base object: Bip44Base object
|
||||
|
||||
Raises:
|
||||
Bip44DepthError: If current depth is not suitable for deriving keys
|
||||
Bip32KeyError: If the derivation results in an invalid key
|
||||
"""
|
||||
return self._AddressIndexGeneric(addr_idx)
|
||||
|
||||
@staticmethod
|
||||
def SpecName() -> str:
|
||||
"""
|
||||
Get specification name.
|
||||
|
||||
Returns:
|
||||
str: Specification name
|
||||
"""
|
||||
return Bip44Const.SPEC_NAME
|
||||
@ -0,0 +1,3 @@
|
||||
from .bip44_base import Bip44Base, Bip44Changes, Bip44Levels
|
||||
from .bip44_base_ex import Bip44DepthError
|
||||
from .bip44_keys import Bip44PrivateKey, Bip44PublicKey
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,624 @@
|
||||
# Copyright (c) 2021 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
|
||||
"""Module with BIP44 base class."""
|
||||
|
||||
# Imports
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from enum import IntEnum, unique
|
||||
from functools import lru_cache
|
||||
from typing import Union
|
||||
|
||||
from ..bip32 import Bip32Base, Bip32KeyData, Bip32KeyIndex
|
||||
from ..bip44_base.bip44_base_ex import Bip44DepthError
|
||||
from ..bip44_base.bip44_keys import Bip44PrivateKey, Bip44PublicKey
|
||||
from ..conf.common import BipCoinConf, BipCoins
|
||||
from ..ecc import IPrivateKey, IPublicKey
|
||||
|
||||
|
||||
@unique
|
||||
class Bip44Changes(IntEnum):
|
||||
"""Enumerative for BIP44 changes."""
|
||||
|
||||
CHAIN_EXT = 0
|
||||
CHAIN_INT = 1
|
||||
|
||||
|
||||
@unique
|
||||
class Bip44Levels(IntEnum):
|
||||
"""Enumerative for BIP44 levels."""
|
||||
|
||||
MASTER = 0
|
||||
PURPOSE = 1
|
||||
COIN = 2
|
||||
ACCOUNT = 3
|
||||
CHANGE = 4
|
||||
ADDRESS_INDEX = 5
|
||||
|
||||
|
||||
class Bip44Base(ABC):
|
||||
"""
|
||||
BIP44 base class.
|
||||
It allows coin, account, chain and address keys generation in according to BIP44 or its extensions.
|
||||
The class is meant to be derived by classes implementing BIP44 or its extensions.
|
||||
"""
|
||||
|
||||
m_bip32_obj: Bip32Base
|
||||
m_coin_conf: BipCoinConf
|
||||
|
||||
#
|
||||
# Class methods for construction
|
||||
#
|
||||
|
||||
@classmethod
|
||||
def _FromSeed(cls,
|
||||
seed_bytes: bytes,
|
||||
coin_conf: BipCoinConf) -> Bip44Base:
|
||||
"""
|
||||
Create a Bip44Base object from the specified seed (e.g. BIP39 seed).
|
||||
|
||||
Args:
|
||||
seed_bytes (bytes) : Seed bytes
|
||||
coin_conf (BipCoinConf): BipCoinConf object
|
||||
|
||||
Returns:
|
||||
Bip44Base object: Bip44Base object
|
||||
|
||||
Raises:
|
||||
ValueError: If the seed is too short
|
||||
Bip32KeyError: If the seed is not suitable for master key generation
|
||||
"""
|
||||
bip32_cls = coin_conf.Bip32Class()
|
||||
return cls(bip32_cls.FromSeed(seed_bytes,
|
||||
coin_conf.KeyNetVersions()),
|
||||
coin_conf)
|
||||
|
||||
@classmethod
|
||||
def _FromExtendedKey(cls,
|
||||
ex_key_str: str,
|
||||
coin_conf: BipCoinConf) -> Bip44Base:
|
||||
"""
|
||||
Create a Bip44Base object from the specified extended key.
|
||||
|
||||
Args:
|
||||
ex_key_str (str) : Extended key string
|
||||
coin_conf (BipCoinConf): BipCoinConf object
|
||||
|
||||
Returns:
|
||||
Bip44Base object: Bip44Base object
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the extended key is not valid
|
||||
"""
|
||||
bip32_cls = coin_conf.Bip32Class()
|
||||
return cls(bip32_cls.FromExtendedKey(ex_key_str, coin_conf.KeyNetVersions()),
|
||||
coin_conf)
|
||||
|
||||
# @classmethod
|
||||
# def _FromPrivateKey(cls,
|
||||
# priv_key: Union[bytes, IPrivateKey],
|
||||
# coin_conf: BipCoinConf,
|
||||
# key_data: Bip32KeyData) -> Bip44Base:
|
||||
# """
|
||||
# Create a Bip44Base object from the specified private key and derivation data.
|
||||
# If only the private key bytes are specified, the key will be considered a master key with
|
||||
# the chain code set to zero, since there is no way to recover the key derivation data.
|
||||
|
||||
# Args:
|
||||
# priv_key (bytes or IPrivateKey): Private key
|
||||
# coin_conf (BipCoinConf) : BipCoinConf object
|
||||
# key_data (Bip32KeyData object) : Key data
|
||||
|
||||
# Returns:
|
||||
# Bip44Base object: Bip44Base object
|
||||
|
||||
# Raises:
|
||||
# Bip32KeyError: If the key is not valid
|
||||
# """
|
||||
# bip32_cls = coin_conf.Bip32Class()
|
||||
# return cls(bip32_cls.FromPrivateKey(priv_key,
|
||||
# key_data,
|
||||
# coin_conf.KeyNetVersions()),
|
||||
# coin_conf)
|
||||
|
||||
# @classmethod
|
||||
# def _FromPublicKey(cls,
|
||||
# pub_key: Union[bytes, IPublicKey],
|
||||
# coin_conf: BipCoinConf,
|
||||
# key_data: Bip32KeyData) -> Bip44Base:
|
||||
# """
|
||||
# Create a Bip44Base object from the specified public key and derivation data.
|
||||
# If only the public key bytes are specified, the key will be considered an account key with
|
||||
# the chain code set to zero, since there is no way to recover the key derivation data.
|
||||
|
||||
# Args:
|
||||
# pub_key (bytes or IPublicKey) : Public key
|
||||
# coin_conf (BipCoinConf) : BipCoinConf object
|
||||
# key_data (Bip32KeyData object) : Key data
|
||||
|
||||
# Returns:
|
||||
# Bip44Base object: Bip44Base object
|
||||
|
||||
# Raises:
|
||||
# Bip32KeyError: If the key is not valid
|
||||
# """
|
||||
# bip32_cls = coin_conf.Bip32Class()
|
||||
# return cls(bip32_cls.FromPublicKey(pub_key,
|
||||
# key_data,
|
||||
# coin_conf.KeyNetVersions()),
|
||||
# coin_conf)
|
||||
|
||||
#
|
||||
# Public methods
|
||||
#
|
||||
|
||||
def __init__(self,
|
||||
bip32_obj: Bip32Base,
|
||||
coin_conf: BipCoinConf) -> None:
|
||||
"""
|
||||
Construct class.
|
||||
|
||||
Args:
|
||||
bip32_obj (Bip32Base object): Bip32Base object
|
||||
coin_conf (BipCoinConf) : BipCoinConf object
|
||||
|
||||
Returns:
|
||||
Bip44DepthError: If the Bip32 object depth is not valid
|
||||
"""
|
||||
depth = bip32_obj.Depth()
|
||||
|
||||
# If the Bip32 is public-only, the depth shall start from the account level because hardened derivation is
|
||||
# used below it, which is not possible with public keys
|
||||
if bip32_obj.IsPublicOnly():
|
||||
if depth < Bip44Levels.ACCOUNT or depth > Bip44Levels.ADDRESS_INDEX:
|
||||
raise Bip44DepthError(
|
||||
f"Depth of the public-only Bip object ({depth}) is below account level or "
|
||||
f"beyond address index level"
|
||||
)
|
||||
# If the Bip32 object is not public-only, any depth is fine as long as it is not greater
|
||||
# than address index level
|
||||
else:
|
||||
if depth < 0 or depth > Bip44Levels.ADDRESS_INDEX:
|
||||
raise Bip44DepthError(
|
||||
f"Depth of the Bip object ({depth}) is invalid or beyond address index level"
|
||||
)
|
||||
|
||||
# Finally, initialize class
|
||||
self.m_bip32_obj = bip32_obj
|
||||
self.m_coin_conf = coin_conf
|
||||
|
||||
@lru_cache()
|
||||
def PublicKey(self) -> Bip44PublicKey:
|
||||
"""
|
||||
Return the public key.
|
||||
|
||||
Returns:
|
||||
Bip44PublicKey object: Bip44PublicKey object
|
||||
"""
|
||||
return Bip44PublicKey(self.m_bip32_obj.PublicKey(),
|
||||
self.m_coin_conf)
|
||||
|
||||
@lru_cache()
|
||||
def PrivateKey(self) -> Bip44PrivateKey:
|
||||
"""
|
||||
Return the private key.
|
||||
|
||||
Returns:
|
||||
Bip44PrivateKey object: Bip44PrivateKey object
|
||||
|
||||
Raises:
|
||||
Bip32KeyError: If the Bip32 object is public-only
|
||||
"""
|
||||
return Bip44PrivateKey(self.m_bip32_obj.PrivateKey(),
|
||||
self.m_coin_conf)
|
||||
|
||||
def Bip32Object(self) -> Bip32Base:
|
||||
"""
|
||||
Return the BIP32 object.
|
||||
|
||||
Returns:
|
||||
Bip32Base object: Bip32Base object
|
||||
"""
|
||||
return self.m_bip32_obj
|
||||
|
||||
def CoinConf(self) -> BipCoinConf:
|
||||
"""
|
||||
Get coin configuration.
|
||||
|
||||
Returns:
|
||||
BipCoinConf object: BipCoinConf object
|
||||
"""
|
||||
return self.m_coin_conf
|
||||
|
||||
def IsPublicOnly(self) -> bool:
|
||||
"""
|
||||
Get if it's public-only.
|
||||
|
||||
Returns:
|
||||
bool: True if public-only, false otherwise
|
||||
"""
|
||||
return self.m_bip32_obj.IsPublicOnly()
|
||||
|
||||
def Level(self) -> Bip44Levels:
|
||||
"""
|
||||
Return the current level.
|
||||
|
||||
Returns:
|
||||
Bip44Levels: Current level
|
||||
"""
|
||||
return Bip44Levels(self.m_bip32_obj.Depth().ToInt())
|
||||
|
||||
def IsLevel(self,
|
||||
level: Bip44Levels) -> bool:
|
||||
"""
|
||||
Return if the current level is the specified one.
|
||||
|
||||
Args:
|
||||
level (Bip44Levels): Level to be checked
|
||||
|
||||
Returns:
|
||||
bool: True if it's the specified level, false otherwise
|
||||
|
||||
Raises:
|
||||
TypeError: If the level index is not a Bip44Levels enum
|
||||
"""
|
||||
if not isinstance(level, Bip44Levels):
|
||||
raise TypeError("Level is not an enumerative of Bip44Levels")
|
||||
return self.m_bip32_obj.Depth() == level
|
||||
|
||||
def DeriveDefaultPath(self) -> Bip44Base:
|
||||
"""
|
||||
Derive the default coin path and return a new Bip44Base object.
|
||||
|
||||
Returns:
|
||||
Bip44Base object: Bip44Base object
|
||||
|
||||
Raises:
|
||||
Bip44DepthError: If the current depth is not suitable for deriving keys
|
||||
Bip32KeyError: If the derivation results in an invalid key
|
||||
"""
|
||||
# Derive purpose and coin by default
|
||||
bip_obj = self.Purpose().Coin()
|
||||
|
||||
# Derive the remaining path
|
||||
return self.__class__(bip_obj.m_bip32_obj.DerivePath(bip_obj.m_coin_conf.DefaultPath()),
|
||||
bip_obj.m_coin_conf)
|
||||
|
||||
#
|
||||
# Protected class methods
|
||||
#
|
||||
|
||||
def _PurposeGeneric(self,
|
||||
purpose: int) -> Bip44Base:
|
||||
"""
|
||||
Derive a child key from the purpose and return a new Bip44Base object.
|
||||
It shall be called from a child class.
|
||||
|
||||
Args:
|
||||
purpose (int): Purpose
|
||||
|
||||
Returns:
|
||||
Bip44Base object: Bip44Base object
|
||||
|
||||
Raises:
|
||||
Bip44DepthError: If the current depth is not suitable for deriving keys
|
||||
Bip32KeyError: If the derivation results in an invalid key
|
||||
"""
|
||||
if not self.IsLevel(Bip44Levels.MASTER):
|
||||
raise Bip44DepthError(
|
||||
f"Current depth ({self.m_bip32_obj.Depth().ToInt()}) is not suitable for deriving purpose"
|
||||
)
|
||||
|
||||
return self.__class__(self.m_bip32_obj.ChildKey(purpose),
|
||||
self.m_coin_conf)
|
||||
|
||||
def _CoinGeneric(self) -> Bip44Base:
|
||||
"""
|
||||
Derive a child key from the coin type specified at construction and return a new Bip44Base object.
|
||||
It shall be called from a child class.
|
||||
|
||||
Returns:
|
||||
Bip44Base object: Bip44Base object
|
||||
|
||||
Raises:
|
||||
Bip44DepthError: If the current depth is not suitable for deriving keys
|
||||
Bip32KeyError: If the derivation results in an invalid key
|
||||
"""
|
||||
if not self.IsLevel(Bip44Levels.PURPOSE):
|
||||
raise Bip44DepthError(
|
||||
f"Current depth ({self.m_bip32_obj.Depth().ToInt()}) is not suitable for deriving coin"
|
||||
)
|
||||
|
||||
coin_idx = self.m_coin_conf.CoinIndex()
|
||||
|
||||
return self.__class__(self.m_bip32_obj.ChildKey(Bip32KeyIndex.HardenIndex(coin_idx)),
|
||||
self.m_coin_conf)
|
||||
|
||||
def _AccountGeneric(self,
|
||||
acc_idx: int) -> Bip44Base:
|
||||
"""
|
||||
Derive a child key from the specified account index and return a new Bip44Base object.
|
||||
It shall be called from a child class.
|
||||
|
||||
Args:
|
||||
acc_idx (int): Account index
|
||||
|
||||
Returns:
|
||||
Bip44Base object: Bip44Base object
|
||||
|
||||
Raises:
|
||||
Bip44DepthError: If the current depth is not suitable for deriving keys
|
||||
Bip32KeyError: If the derivation results in an invalid key
|
||||
"""
|
||||
if not self.IsLevel(Bip44Levels.COIN):
|
||||
raise Bip44DepthError(
|
||||
f"Current depth ({self.m_bip32_obj.Depth().ToInt()}) is not suitable for deriving account"
|
||||
)
|
||||
|
||||
return self.__class__(self.m_bip32_obj.ChildKey(Bip32KeyIndex.HardenIndex(acc_idx)),
|
||||
self.m_coin_conf)
|
||||
|
||||
def _ChangeGeneric(self,
|
||||
change_type: Bip44Changes) -> Bip44Base:
|
||||
"""
|
||||
Derive a child key from the specified change type and return a new Bip44Base object.
|
||||
It shall be called from a child class.
|
||||
|
||||
Args:
|
||||
change_type (Bip44Changes): Change type, must a Bip44Changes enum
|
||||
|
||||
Returns:
|
||||
Bip44Base object: Bip44Base object
|
||||
|
||||
Raises:
|
||||
TypeError: If change type is not a Bip44Changes enum
|
||||
Bip44DepthError: If the current depth is not suitable for deriving keys
|
||||
Bip32KeyError: If the derivation results in an invalid key
|
||||
"""
|
||||
if not isinstance(change_type, Bip44Changes):
|
||||
raise TypeError("Change index is not an enumerative of Bip44Changes")
|
||||
|
||||
if not self.IsLevel(Bip44Levels.ACCOUNT):
|
||||
raise Bip44DepthError(
|
||||
f"Current depth ({self.m_bip32_obj.Depth().ToInt()}) is not suitable for deriving change"
|
||||
)
|
||||
|
||||
# Use hardened derivation if not-hardended is not supported
|
||||
if not self.m_bip32_obj.IsPublicDerivationSupported():
|
||||
change_idx = Bip32KeyIndex.HardenIndex(int(change_type))
|
||||
else:
|
||||
change_idx = int(change_type)
|
||||
|
||||
return self.__class__(self.m_bip32_obj.ChildKey(change_idx),
|
||||
self.m_coin_conf)
|
||||
|
||||
def _AddressIndexGeneric(self,
|
||||
addr_idx: int) -> Bip44Base:
|
||||
"""
|
||||
Derive a child key from the specified address index and return a new Bip44Base object.
|
||||
It shall be called from a child class.
|
||||
|
||||
Args:
|
||||
addr_idx (int): Address index
|
||||
|
||||
Returns:
|
||||
Bip44Base object: Bip44Base object
|
||||
|
||||
Raises:
|
||||
Bip44DepthError: If the current depth is not suitable for deriving keys
|
||||
Bip32KeyError: If the derivation results in an invalid key
|
||||
"""
|
||||
if not self.IsLevel(Bip44Levels.CHANGE):
|
||||
raise Bip44DepthError(
|
||||
f"Current depth ({self.m_bip32_obj.Depth().ToInt()}) is not suitable for deriving address"
|
||||
)
|
||||
|
||||
# Use hardened derivation if not-hardended is not supported
|
||||
if not self.m_bip32_obj.IsPublicDerivationSupported():
|
||||
addr_idx = Bip32KeyIndex.HardenIndex(addr_idx)
|
||||
|
||||
return self.__class__(self.m_bip32_obj.ChildKey(addr_idx),
|
||||
self.m_coin_conf)
|
||||
|
||||
#
|
||||
# Abstract methods
|
||||
#
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def FromSeed(cls,
|
||||
seed_bytes: bytes,
|
||||
coin_type: BipCoins) -> Bip44Base:
|
||||
"""
|
||||
Create a Bip44Base object from the specified seed (e.g. BIP39 seed).
|
||||
The test net flag is automatically set when the coin is derived. However, if you want to get the correct master
|
||||
or purpose keys, you have to specify here if it's a test net.
|
||||
|
||||
Args:
|
||||
seed_bytes (bytes) : Seed bytes
|
||||
coin_type (BipCoins): Coin type (the type depends on the specific child class)
|
||||
|
||||
Returns:
|
||||
Bip44Base object: Bip44Base object
|
||||
|
||||
Raises:
|
||||
TypeError: If coin type is not of the correct type
|
||||
ValueError: If the seed is too short
|
||||
Bip32KeyError: If the seed is not suitable for master key generation
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def FromExtendedKey(cls,
|
||||
ex_key_str: str,
|
||||
coin_type: BipCoins) -> Bip44Base:
|
||||
"""
|
||||
Create a Bip44Base object from the specified extended key.
|
||||
|
||||
Args:
|
||||
ex_key_str (str) : Extended key string
|
||||
coin_type (BipCoins): Coin type (the type depends on the specific child class)
|
||||
|
||||
Returns:
|
||||
Bip44Base object: Bip44Base object
|
||||
|
||||
Raises:
|
||||
TypeError: If coin type is not of the correct type
|
||||
Bip32KeyError: If the extended key is not valid
|
||||
"""
|
||||
|
||||
# @classmethod
|
||||
# @abstractmethod
|
||||
# def FromPrivateKey(cls,
|
||||
# priv_key: Union[bytes, IPrivateKey],
|
||||
# coin_type: BipCoins,
|
||||
# key_data: Bip32KeyData) -> Bip44Base:
|
||||
# """
|
||||
# Create a Bip44Base object from the specified private key and derivation data.
|
||||
# If only the private key bytes are specified, the key will be considered a master key with
|
||||
# the chain code set to zero, since there is no way to recover the key derivation data.
|
||||
|
||||
# Args:
|
||||
# priv_key (bytes or IPrivateKey): Private key
|
||||
# coin_type (BipCoins) : Coin type, shall be a Bip44Coins enum
|
||||
# key_data (Bip32KeyData object) : Key data
|
||||
|
||||
# Returns:
|
||||
# Bip44Base object: Bip44Base object
|
||||
|
||||
# Raises:
|
||||
# TypeError: If coin type is not a Bip44Coins enum
|
||||
# Bip32KeyError: If the key is not valid
|
||||
# """
|
||||
|
||||
# @classmethod
|
||||
# @abstractmethod
|
||||
# def FromPublicKey(cls,
|
||||
# pub_key: Union[bytes, IPublicKey],
|
||||
# coin_type: BipCoins,
|
||||
# key_data: Bip32KeyData) -> Bip44Base:
|
||||
# """
|
||||
# Create a Bip44Base object from the specified public key and derivation data.
|
||||
# If only the public key bytes are specified, the key will be considered an account key with
|
||||
# the chain code set to zero, since there is no way to recover the key derivation data.
|
||||
|
||||
# Args:
|
||||
# pub_key (bytes or IPublicKey) : Public key
|
||||
# coin_type (BipCoins) : Coin type, shall be a Bip44Coins enum
|
||||
# key_data (Bip32KeyData object): Key data
|
||||
|
||||
# Returns:
|
||||
# Bip44Base object: Bip44Base object
|
||||
|
||||
# Raises:
|
||||
# TypeError: If coin type is not a Bip44Coins enum
|
||||
# Bip32KeyError: If the key is not valid
|
||||
# """
|
||||
|
||||
@abstractmethod
|
||||
def Purpose(self) -> Bip44Base:
|
||||
"""
|
||||
Derive a child key from the purpose and return a new Bip44Base object.
|
||||
|
||||
Returns:
|
||||
Bip44Base object: Bip44Base object
|
||||
|
||||
Raises:
|
||||
Bip44DepthError: If current depth is not suitable for deriving keys
|
||||
Bip32KeyError: If the derivation results in an invalid key
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def Coin(self) -> Bip44Base:
|
||||
"""
|
||||
Derive a child key from the coin type specified at construction and return a new Bip44Base object.
|
||||
|
||||
Returns:
|
||||
Bip44Base object: Bip44Base object
|
||||
|
||||
Raises:
|
||||
Bip44DepthError: If current depth is not suitable for deriving keys
|
||||
Bip32KeyError: If the derivation results in an invalid key
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def Account(self,
|
||||
acc_idx: int) -> Bip44Base:
|
||||
"""
|
||||
Derive a child key from the specified account index and return a new Bip44Base object.
|
||||
|
||||
Args:
|
||||
acc_idx (int): Account index
|
||||
|
||||
Returns:
|
||||
Bip44Base object: Bip44Base object
|
||||
|
||||
Raises:
|
||||
Bip44DepthError: If current depth is not suitable for deriving keys
|
||||
Bip32KeyError: If the derivation results in an invalid key
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def Change(self,
|
||||
change_type: Bip44Changes) -> Bip44Base:
|
||||
"""
|
||||
Derive a child key from the specified change type and return a new Bip44Base object.
|
||||
|
||||
Args:
|
||||
change_type (Bip44Changes): Change type, must a Bip44Changes enum
|
||||
|
||||
Returns:
|
||||
Bip44Base object: Bip44Base object
|
||||
|
||||
Raises:
|
||||
TypeError: If change type is not a Bip44Changes enum
|
||||
Bip44DepthError: If current depth is not suitable for deriving keys
|
||||
Bip32KeyError: If the derivation results in an invalid key
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def AddressIndex(self,
|
||||
addr_idx: int) -> Bip44Base:
|
||||
"""
|
||||
Derive a child key from the specified address index and return a new Bip44Base object.
|
||||
|
||||
Args:
|
||||
addr_idx (int): Address index
|
||||
|
||||
Returns:
|
||||
Bip44Base object: Bip44Base object
|
||||
|
||||
Raises:
|
||||
Bip44DepthError: If current depth is not suitable for deriving keys
|
||||
Bip32KeyError: If the derivation results in an invalid key
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
@abstractmethod
|
||||
def SpecName() -> str:
|
||||
"""
|
||||
Get specification name.
|
||||
|
||||
Returns:
|
||||
str: Specification name
|
||||
"""
|
||||
@ -0,0 +1,25 @@
|
||||
# Copyright (c) 2021 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""Module for BIP44 exceptions."""
|
||||
|
||||
|
||||
class Bip44DepthError(Exception):
|
||||
"""Exception in case of derivation from wrong depth."""
|
||||
@ -0,0 +1,225 @@
|
||||
# Copyright (c) 2021 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""Module for BIP44 keys handling."""
|
||||
|
||||
# Imports
|
||||
from functools import lru_cache
|
||||
|
||||
# from ..addr import AdaShelleyAddrEncoder, XmrAddrEncoder
|
||||
from ..bip32 import Bip32ChainCode, Bip32PrivateKey, Bip32PublicKey
|
||||
from ..conf.common import BipCoinConf
|
||||
from ..utils.misc import DataBytes
|
||||
from ..wif import WifEncoder, WifPubKeyModes
|
||||
|
||||
|
||||
class Bip44PublicKey:
|
||||
"""
|
||||
BIP44 public key class.
|
||||
It contains Bip32PublicKey and add the possibility to compute the address from the coin type.
|
||||
"""
|
||||
|
||||
m_pub_key: Bip32PublicKey
|
||||
m_coin_conf: BipCoinConf
|
||||
|
||||
def __init__(self,
|
||||
pub_key: Bip32PublicKey,
|
||||
coin_conf: BipCoinConf) -> None:
|
||||
"""
|
||||
Construct class.
|
||||
|
||||
Args:
|
||||
pub_key (Bip32PublicKey object): Bip32PublicKey object
|
||||
coin_conf (BipCoinConf object) : BipCoinConf object
|
||||
|
||||
Raises:
|
||||
ValueError: If the key elliptic curve is different from the coin configuration one
|
||||
"""
|
||||
if pub_key.CurveType() != coin_conf.Bip32Class().CurveType():
|
||||
raise ValueError(
|
||||
f"The public key elliptic curve ({pub_key.CurveType()}) shall match "
|
||||
f"the coin configuration one ({coin_conf.Bip32Class().CurveType()})"
|
||||
)
|
||||
|
||||
self.m_pub_key = pub_key
|
||||
self.m_coin_conf = coin_conf
|
||||
|
||||
def Bip32Key(self) -> Bip32PublicKey:
|
||||
"""
|
||||
Return the BIP32 key object.
|
||||
|
||||
Returns:
|
||||
Bip32PublicKey object: BIP32 key object
|
||||
"""
|
||||
return self.m_pub_key
|
||||
|
||||
def ToExtended(self) -> str:
|
||||
"""
|
||||
Return key in serialized extended format.
|
||||
|
||||
Returns:
|
||||
str: Key in serialized extended format
|
||||
"""
|
||||
return self.m_pub_key.ToExtended()
|
||||
|
||||
def ChainCode(self) -> Bip32ChainCode:
|
||||
"""
|
||||
Return the chain code.
|
||||
|
||||
Returns:
|
||||
Bip32ChainCode object: Bip32ChainCode object
|
||||
"""
|
||||
return self.m_pub_key.ChainCode()
|
||||
|
||||
def RawCompressed(self) -> DataBytes:
|
||||
"""
|
||||
Return raw compressed public key.
|
||||
|
||||
Returns:
|
||||
DataBytes object: DataBytes object
|
||||
"""
|
||||
return self.m_pub_key.RawCompressed()
|
||||
|
||||
def RawUncompressed(self) -> DataBytes:
|
||||
"""
|
||||
Return raw uncompressed public key.
|
||||
|
||||
Returns:
|
||||
DataBytes object: DataBytes object
|
||||
"""
|
||||
return self.m_pub_key.RawUncompressed()
|
||||
|
||||
@lru_cache()
|
||||
def ToAddress(self) -> str:
|
||||
"""
|
||||
Return the address correspondent to the public key.
|
||||
|
||||
Returns:
|
||||
str: Address string
|
||||
"""
|
||||
addr_cls = self.m_coin_conf.AddrClass()
|
||||
pub_key_obj = self.m_pub_key.KeyObject()
|
||||
|
||||
# # Exception for Cardano
|
||||
# if addr_cls is AdaShelleyAddrEncoder:
|
||||
# raise ValueError("Use the CardanoShelley class to get Cardano Shelley addresses")
|
||||
# # Exception for Monero
|
||||
# if addr_cls is XmrAddrEncoder:
|
||||
# raise ValueError("Use the Monero class to get Monero addresses")
|
||||
|
||||
return addr_cls.EncodeKey(pub_key_obj,
|
||||
**self.m_coin_conf.AddrParamsWithResolvedCalls(self.m_pub_key))
|
||||
|
||||
|
||||
class Bip44PrivateKey:
|
||||
"""
|
||||
BIP44 private key class.
|
||||
It contains Bip32PrivateKey and add the possibility to compute the WIF from the coin type.
|
||||
"""
|
||||
|
||||
m_priv_key: Bip32PrivateKey
|
||||
m_coin_conf: BipCoinConf
|
||||
|
||||
def __init__(self,
|
||||
priv_key: Bip32PrivateKey,
|
||||
coin_conf: BipCoinConf) -> None:
|
||||
"""
|
||||
Construct class.
|
||||
|
||||
Args:
|
||||
priv_key (Bip32PrivateKey object): Bip32PrivateKey object
|
||||
coin_conf (BipCoinConf object) : BipCoinConf object
|
||||
|
||||
Raises:
|
||||
ValueError: If the key elliptic curve is different from the coin configuration one
|
||||
"""
|
||||
if priv_key.CurveType() != coin_conf.Bip32Class().CurveType():
|
||||
raise ValueError(
|
||||
f"The private key elliptic curve ({priv_key.CurveType()}) shall match "
|
||||
f"the coin configuration one ({coin_conf.Bip32Class().CurveType()})"
|
||||
)
|
||||
|
||||
self.m_priv_key = priv_key
|
||||
self.m_coin_conf = coin_conf
|
||||
|
||||
def Bip32Key(self) -> Bip32PrivateKey:
|
||||
"""
|
||||
Return the BIP32 key object.
|
||||
|
||||
Returns:
|
||||
Bip32PublicKey object: BIP32 key object
|
||||
"""
|
||||
return self.m_priv_key
|
||||
|
||||
def ToExtended(self) -> str:
|
||||
"""
|
||||
Return key in serialized extended format.
|
||||
|
||||
Returns:
|
||||
str: Key in serialized extended format
|
||||
"""
|
||||
return self.m_priv_key.ToExtended()
|
||||
|
||||
def ChainCode(self) -> Bip32ChainCode:
|
||||
"""
|
||||
Return the chain code.
|
||||
|
||||
Returns:
|
||||
Bip32ChainCode object: Bip32ChainCode object
|
||||
"""
|
||||
return self.m_priv_key.ChainCode()
|
||||
|
||||
def Raw(self) -> DataBytes:
|
||||
"""
|
||||
Return raw compressed public key.
|
||||
|
||||
Returns:
|
||||
DataBytes object: DataBytes object
|
||||
"""
|
||||
return self.m_priv_key.Raw()
|
||||
|
||||
@lru_cache()
|
||||
def PublicKey(self) -> Bip44PublicKey:
|
||||
"""
|
||||
Get the public key correspondent to the private one.
|
||||
|
||||
Returns:
|
||||
Bip44PublicKey object: Bip44PublicKey object
|
||||
"""
|
||||
return Bip44PublicKey(self.m_priv_key.PublicKey(),
|
||||
self.m_coin_conf)
|
||||
|
||||
@lru_cache()
|
||||
def ToWif(self,
|
||||
pub_key_mode: WifPubKeyModes = WifPubKeyModes.COMPRESSED) -> str:
|
||||
"""
|
||||
Return key in WIF format.
|
||||
|
||||
Args:
|
||||
pub_key_mode (WifPubKeyModes): Specify if the private key corresponds to a compressed public key
|
||||
|
||||
Returns:
|
||||
str: Key in WIF format
|
||||
"""
|
||||
wif_net_ver = self.m_coin_conf.WifNetVersion()
|
||||
|
||||
return (WifEncoder.Encode(self.m_priv_key.Raw().ToBytes(), wif_net_ver, pub_key_mode)
|
||||
if wif_net_ver is not None
|
||||
else "")
|
||||
@ -0,0 +1,2 @@
|
||||
from .coin_conf import CoinConf
|
||||
from .coins_conf import CoinsConf
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,68 @@
|
||||
# Copyright (c) 2021 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""Module with helper class for generic coins configuration handling."""
|
||||
|
||||
# Imports
|
||||
from typing import Any, Dict
|
||||
|
||||
from ..utils.conf import CoinNames as ConfCoinNames
|
||||
|
||||
|
||||
class CoinConf:
|
||||
"""Coin configuration class."""
|
||||
|
||||
m_coin_name: ConfCoinNames
|
||||
m_params: Dict[str, Any]
|
||||
|
||||
def __init__(self,
|
||||
coin_name: ConfCoinNames,
|
||||
params: Dict[str, Any]) -> None:
|
||||
"""
|
||||
Construct class.
|
||||
|
||||
Args:
|
||||
coin_name (CoinNames object): Coin names
|
||||
params (dict) : SS58 format
|
||||
"""
|
||||
self.m_coin_name = coin_name
|
||||
self.m_params = params
|
||||
|
||||
def CoinNames(self) -> ConfCoinNames:
|
||||
"""
|
||||
Get coin names.
|
||||
|
||||
Returns:
|
||||
CoinNames object: CoinNames object
|
||||
"""
|
||||
return self.m_coin_name
|
||||
|
||||
def ParamByKey(self,
|
||||
key: str) -> Any:
|
||||
"""
|
||||
Get the parameter by key.
|
||||
|
||||
Args:
|
||||
key (str): Parameter key
|
||||
|
||||
Returns:
|
||||
Any: Parameter value
|
||||
"""
|
||||
return self.m_params[key]
|
||||
@ -0,0 +1,890 @@
|
||||
# Copyright (c) 2021 Emanuele Bellocchia
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
"""Module with generic coins configuration for all other modules."""
|
||||
|
||||
# Imports
|
||||
from .coin_conf import CoinConf
|
||||
from ..slip.slip173 import Slip173
|
||||
from ..utils.conf import CoinNames
|
||||
|
||||
|
||||
# Bitcoin constants used by different coins
|
||||
# Main net
|
||||
_BTC_P2PKH_NET_VER_MN: bytes = b"\x00"
|
||||
_BTC_P2SH_NET_VER_MN: bytes = b"\x05"
|
||||
_BTC_P2WPKH_HRP_MN: str = Slip173.BITCOIN_MAINNET
|
||||
_BTC_P2WPKH_WIT_VER_MN: int = 0
|
||||
_BTC_P2TR_HRP_MN: str = Slip173.BITCOIN_MAINNET
|
||||
_BTC_P2TR_WIT_VER_MN: int = 1
|
||||
_BTC_WIF_NET_VER_MN: bytes = b"\x80"
|
||||
# Test net
|
||||
_BTC_P2PKH_NET_VER_TN: bytes = b"\x6f"
|
||||
_BTC_P2SH_NET_VER_TN: bytes = b"\xc4"
|
||||
_BTC_P2WPKH_HRP_TN: str = Slip173.BITCOIN_TESTNET
|
||||
_BTC_P2WPKH_WIT_VER_TN: int = 0
|
||||
_BTC_P2TR_HRP_TN: str = Slip173.BITCOIN_TESTNET
|
||||
_BTC_P2TR_WIT_VER_TN: int = 1
|
||||
_BTC_WIF_NET_VER_TN: bytes = b"\xef"
|
||||
# Regtest
|
||||
_BTC_P2PKH_NET_VER_RT: bytes = _BTC_P2PKH_NET_VER_TN
|
||||
_BTC_P2SH_NET_VER_RT: bytes = _BTC_P2SH_NET_VER_TN
|
||||
_BTC_P2WPKH_HRP_RT: str = Slip173.BITCOIN_REGTEST
|
||||
_BTC_P2WPKH_WIT_VER_RT: int = _BTC_P2TR_WIT_VER_TN
|
||||
_BTC_P2TR_HRP_RT: str = Slip173.BITCOIN_REGTEST
|
||||
_BTC_P2TR_WIT_VER_RT: int = _BTC_P2TR_WIT_VER_TN
|
||||
_BTC_WIF_NET_VER_RT: bytes = _BTC_WIF_NET_VER_TN
|
||||
|
||||
|
||||
class CoinsConf:
|
||||
"""Class container for coins configuration."""
|
||||
|
||||
# # Configuration for Acala
|
||||
# Acala: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Acala", "ACA"),
|
||||
# params={
|
||||
# "addr_ss58_format": 10,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Akash Network
|
||||
# AkashNetwork: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Akash Network", "AKT"),
|
||||
# params={
|
||||
# "addr_hrp": Slip173.AKASH_NETWORK,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Algorand
|
||||
# Algorand: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Algorand", "ALGO"),
|
||||
# params={},
|
||||
# )
|
||||
|
||||
# # Configuration for Aptos
|
||||
# Aptos: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Aptos", "APTOS"),
|
||||
# params={
|
||||
# "addr_prefix": "0x",
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Arbitrum
|
||||
# Arbitrum: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Arbitrum", "ARB"),
|
||||
# params={},
|
||||
# )
|
||||
|
||||
# # Configuration for Avax C-Chain
|
||||
# AvaxCChain: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Avax C-Chain", "AVAX"),
|
||||
# params={},
|
||||
# )
|
||||
|
||||
# # Configuration for Avax P-Chain
|
||||
# AvaxPChain: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Avax P-Chain", "AVAX"),
|
||||
# params={
|
||||
# "addr_hrp": "avax",
|
||||
# "addr_prefix": "P-",
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Avax X-Chain
|
||||
# AvaxXChain: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Avax X-Chain", "AVAX"),
|
||||
# params={
|
||||
# "addr_hrp": "avax",
|
||||
# "addr_prefix": "X-",
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Axelar
|
||||
# Axelar: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Axelar", "AXL"),
|
||||
# params={
|
||||
# "addr_hrp": Slip173.AXELAR,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Band Protocol
|
||||
# BandProtocol: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Band Protocol", "BAND"),
|
||||
# params={
|
||||
# "addr_hrp": Slip173.BAND_PROTOCOL,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Bifrost
|
||||
# Bifrost: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Bifrost", "BNC"),
|
||||
# params={
|
||||
# "addr_ss58_format": 6,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Binance Chain
|
||||
# BinanceChain: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Binance Chain", "BNB"),
|
||||
# params={
|
||||
# "addr_hrp": Slip173.BINANCE_CHAIN,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Binance Smart Chain
|
||||
# BinanceSmartChain: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Binance Smart Chain", "BNB"),
|
||||
# params={},
|
||||
# )
|
||||
|
||||
# Configuration for Bitcoin main net
|
||||
BitcoinMainNet: CoinConf = CoinConf(
|
||||
coin_name=CoinNames("Bitcoin", "BTC"),
|
||||
params={
|
||||
"p2pkh_net_ver": _BTC_P2PKH_NET_VER_MN,
|
||||
"p2sh_net_ver": _BTC_P2SH_NET_VER_MN,
|
||||
"p2wpkh_hrp": _BTC_P2WPKH_HRP_MN,
|
||||
"p2wpkh_wit_ver": _BTC_P2WPKH_WIT_VER_MN,
|
||||
"p2tr_hrp": _BTC_P2TR_HRP_MN,
|
||||
"p2tr_wit_ver": _BTC_P2TR_WIT_VER_MN,
|
||||
"wif_net_ver": _BTC_WIF_NET_VER_MN,
|
||||
},
|
||||
)
|
||||
|
||||
# # Configuration for Bitcoin test net
|
||||
# BitcoinTestNet: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Bitcoin TestNet", "BTC"),
|
||||
# params={
|
||||
# "p2pkh_net_ver": _BTC_P2PKH_NET_VER_TN,
|
||||
# "p2sh_net_ver": _BTC_P2SH_NET_VER_TN,
|
||||
# "p2wpkh_hrp": _BTC_P2WPKH_HRP_TN,
|
||||
# "p2wpkh_wit_ver": _BTC_P2WPKH_WIT_VER_TN,
|
||||
# "p2tr_hrp": _BTC_P2TR_HRP_TN,
|
||||
# "p2tr_wit_ver": _BTC_P2TR_WIT_VER_TN,
|
||||
# "wif_net_ver": _BTC_WIF_NET_VER_TN,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Bitcoin regtest
|
||||
# BitcoinRegTest: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Bitcoin RegTest", "BTC"),
|
||||
# params={
|
||||
# "p2pkh_net_ver": _BTC_P2PKH_NET_VER_RT,
|
||||
# "p2sh_net_ver": _BTC_P2SH_NET_VER_RT,
|
||||
# "p2wpkh_hrp": _BTC_P2WPKH_HRP_RT,
|
||||
# "p2wpkh_wit_ver": _BTC_P2WPKH_WIT_VER_RT,
|
||||
# "p2tr_hrp": _BTC_P2TR_HRP_RT,
|
||||
# "p2tr_wit_ver": _BTC_P2TR_WIT_VER_RT,
|
||||
# "wif_net_ver": _BTC_WIF_NET_VER_RT,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Bitcoin Cash main net
|
||||
# BitcoinCashMainNet: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Bitcoin Cash", "BCH"),
|
||||
# params={
|
||||
# "p2pkh_std_hrp": "bitcoincash",
|
||||
# "p2pkh_std_net_ver": _BTC_P2PKH_NET_VER_MN,
|
||||
# "p2pkh_legacy_net_ver": _BTC_P2PKH_NET_VER_MN,
|
||||
# "p2sh_std_hrp": "bitcoincash",
|
||||
# "p2sh_std_net_ver": b"\x08",
|
||||
# "p2sh_legacy_net_ver": _BTC_P2SH_NET_VER_MN,
|
||||
# "wif_net_ver": _BTC_WIF_NET_VER_MN,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Bitcoin Cash test net
|
||||
# BitcoinCashTestNet: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Bitcoin Cash TestNet", "BCH"),
|
||||
# params={
|
||||
# "p2pkh_std_hrp": "bchtest",
|
||||
# "p2pkh_std_net_ver": b"\x00",
|
||||
# "p2pkh_legacy_net_ver": _BTC_P2PKH_NET_VER_TN,
|
||||
# "p2sh_std_hrp": "bchtest",
|
||||
# "p2sh_std_net_ver": b"\x08",
|
||||
# "p2sh_legacy_net_ver": _BTC_P2SH_NET_VER_TN,
|
||||
# "wif_net_ver": _BTC_WIF_NET_VER_TN,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Bitcoin Cash Simple Ledger Protocol main net
|
||||
# BitcoinCashSlpMainNet: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Bitcoin Cash SLP", "SLP"),
|
||||
# params={
|
||||
# "p2pkh_std_hrp": "simpleledger",
|
||||
# "p2pkh_std_net_ver": b"\x00",
|
||||
# "p2pkh_legacy_net_ver": _BTC_P2PKH_NET_VER_MN,
|
||||
# "p2sh_std_hrp": "simpleledger",
|
||||
# "p2sh_std_net_ver": b"\x08",
|
||||
# "p2sh_legacy_net_ver": _BTC_P2SH_NET_VER_MN,
|
||||
# "wif_net_ver": _BTC_WIF_NET_VER_MN,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Bitcoin Cash Simple Ledger Protocol test net
|
||||
# BitcoinCashSlpTestNet: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Bitcoin Cash SLP TestNet", "SLP"),
|
||||
# params={
|
||||
# "p2pkh_std_hrp": "slptest",
|
||||
# "p2pkh_std_net_ver": b"\x00",
|
||||
# "p2pkh_legacy_net_ver": _BTC_P2PKH_NET_VER_TN,
|
||||
# "p2sh_std_hrp": "slptest",
|
||||
# "p2sh_std_net_ver": b"\x08",
|
||||
# "p2sh_legacy_net_ver": _BTC_P2SH_NET_VER_TN,
|
||||
# "wif_net_ver": _BTC_WIF_NET_VER_TN,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Bitcoin SV main net
|
||||
# BitcoinSvMainNet: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("BitcoinSV", "BSV"),
|
||||
# params={
|
||||
# "p2pkh_net_ver": _BTC_P2PKH_NET_VER_MN,
|
||||
# "p2sh_net_ver": _BTC_P2SH_NET_VER_MN,
|
||||
# "wif_net_ver": _BTC_WIF_NET_VER_MN,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Bitcoin SV test net
|
||||
# BitcoinSvTestNet: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("BitcoinSV TestNet", "BSV"),
|
||||
# params={
|
||||
# "p2pkh_net_ver": _BTC_P2PKH_NET_VER_TN,
|
||||
# "p2sh_net_ver": _BTC_P2SH_NET_VER_TN,
|
||||
# "wif_net_ver": _BTC_WIF_NET_VER_TN,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Cardano main net
|
||||
# CardanoMainNet: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Cardano", "ADA"),
|
||||
# params={
|
||||
# "addr_hrp": "addr",
|
||||
# "staking_addr_hrp": "stake",
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Cardano test
|
||||
# CardanoTestNet: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Cardano TestNet", "ADA"),
|
||||
# params={
|
||||
# "addr_hrp": "addr_test",
|
||||
# "staking_addr_hrp": "stake_test",
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Celestia
|
||||
# Celestia: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Celestia", "TIA"),
|
||||
# params={
|
||||
# "addr_hrp": Slip173.CELESTIA,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Celo
|
||||
# Celo: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Celo", "CELO"),
|
||||
# params={},
|
||||
# )
|
||||
|
||||
# # Configuration for Certik
|
||||
# Certik: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Certik", "CTK"),
|
||||
# params={
|
||||
# "addr_hrp": Slip173.CERTIK,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for ChainX
|
||||
# ChainX: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("ChainX", "PCX"),
|
||||
# params={
|
||||
# "addr_ss58_format": 44,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Chihuahua
|
||||
# Chihuahua: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Chihuahua", "HUAHUA"),
|
||||
# params={
|
||||
# "addr_hrp": Slip173.CHIHUAHUA,
|
||||
# },
|
||||
# )
|
||||
|
||||
# Configuration for Cosmos
|
||||
Cosmos: CoinConf = CoinConf(
|
||||
coin_name=CoinNames("Cosmos", "ATOM"),
|
||||
params={
|
||||
"addr_hrp": Slip173.COSMOS,
|
||||
},
|
||||
)
|
||||
|
||||
# # Configuration for Dash main net
|
||||
# DashMainNet: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Dash", "DASH"),
|
||||
# params={
|
||||
# "p2pkh_net_ver": b"\x4c",
|
||||
# "p2sh_net_ver": b"\x10",
|
||||
# "wif_net_ver": b"\xcc",
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Dash test net
|
||||
# DashTestNet: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Dash TestNet", "DASH"),
|
||||
# params={
|
||||
# "p2pkh_net_ver": b"\x8c",
|
||||
# "p2sh_net_ver": b"\x13",
|
||||
# "wif_net_ver": _BTC_WIF_NET_VER_TN,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Dogecoin main net
|
||||
# DogecoinMainNet: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Dogecoin", "DOGE"),
|
||||
# params={
|
||||
# "p2pkh_net_ver": b"\x1e",
|
||||
# "p2sh_net_ver": b"\x16",
|
||||
# "wif_net_ver": b"\x9e",
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Dogecoin test net
|
||||
# DogecoinTestNet: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Dogecoin TestNet", "DOGE"),
|
||||
# params={
|
||||
# "p2pkh_net_ver": b"\x71",
|
||||
# "p2sh_net_ver": _BTC_P2SH_NET_VER_TN,
|
||||
# "wif_net_ver": b"\xf1",
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for dYdX
|
||||
# DYDX: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("dYdX", "DYDX"),
|
||||
# params={
|
||||
# "addr_hrp": Slip173.DYDX,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for eCash main net
|
||||
# EcashMainNet: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("eCash", "XEC"),
|
||||
# params={
|
||||
# "p2pkh_std_hrp": "ecash",
|
||||
# "p2pkh_std_net_ver": b"\x00",
|
||||
# "p2pkh_legacy_net_ver": _BTC_P2PKH_NET_VER_MN,
|
||||
# "p2sh_std_hrp": "ecash",
|
||||
# "p2sh_std_net_ver": b"\x08",
|
||||
# "p2sh_legacy_net_ver": _BTC_P2SH_NET_VER_MN,
|
||||
# "wif_net_ver": _BTC_WIF_NET_VER_MN,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for eCash test net
|
||||
# EcashTestNet: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("eCash TestNet", "XEC"),
|
||||
# params={
|
||||
# "p2pkh_std_hrp": "ectest",
|
||||
# "p2pkh_std_net_ver": b"\x00",
|
||||
# "p2pkh_legacy_net_ver": _BTC_P2PKH_NET_VER_TN,
|
||||
# "p2sh_std_hrp": "ectest",
|
||||
# "p2sh_std_net_ver": b"\x08",
|
||||
# "p2sh_legacy_net_ver": _BTC_P2SH_NET_VER_TN,
|
||||
# "wif_net_ver": _BTC_WIF_NET_VER_TN,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Edgeware
|
||||
# Edgeware: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Edgeware", "EDG"),
|
||||
# params={
|
||||
# "addr_ss58_format": 7,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Elrond
|
||||
# Elrond: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("MultiversX", "EGLD"),
|
||||
# params={
|
||||
# "addr_hrp": Slip173.ELROND,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Eos
|
||||
# Eos: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("EOS", "EOS"),
|
||||
# params={
|
||||
# "addr_prefix": "EOS",
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Ergo main net
|
||||
# ErgoMainNet: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Ergo", "ERGO"),
|
||||
# params={},
|
||||
# )
|
||||
|
||||
# # Configuration for Ergo test net
|
||||
# ErgoTestNet: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Ergo TestNet", "ERGO"),
|
||||
# params={},
|
||||
# )
|
||||
|
||||
# # Configuration for Ethereum
|
||||
# Ethereum: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Ethereum", "ETH"),
|
||||
# params={
|
||||
# "addr_prefix": "0x",
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Ethereum Classic
|
||||
# EthereumClassic: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Ethereum Classic", "ETC"),
|
||||
# params={},
|
||||
# )
|
||||
|
||||
# # Configuration for Fantom Opera
|
||||
# FantomOpera: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Fantom Opera", "FTM"),
|
||||
# params={},
|
||||
# )
|
||||
|
||||
# # Configuration for Fetch.ai
|
||||
# FetchAi: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Fetch.ai", "FET"),
|
||||
# params={
|
||||
# "addr_hrp": Slip173.FETCH_AI,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Filecoin
|
||||
# Filecoin: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Filecoin", "FIL"),
|
||||
# params={
|
||||
# "addr_prefix": "f",
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for generic Substrate coin
|
||||
# GenericSubstrate: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Generic Substrate", ""),
|
||||
# params={
|
||||
# "addr_ss58_format": 42,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Harmony One
|
||||
# HarmonyOne: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Harmony One", "ONE"),
|
||||
# params={
|
||||
# "addr_hrp": Slip173.HARMONY_ONE,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Huobi Chain
|
||||
# HuobiChain: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Huobi Token", "HT"),
|
||||
# params={},
|
||||
# )
|
||||
|
||||
# # Configuration for Icon
|
||||
# Icon: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Icon", "ICX"),
|
||||
# params={
|
||||
# "addr_prefix": "hx",
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Injective
|
||||
# Injective: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Injective", "INJ"),
|
||||
# params={
|
||||
# "addr_hrp": Slip173.INJECTIVE,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for IRISnet
|
||||
# IrisNet: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("IRIS Network", "IRIS"),
|
||||
# params={
|
||||
# "addr_hrp": Slip173.IRIS_NETWORK,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Karura
|
||||
# Karura: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Karura", "KAR"),
|
||||
# params={
|
||||
# "addr_ss58_format": 8,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Kava
|
||||
# Kava: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Kava", "KAVA"),
|
||||
# params={
|
||||
# "addr_hrp": Slip173.KAVA,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Kusama
|
||||
# Kusama: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Kusama", "KSM"),
|
||||
# params={
|
||||
# "addr_ss58_format": 2,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Litecoin main net
|
||||
# LitecoinMainNet: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Litecoin", "LTC"),
|
||||
# params={
|
||||
# "p2pkh_std_net_ver": b"\x30",
|
||||
# "p2pkh_depr_net_ver": _BTC_P2PKH_NET_VER_MN,
|
||||
# "p2sh_std_net_ver": b"\x32",
|
||||
# "p2sh_depr_net_ver": _BTC_P2SH_NET_VER_MN,
|
||||
# "p2wpkh_hrp": Slip173.LITECOIN_MAINNET,
|
||||
# "p2wpkh_wit_ver": _BTC_P2WPKH_WIT_VER_MN,
|
||||
# "wif_net_ver": b"\xb0",
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Litecoin test net
|
||||
# LitecoinTestNet: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Litecoin TestNet", "LTC"),
|
||||
# params={
|
||||
# "p2pkh_std_net_ver": b"\x6f",
|
||||
# "p2pkh_depr_net_ver": _BTC_P2PKH_NET_VER_TN,
|
||||
# "p2sh_std_net_ver": b"\x3a",
|
||||
# "p2sh_depr_net_ver": _BTC_P2SH_NET_VER_TN,
|
||||
# "p2wpkh_hrp": Slip173.LITECOIN_TESTNET,
|
||||
# "p2wpkh_wit_ver": _BTC_P2WPKH_WIT_VER_TN,
|
||||
# "wif_net_ver": _BTC_WIF_NET_VER_TN,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Metis
|
||||
# Metis: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Metis", "METIS"),
|
||||
# params={},
|
||||
# )
|
||||
|
||||
# # Configuration for Monero main net
|
||||
# MoneroMainNet: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Monero", "XMR"),
|
||||
# params={
|
||||
# "addr_net_ver": b"\x12",
|
||||
# "addr_int_net_ver": b"\x13",
|
||||
# "subaddr_net_ver": b"\x2a",
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Monero stage net
|
||||
# MoneroStageNet: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Monero StageNet", "XMR"),
|
||||
# params={
|
||||
# "addr_net_ver": b"\x18",
|
||||
# "addr_int_net_ver": b"\x19",
|
||||
# "subaddr_net_ver": b"\x24",
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Monero test net
|
||||
# MoneroTestNet: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Monero TestNet", "XMR"),
|
||||
# params={
|
||||
# "addr_net_ver": b"\x35",
|
||||
# "addr_int_net_ver": b"\x36",
|
||||
# "subaddr_net_ver": b"\x3f",
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Moonbeam
|
||||
# Moonbeam: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Moonbeam", "GLMR"),
|
||||
# params={
|
||||
# "addr_ss58_format": 1284,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Moonriver
|
||||
# Moonriver: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Moonriver", "MOVR"),
|
||||
# params={
|
||||
# "addr_ss58_format": 1285,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Nano
|
||||
# Nano: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Nano", "NANO"),
|
||||
# params={
|
||||
# "addr_prefix": "nano_",
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Near Protocol
|
||||
# NearProtocol: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Near Protocol", "NEAR"),
|
||||
# params={},
|
||||
# )
|
||||
|
||||
# # For compatibility, later assigned to NeoLegacy
|
||||
# Neo: CoinConf
|
||||
|
||||
# # Configuration for Neo legacy
|
||||
# NeoLegacy: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("NEO", "NEO"),
|
||||
# params={
|
||||
# "addr_ver": b"\x17",
|
||||
# "addr_prefix": b"\x21",
|
||||
# "addr_suffix": b"\xac",
|
||||
# "wif_net_ver": _BTC_WIF_NET_VER_MN,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Neo N3
|
||||
# NeoN3: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("NEO", "NEO"),
|
||||
# params={
|
||||
# "addr_ver": b"\x35",
|
||||
# "addr_prefix": b"\x0c\x21",
|
||||
# "addr_suffix": b"\x41\x56\xe7\xb3\x27",
|
||||
# "wif_net_ver": _BTC_WIF_NET_VER_MN,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Neutron
|
||||
# Neutron: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Neutron", "NTRN"),
|
||||
# params={
|
||||
# "addr_hrp": Slip173.NEUTRON,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Nimiq
|
||||
# Nimiq: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Nimiq", "NIM"),
|
||||
# params={
|
||||
# "addr_prefix": "NQ"
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Nine Chronicles Gold
|
||||
# NineChroniclesGold: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("NineChroniclesGold", "NCG"),
|
||||
# params={},
|
||||
# )
|
||||
|
||||
# # Configuration for OKEx Chain
|
||||
# OkexChain: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("OKExChain", "OKT"),
|
||||
# params={
|
||||
# "addr_hrp": Slip173.OKEX_CHAIN,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Ontology
|
||||
# Ontology: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Ontology", "ONT"),
|
||||
# params={
|
||||
# "addr_ver": b"\x17",
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Optimism
|
||||
# Optimism: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Optimism", "OP"),
|
||||
# params={},
|
||||
# )
|
||||
|
||||
# # Configuration for Osmosis
|
||||
# Osmosis: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Osmosis", "OSMO"),
|
||||
# params={
|
||||
# "addr_hrp": Slip173.OSMOSIS,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Phala
|
||||
# Phala: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Phala Network", "PHA"),
|
||||
# params={
|
||||
# "addr_ss58_format": 30,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Pi Network
|
||||
# PiNetwork: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Pi Network", "PI"),
|
||||
# params={},
|
||||
# )
|
||||
|
||||
# # Configuration for Plasm
|
||||
# Plasm: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Plasm Network", "PLM"),
|
||||
# params={
|
||||
# "addr_ss58_format": 5,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Polkadot
|
||||
# Polkadot: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Polkadot", "DOT"),
|
||||
# params={
|
||||
# "addr_ss58_format": 0,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Polygon
|
||||
# Polygon: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Polygon", "MATIC"),
|
||||
# params={},
|
||||
# )
|
||||
|
||||
# # Configuration for Ripple
|
||||
# Ripple: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Ripple", "XRP"),
|
||||
# params={
|
||||
# "p2pkh_net_ver": _BTC_P2PKH_NET_VER_MN,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Secret Network
|
||||
# SecretNetwork: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Secret Network", "SCRT"),
|
||||
# params={
|
||||
# "addr_hrp": Slip173.SECRET_NETWORK,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Solana
|
||||
# Solana: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Solana", "SOL"),
|
||||
# params={},
|
||||
# )
|
||||
|
||||
# # Configuration for Sora
|
||||
# Sora: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Sora", "XOR"),
|
||||
# params={
|
||||
# "addr_ss58_format": 69,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Stafi
|
||||
# Stafi: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Stafi", "FIS"),
|
||||
# params={
|
||||
# "addr_hrp": Slip173.STAFI,
|
||||
# "addr_ss58_format": 20,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Stellar
|
||||
# Stellar: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Stellar", "XLM"),
|
||||
# params={},
|
||||
# )
|
||||
|
||||
# # Configuration for Sui
|
||||
# Sui: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Sui", "SUI"),
|
||||
# params={
|
||||
# "addr_prefix": "0x",
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Terra
|
||||
# Terra: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Terra", "LUNA"),
|
||||
# params={
|
||||
# "addr_hrp": Slip173.TERRA,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Tezos
|
||||
# Tezos: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Tezos", "XTZ"),
|
||||
# params={},
|
||||
# )
|
||||
|
||||
# # Configuration for Theta
|
||||
# Theta: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Theta Network", "THETA"),
|
||||
# params={},
|
||||
# )
|
||||
|
||||
# # Configuration for Tron
|
||||
# Tron: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Tron", "TRX"),
|
||||
# params={
|
||||
# "addr_prefix": b"\x41",
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for VeChain
|
||||
# VeChain: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("VeChain", "VET"),
|
||||
# params={},
|
||||
# )
|
||||
|
||||
# # Configuration for Verge
|
||||
# Verge: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Verge", "XVG"),
|
||||
# params={
|
||||
# "p2pkh_net_ver": b"\x1e",
|
||||
# "wif_net_ver": b"\x9e",
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Zcash main net
|
||||
# ZcashMainNet: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Zcash", "ZEC"),
|
||||
# params={
|
||||
# "p2pkh_net_ver": b"\x1c\xb8",
|
||||
# "p2sh_net_ver": b"\x1c\xbd",
|
||||
# "wif_net_ver": _BTC_WIF_NET_VER_MN,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Zcash test net
|
||||
# ZcashTestNet: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Zcash TestNet", "ZEC"),
|
||||
# params={
|
||||
# "p2pkh_net_ver": b"\x1d\x25",
|
||||
# "p2sh_net_ver": b"\x1c\xba",
|
||||
# "wif_net_ver": _BTC_WIF_NET_VER_TN,
|
||||
# },
|
||||
# )
|
||||
|
||||
# # Configuration for Zilliqa
|
||||
# Zilliqa: CoinConf = CoinConf(
|
||||
# coin_name=CoinNames("Zilliqa", "ZIL"),
|
||||
# params={
|
||||
# "addr_hrp": Slip173.ZILLIQA,
|
||||
# },
|
||||
# )
|
||||
|
||||
|
||||
# For compatibility
|
||||
# CoinsConf.Neo = CoinsConf.NeoLegacy
|
||||
Binary file not shown.
@ -0,0 +1,3 @@
|
||||
from .bip44_coins import Bip44Coins
|
||||
# from .bip44_conf import Bip44Conf
|
||||
# from .bip44_conf_getter import Bip44ConfGetter
|
||||
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user