Skip to content

Commit

Permalink
Updates
Browse files Browse the repository at this point in the history
- Introduce `DEFAULT_SEMANTIC` to cryptocurrency description;
- Introduce `"base58check"` semantic (for DeSo);
- Introduce `address()` in `HDWallet`;
- Fix xpub/xprv address prefixes for DESOTEST.

The default semantic for Bitcoin is "p2wpkh".
  • Loading branch information
alecov committed Feb 24, 2022
1 parent aa0c4a3 commit 075f739
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 45 deletions.
54 changes: 13 additions & 41 deletions hdwallet/cryptocurrencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,19 @@ class Cryptocurrency(NestedNamespace):
SOURCE_CODE: Optional[str]
COIN_TYPE: CoinType

SCRIPT_ADDRESS: int
SCRIPT_ADDRESS: Optional[int] = None
PUBLIC_KEY_ADDRESS: int = 0
PRIVATE_KEY_ADDRESS: int = 0
SEGWIT_ADDRESS: SegwitAddress
SEGWIT_ADDRESS: Optional[SegwitAddress] = None

EXTENDED_PRIVATE_KEY: ExtendedPrivateKey
EXTENDED_PUBLIC_KEY: ExtendedPublicKey

MESSAGE_PREFIX: Optional[str]
DEFAULT_PATH: str
WIF_SECRET_KEY: int
WIF_SECRET_KEY: Optional[int] = None

DEFAULT_SEMANTIC: str = "p2pkh"


class AnonMainnet(Cryptocurrency):
Expand Down Expand Up @@ -713,6 +715,7 @@ class BitcoinMainnet(Cryptocurrency):
MESSAGE_PREFIX = "\x18Bitcoin Signed Message:\n"
DEFAULT_PATH = f"m/44'/{str(COIN_TYPE)}/0'/0/0"
WIF_SECRET_KEY = 0x80
DEFAULT_SEMANTIC = "p2wpkh"


class BitcoinPlusMainnet(Cryptocurrency):
Expand Down Expand Up @@ -833,6 +836,7 @@ class BitcoinTestnet(Cryptocurrency):
MESSAGE_PREFIX = "\x18Bitcoin Signed Message:\n"
DEFAULT_PATH = f"m/44'/{str(COIN_TYPE)}/0'/0/0"
WIF_SECRET_KEY = 0xef
DEFAULT_SEMANTIC = "p2wpkh"


class BitcoinZMainnet(Cryptocurrency):
Expand Down Expand Up @@ -1686,34 +1690,18 @@ class DeSoMainnet(Cryptocurrency):
"HARDENED": True
})

SCRIPT_ADDRESS = 0x05
PUBLIC_KEY_ADDRESS = 0xCD1400
PRIVATE_KEY_ADDRESS = 0x350000
SEGWIT_ADDRESS = SegwitAddress({
"HRP": "bc",
"VERSION": 0x00
})

EXTENDED_PRIVATE_KEY = ExtendedPrivateKey({
"P2PKH": 0x0488ade4,
"P2SH": 0x0488ade4,
"P2WPKH": 0x04b2430c,
"P2WPKH_IN_P2SH": 0x049d7878,
"P2WSH": 0x02aa7a99,
"P2WSH_IN_P2SH": 0x0295b005
"BASE58CHECK": 0x0488ade4
})
EXTENDED_PUBLIC_KEY = ExtendedPublicKey({
"P2PKH": 0x0488b21e,
"P2SH": 0x0488b21e,
"P2WPKH": 0x04b24746,
"P2WPKH_IN_P2SH": 0x049d7cb2,
"P2WSH": 0x02aa7ed3,
"P2WSH_IN_P2SH": 0x0295b43f
"BASE58CHECK": 0x0488b21e
})

MESSAGE_PREFIX = "\x18DeSo Signed Message:\n"
DEFAULT_PATH = f"m/44'/{str(COIN_TYPE)}/0'/0/0"
WIF_SECRET_KEY = 0x80
DEFAULT_SEMANTIC = "base58check"


class DeSoTestnet(Cryptocurrency):
Expand All @@ -1727,34 +1715,18 @@ class DeSoTestnet(Cryptocurrency):
"HARDENED": True
})

SCRIPT_ADDRESS = 0x05
PUBLIC_KEY_ADDRESS = 0x11C200
PRIVATE_KEY_ADDRESS = 0x4F061B
SEGWIT_ADDRESS = SegwitAddress({
"HRP": "bc",
"VERSION": 0x00
})

EXTENDED_PRIVATE_KEY = ExtendedPrivateKey({
"P2PKH": 0x0488ade4,
"P2SH": 0x0488ade4,
"P2WPKH": 0x04b2430c,
"P2WPKH_IN_P2SH": 0x049d7878,
"P2WSH": 0x02aa7a99,
"P2WSH_IN_P2SH": 0x0295b005
"BASE58CHECK": 0x04358394
})
EXTENDED_PUBLIC_KEY = ExtendedPublicKey({
"P2PKH": 0x0488b21e,
"P2SH": 0x0488b21e,
"P2WPKH": 0x04b24746,
"P2WPKH_IN_P2SH": 0x049d7cb2,
"P2WSH": 0x02aa7ed3,
"P2WSH_IN_P2SH": 0x0295b43f
"BASE58CHECK": 0x043587cf
})

MESSAGE_PREFIX = "\x18DeSo Signed Message:\n"
DEFAULT_PATH = f"m/44'/{str(COIN_TYPE)}/0'/0/0"
WIF_SECRET_KEY = 0x80
DEFAULT_SEMANTIC = "base58check"


class DiamondMainnet(Cryptocurrency):
Expand Down
38 changes: 34 additions & 4 deletions hdwallet/hdwallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class HDWallet:
:type symbol: str
:param cryptocurrency: Cryptocurrency instance, defaults to ``None``.
:type cryptocurrency: Cryptocurrency
:param semantic: Extended semantic, defaults to ``P2PKH``.
:param semantic: Extended semantic
:type semantic: str
:param use_default_path: Use default derivation path, defaults to ``False``.
:type use_default_path: bool
Expand All @@ -79,7 +79,7 @@ class HDWallet:
"""

def __init__(self, symbol: str = "BTC", cryptocurrency: Any = None,
semantic: str = "p2pkh", use_default_path: bool = False):
semantic: str = None, use_default_path: bool = False):
self._cryptocurrency: Any = None
if cryptocurrency:
if not issubclass(cryptocurrency, Cryptocurrency):
Expand All @@ -88,6 +88,9 @@ def __init__(self, symbol: str = "BTC", cryptocurrency: Any = None,
else:
self._cryptocurrency: Any = get_cryptocurrency(symbol=symbol)

if semantic is None:
semantic = self._cryptocurrency.DEFAULT_SEMANTIC

self._strength: Optional[int] = None
self._entropy: Optional[str] = None
self._mnemonic: Optional[str] = None
Expand Down Expand Up @@ -878,6 +881,9 @@ def private_key_base58check(self) -> str:
unhexlify(self.private_key())
).decode()

def base58check_address(self) -> str:
return self.public_key_base58check()

def strength(self) -> Optional[int]:
"""
Get Entropy strength.
Expand Down Expand Up @@ -1142,7 +1148,7 @@ def p2pkh_address(self) -> str:
network_hash160_bytes = _unhexlify(self._cryptocurrency.PUBLIC_KEY_ADDRESS) + public_key_hash
return ensure_string(base58.b58encode_check(network_hash160_bytes))

def p2sh_address(self) -> str:
def p2sh_address(self) -> Optional[str]:
"""
Get Pay to Script Hash (P2SH) address.
Expand All @@ -1157,6 +1163,9 @@ def p2sh_address(self) -> str:
"3Jp6ad4ErhibQmhSRfavbPRiUyg2xTTT4j"
"""

if self._cryptocurrency.SCRIPT_ADDRESS is None:
return None

compressed_public_key = unhexlify(self.compressed())
public_key_hash = hashlib.new('ripemd160', sha256(compressed_public_key).digest()).hexdigest()
public_key_hash_script = unhexlify("76a914" + public_key_hash + "88ac")
Expand All @@ -1181,6 +1190,8 @@ def p2wpkh_address(self) -> Optional[str]:

compressed_public_key = unhexlify(self.compressed())
public_key_hash = hashlib.new('ripemd160', sha256(compressed_public_key).digest()).digest()
if self._cryptocurrency.SEGWIT_ADDRESS is None:
return None
if self._cryptocurrency.SEGWIT_ADDRESS.HRP is None:
return None
return ensure_string(encode(self._cryptocurrency.SEGWIT_ADDRESS.HRP, 0, public_key_hash))
Expand All @@ -1200,10 +1211,15 @@ def p2wpkh_in_p2sh_address(self) -> Optional[str]:
"3CCrxPrHNa6ePbnB7qjh7S3vaPx9qiLc3e"
"""

if self._cryptocurrency.SCRIPT_ADDRESS is None:
return None

compressed_public_key = unhexlify(self.compressed())
public_key_hash = hashlib.new('ripemd160', sha256(compressed_public_key).digest()).hexdigest()
script_hash = hashlib.new('ripemd160', sha256(unhexlify("0014" + public_key_hash)).digest()).digest()
network_hash160_bytes = _unhexlify(self._cryptocurrency.SCRIPT_ADDRESS) + script_hash
if self._cryptocurrency.SEGWIT_ADDRESS is None:
return None
if self._cryptocurrency.SEGWIT_ADDRESS.HRP is None:
return None
return ensure_string(base58.b58encode_check(network_hash160_bytes))
Expand All @@ -1225,6 +1241,8 @@ def p2wsh_address(self) -> Optional[str]:

compressed_public_key = unhexlify("5121" + self.compressed() + "51ae")
script_hash = sha256(compressed_public_key).digest()
if self._cryptocurrency.SEGWIT_ADDRESS is None:
return None
if self._cryptocurrency.SEGWIT_ADDRESS.HRP is None:
return None
return ensure_string(encode(self._cryptocurrency.SEGWIT_ADDRESS.HRP, 0, script_hash))
Expand All @@ -1244,6 +1262,9 @@ def p2wsh_in_p2sh_address(self) -> Optional[str]:
"38YMonfh2yLFRViLrM2kdvZx8ctcp1vbbV"
"""

if self._cryptocurrency.SCRIPT_ADDRESS is None:
return None

compressed_public_key = unhexlify("5121" + self.compressed() + "51ae")
script_hash = unhexlify("0020" + sha256(compressed_public_key).hexdigest())
script_hash = hashlib.new('ripemd160', sha256(script_hash).digest()).digest()
Expand All @@ -1252,6 +1273,11 @@ def p2wsh_in_p2sh_address(self) -> Optional[str]:
return None
return ensure_string(base58.b58encode_check(network_hash160_bytes))

def address(self, semantic=None):
if semantic == None:
semantic = self._cryptocurrency.DEFAULT_SEMANTIC
return getattr(self, f"{semantic}_address")()

def wif(self) -> Optional[str]:
"""
Get Wallet Important Format.
Expand All @@ -1267,6 +1293,9 @@ def wif(self) -> Optional[str]:
"KzsHWUJsrTWUUhBGPfMMxLLydiH7NhEn6z7mKHXD5qNkUWaC4TEn"
"""

if self._cryptocurrency.WIF_SECRET_KEY is None:
return

return check_encode(_unhexlify(self._cryptocurrency.WIF_SECRET_KEY) + self._key.to_string() + b"\x01") if self._key else None

def dumps(self) -> dict:
Expand Down Expand Up @@ -1321,7 +1350,8 @@ def dumps(self) -> dict:
p2wpkh_in_p2sh=self.p2wpkh_in_p2sh_address(),
p2wsh=self.p2wsh_address(),
p2wsh_in_p2sh=self.p2wsh_in_p2sh_address()
)
),
address=self.address()
)


Expand Down

0 comments on commit 075f739

Please sign in to comment.