2424import logging
2525import math
2626import re
27- import secrets
2827import string
2928import warnings
3029
4140from .defaults import (
4241 BITS_DEFAULT , BITS , MNEM_ROWS_COLS , GROUPS , GROUP_REQUIRED_RATIO , GROUP_THRESHOLD_RATIO , CRYPTO_PATHS
4342)
44- from .util import ordinal , commas , is_listlike , is_mapping
43+ from .util import ordinal , commas , is_mapping
4544from .recovery import produce_bip39 , recover_bip39 , recover as recover_slip39
4645
4746__author__ = "Perry Kundert"
6160 AES = None
6261 scrypt = None
6362 message = f"Unable to support Paper Wallet output: { exc } "
64- warnings .warn ( message , ImportWarning )
63+ warnings .warning ( message , ImportWarning )
6564 if log .isEnabledFor ( logging .DEBUG ):
6665 log .exception ( message )
6766 paper_wallet_issues .append ( message )
7170except ImportError as exc :
7271 eth_account = None
7372 message = f"Unable to support Paper Wallet output: { exc } "
74- warnings .warn ( message , ImportWarning )
73+ warnings .warning ( message , ImportWarning )
7574 if log .isEnabledFor ( logging .DEBUG ):
7675 log .exception ( message )
7776 paper_wallet_issues .append ( message )
@@ -156,68 +155,6 @@ class BinanceMainnet( cryptocurrencies.Cryptocurrency ):
156155 WIF_SECRET_KEY = 0x80
157156
158157
159- class RippleMainnet ( cryptocurrencies .Cryptocurrency ):
160- """The standard HDWallet.p2pkh_address (Pay to Public Key Hash) encoding is used, w/ a prefix of
161- 00. However, the XRP-specific base-58 encoding is used, resulting in a fixed 'r' prefix.
162-
163- See: https://xrpl.org/accounts.html#address-encoding.
164-
165- """
166- NAME = "Ripple"
167- SYMBOL = "XRP"
168- NETWORK = "mainnet"
169- SOURCE_CODE = "https://github.com/ripple/rippled"
170- COIN_TYPE = cryptocurrencies .CoinType ({
171- "INDEX" : 144 ,
172- "HARDENED" : True
173- })
174-
175- PUBLIC_KEY_ADDRESS = 0x00 # Results in the prefix r..., when used w/ the Ripple base-58 alphabet
176- SEGWIT_ADDRESS = cryptocurrencies .SegwitAddress ({
177- "HRP" : None ,
178- "VERSION" : 0x00
179- })
180-
181- EXTENDED_PRIVATE_KEY = cryptocurrencies .ExtendedPrivateKey ({
182- "P2PKH" : None ,
183- "P2SH" : None ,
184- "P2WPKH" : None ,
185- "P2WPKH_IN_P2SH" : None ,
186- "P2WSH" : None ,
187- "P2WSH_IN_P2SH" : None ,
188- })
189- EXTENDED_PUBLIC_KEY = cryptocurrencies .ExtendedPublicKey ({
190- "P2PKH" : None ,
191- "P2SH" : None ,
192- "P2WPKH" : None ,
193- "P2WPKH_IN_P2SH" : None ,
194- "P2WSH" : None ,
195- "P2WSH_IN_P2SH" : None ,
196- })
197-
198- MESSAGE_PREFIX = None
199- DEFAULT_PATH = f"m/44'/{ str (COIN_TYPE )} /0'/0/0"
200- WIF_SECRET_KEY = 0x80
201-
202-
203- class XRPHDWallet ( hdwallet .HDWallet ):
204- """The XRP address format uses the standard p2pkh_address formulation, from
205- https://xrpl.org/accounts.html#creating-accounts:
206-
207- The ripemd160 hash of sha256 hash of public key, then base58-encoded w/ 4-byte checksum. The
208- base-58 dictionary used is the standard Ripple (not Bitcoin!) alphabet:
209-
210- rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz
211-
212- NOTE: Only secp256k1 keypairs are supported; these are the default for the Ripple ledger.
213-
214- """
215- def p2pkh_address ( self ):
216- p2pkh_btc = super ( XRPHDWallet , self ).p2pkh_address ()
217- p2pkh = base58 .b58decode_check ( p2pkh_btc )
218- return base58 .b58encode_check ( p2pkh , base58 .RIPPLE_ALPHABET ).decode ( 'UTF-8' )
219-
220-
221158class Account :
222159 """A Cryptocurrency "Account" / Wallet, based on a variety of underlying Python crypto-asset
223160 support modules. Presently, only meherett/python-hdwallet is used.
@@ -315,11 +252,9 @@ class Account:
315252 # Any locally-defined python-hdwallet classes, cryptocurrency definitions, and any that may
316253 # require some adjustments when calling python-hdwallet address and other functions.
317254 CRYPTO_WALLET_CLS = dict (
318- XRP = XRPHDWallet ,
319255 )
320256 CRYPTO_LOCAL = dict (
321257 BSC = BinanceMainnet ,
322- XRP = RippleMainnet ,
323258 )
324259 CRYPTO_LOCAL_SYMBOL = dict (
325260 BSC = "ETH"
@@ -930,6 +865,47 @@ def random_secret(
930865 return RANDOM_BYTES ( seed_length )
931866
932867
868+ def stretch_seed_entropy ( entropy , n , bits , encoding = None ):
869+ """To support the generation of a number of Seeds, each subsequent seed *must* be independent of
870+ the prior seed. The Seed Data supplied (ie. recovered from BIP/SLIP-39 Mnemonics, or from fixed/random
871+ data) is of course unchanging for the subsequent seeds to be produced; only the "extra" Seed Entropy
872+ is useful for producing multiple sequential Seeds. Returns the designated number of bits
873+ (rounded up to bytes).
874+
875+ If non-binary hex data is supplied, encoding should be 'hex_codec' (0-filled/truncated on the
876+ right up to the required number of bits); otherwise probably 'UTF-8' (and we'll always stretch
877+ other encoded Entropy, even for the first (ie. 0th) seed).
878+
879+ If binary data is supplied, it must be sufficient to provide the required number of bits for the
880+ first and subsequent Seeds (SHA-512 is used to stretch, so any encoded and stretched entropy
881+ data will be sufficient)
882+
883+ """
884+ assert n == 0 or ( entropy and n >= 0 ), \
885+ f"Some Extra Seed Entropy is required to produce the { ordinal (n + 1 )} + Seed(s)"
886+ assert ( type (entropy ) is bytes ) == ( not encoding ), \
887+ "If non-binary Seed Entropy is supplied, an appropriate encoding must be specified"
888+ if encoding :
889+ if encoding == 'hex_codec' :
890+ # Hexadecimal Entropy was provided; Use the raw encoded Hex data for the first round!
891+ entropy = f"{ entropy :<0{bits // 4 }.{bits // 4 }} "
892+ entropy = codecs .decode ( entropy , encoding ) # '012abc' --> b'\x01\x2a\xbc'
893+ else :
894+ # Other encoding was provided, eg 'UTF-8', 'ASCII', ...; stretch for the 0th Seed, too.
895+ n += 1
896+ entropy = codecs .encode ( entropy , encoding ) # '012abc' --> b'012abc'
897+ octets = ( bits + 7 ) // 8
898+ if entropy :
899+ for _ in range ( n ):
900+ entropy = hashlib .sha512 ( entropy ).digest ()
901+ else :
902+ # If no entropy provided, result is all 0
903+ entropy = b'\0 ' * octets
904+ assert len ( entropy ) >= octets , \
905+ "Insufficient extra Seed Entropy provided for {ordinal(n+1)} {bits}-bit Seed"
906+ return entropy [:octets ]
907+
908+
933909Details = namedtuple ( 'Details' , ('name' , 'group_threshold' , 'groups' , 'accounts' , 'using_bip39' ) )
934910
935911
@@ -1046,7 +1022,7 @@ def create(
10461022 if isinstance ( master_secret , str ) and all ( c in '0123456789abcdef' for c in master_secret .lower () ):
10471023 master_secret = codecs .decode ( master_secret , 'hex_codec' )
10481024 if using_bip39 is None and isinstance ( master_secret , str ):
1049- # Assume it must be a BIP-39 Mnemonic
1025+ # Assume it must be a BIP-39 Mnemonic
10501026 using_bip39 = True
10511027 else :
10521028 # Assume caller knows; default False (use SLIP-39 directly)
@@ -1059,7 +1035,7 @@ def create(
10591035 # using the BIP-39 Seed generated from entropy + passphrase. This should be the "typical"
10601036 # use-case, where someone already has a BIP-39 Mnemonic and/or wants to use a "standard"
10611037 # BIP-39 compatible hardware wallet.
1062- log .warning ( f "Assuming BIP-39 seed entropy: Ensure you recover and use via a BIP-39 Mnemonic" )
1038+ log .warning ( "Assuming BIP-39 seed entropy: Ensure you recover and use via a BIP-39 Mnemonic" )
10631039 if isinstance ( master_secret , str ):
10641040 master_secret = recover_bip39 ( mnemonic = master_secret , as_entropy = True )
10651041 bip39_mnem = produce_bip39 ( entropy = master_secret )
@@ -1165,6 +1141,7 @@ def mnemonics(
11651141 groups = groups ,
11661142 master_secret = master_secret ,
11671143 passphrase = passphrase or b"" , # python-shamir-mnemonic requires bytes
1144+ extendable = False ,
11681145 iteration_exponent = iteration_exponent ,
11691146 )
11701147
@@ -1263,7 +1240,7 @@ def accountgroups(
12631240 master_secret : Union [str ,bytes ],
12641241 cryptopaths : Optional [Sequence [Union [str ,Tuple [str ,str ],Tuple [str ,str ,str ]]]] = None , # default: ETH, BTC at default path, format
12651242 allow_unbounded : bool = True ,
1266- passphrase : Optional [Union [bytes ,str ]] = None , # If mnemonic(s) provided, then passphrase/using_bip39 optional
1243+ passphrase : Optional [Union [bytes ,str ]] = None , # If mnemonic(s) provided, then passphrase/using_bip39 optional
12671244 using_bip39 : bool = False ,
12681245 format : Optional [str ] = None , # If the default format for every cryptopath isn't desired
12691246 edit : Optional [str ] = None ,
0 commit comments