Skip to content

Commit 07f9243

Browse files
committed
Fix flake8 issues, improve cli
1 parent f8b329b commit 07f9243

18 files changed

+124
-170
lines changed

requirements-invoice.txt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,6 @@ py-solc-x >=1.1.1,<1.2
33
pycryptodome >=3.16, <4
44
requests >=2.20, <3
55

6-
# Update to 0.9.1 w/ type deduction w/ missing/empty support
7-
#tabulate >=0.9.1,<1
8-
tabulate @ git+https://github.com/pjkundert/python-tabulate.git@python-slip39#egg=tabulate
9-
106
dkimpy[ed25519] >=1.0.5,<2
117

128
# These versions are very brittle; must be upgraded in lock-step (see web3.py/setup.py)

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ cx_Freeze >=6.12 ; sys_platform == "win32"
66
fpdf2 >=2.7.6,<3
77
#hdwallet >=2.3.0,<3
88
hdwallet @ git+https://github.com/pjkundert/python-hdwallet.git@python-slip39#egg=hdwallet
9+
tabulate @ git+https://github.com/pjkundert/python-tabulate.git@python-slip39#egg=tabulate
910
mnemonic >=0.2, <1
1011
qrcode >=7.3
11-
shamir-mnemonic >=0.2.2,<1
12+
shamir-mnemonic >=0.3.0,<0.4

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
'gui', # slip39[gui]: Support PySimpleGUI/tkinter Graphical UI App
1515
'serial', # slip39[serial]: Support serial I/O of generated wallet data
1616
'wallet', # slip39[wallet]: Paper Wallet and BIP-38/Ethereum wallet encryption
17-
# 'invoice', # slip39[invoice]: Generation of invoices, and associated Smart Contracts
17+
'invoice', # slip39[invoice]: Generation of invoices, and associated Smart Contracts
1818
]
1919
extras_require = {
2020
option: list(

slip39/api.py

Lines changed: 48 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import logging
2525
import math
2626
import re
27-
import secrets
2827
import string
2928
import warnings
3029

@@ -41,7 +40,7 @@
4140
from .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
4544
from .recovery import produce_bip39, recover_bip39, recover as recover_slip39
4645

4746
__author__ = "Perry Kundert"
@@ -61,7 +60,7 @@
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 )
@@ -71,7 +70,7 @@
7170
except 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-
221158
class 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+
933909
Details = 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,

slip39/api_passphrase_test.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import codecs
2-
import json
32
import math
43

54
import pytest

slip39/api_test.py

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@
1818
from . import account, create, addresses, addressgroups, accountgroups, Account
1919
from .recovery import recover
2020

21-
from .dependency_test import substitute, nonrandom_bytes, SEED_XMAS, SEED_ONES, SEED_ZERO
21+
from .dependency_test import substitute, nonrandom_bytes, SEED_XMAS, SEED_ONES
2222

2323
BIP39_ABANDON = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
2424
BIP39_ZOO = 'zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong'
2525

26+
2627
def test_account_smoke():
2728
acct = account( SEED_XMAS )
2829
assert acct.address == '0x336cBeAB83aCCdb2541e43D514B62DC6C53675f4'
@@ -117,7 +118,6 @@ def test_account_format():
117118
means to restrict the path to hardened_defaults).
118119
119120
"""
120-
121121
# Legacy Bitcoin
122122
acct = account(
123123
master_secret = BIP39_ABANDON,
@@ -160,7 +160,6 @@ def test_account_format():
160160
assert acct.xprvkey == "xprv9xpXFhFpqdQK3TmytPBqXtGSwS3DLjojFhTGht8gwAAii8py5X6pxeBnQ6ehJiyJ6nDjWGJfZ95WxByFXVkDxHXrqu53WCRGypk2ttuqncb"
161161
assert acct.xpubkey == "xpub6BosfCnifzxcFwrSzQiqu2DBVTshkCXacvNsWGYJVVhhawA7d4R5WSWGFNbi8Aw6ZRc1brxMyWMzG3DSSSSoekkudhUd9yLb6qx39T9nMdj"
162162

163-
164163
# SegWit
165164
acct = account(
166165
master_secret = BIP39_ABANDON,
@@ -191,7 +190,6 @@ def test_account_format():
191190
assert acct.path == "m/49'/0'/0'/0/0"
192191
assert acct.address == "37VucYSaXLCAsxYyAPfbSi9eh4iEcbShgf"
193192

194-
195193
# Bech32
196194
(acct,), = accountgroups(
197195
BIP39_ABANDON, cryptopaths=['Bitcoin'], format='bech32', hardened_defaults=True,
@@ -561,12 +559,12 @@ def test_addressgroups():
561559
)))
562560
# print( repr( addrgrps ))
563561
assert addrgrps == [ # Verified
564-
(0,(('ETH', "m/44'/60'/0'/0/0", '0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E'), # Ledger
565-
('BTC', "m/84'/0'/0'/0/0", 'bc1qk0a9hr7wjfxeenz9nwenw9flhq0tmsf6vsgnn2'), # Ledger
566-
('LTC', "m/84'/2'/0'/0/0", 'ltc1qnreu4d88p5tvh33anptujvcvn3xmfhh43yg0am'), # Ledger
567-
('DOGE', "m/44'/3'/0'/0/0", 'DTMaJd8wqye1fymnjxZ5Cc5QkN1w4pMgXT'), # Ledger
568-
('BSC', "m/44'/60'/0'/0/0", '0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E'), # Ledger
569-
('XRP', "m/44'/144'/0'/0/0", 'rUPzi4ZwoYxi7peKCqUkzqEuSrzSRyLguV'))), # Ledger
562+
(0,(('ETH', "m/44'/60'/0'/0/0", '0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E'), # Ledger
563+
('BTC', "m/84'/0'/0'/0/0", 'bc1qk0a9hr7wjfxeenz9nwenw9flhq0tmsf6vsgnn2'), # Ledger
564+
('LTC', "m/84'/2'/0'/0/0", 'ltc1qnreu4d88p5tvh33anptujvcvn3xmfhh43yg0am'), # Ledger
565+
('DOGE', "m/44'/3'/0'/0/0", 'DTMaJd8wqye1fymnjxZ5Cc5QkN1w4pMgXT'), # Ledger
566+
('BSC', "m/44'/60'/0'/0/0", '0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E'), # Ledger
567+
('XRP', "m/44'/144'/0'/0/0", 'rUPzi4ZwoYxi7peKCqUkzqEuSrzSRyLguV'))), # Ledger
570568
(1, (('ETH', "m/44'/60'/0'/0/1", '0xd1a7451beB6FE0326b4B78e3909310880B781d66'),
571569
('BTC', "m/84'/0'/0'/0/1", 'bc1qkd33yck74lg0kaq4tdcmu3hk4yruhjayxpe9ug'),
572570
('LTC', "m/84'/2'/0'/0/1", 'ltc1qm4yc8vgxyv0xeu8p4vtq2wls245y2ueqpfrp4d'),

slip39/communications_test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
log = logging.getLogger( __package__ )
1919

2020
# Disable printing of details unless something goes wrong...
21-
print_NOOP = lambda *args, **kwds: None
22-
print = print_NOOP
21+
print_NOOP = lambda *args, **kwds: None # noqa: E731
22+
print = print_NOOP # noqa: E273
2323

2424
# If we find a DKIM key, lets use it. Otherwise, we'll just use the pre-defined pre-signed email.Message
2525
dkim_keys = list( Path( __file__ ).resolve().parent.parent.glob( 'licensing.dominionrnd.com.*.key' ))

slip39/dependency_test.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ def test_share_1_of_3_5():
5555
groups = [(3, 5)],
5656
master_secret = SEED_TREZOR,
5757
passphrase = PASS_TREZOR,
58+
extendable = False,
59+
iteration_exponent = 1,
5860
)
5961

6062
#print( json.dumps( mnemonics, indent=4 ))
@@ -71,6 +73,8 @@ def test_share_2_of_groups():
7173
groups = [(1, 1), (1, 1), (2, 5), (3, 6)],
7274
master_secret = SEED_KNOWN,
7375
passphrase = PASS_KNOWN,
76+
extendable = False,
77+
iteration_exponent = 1,
7478
)
7579

7680
#print( json.dumps( mnemonics, indent=4 ))
@@ -121,6 +125,8 @@ def test_bip39( entropy, expected_BIP39, expected_seed, expected_SLIP39 ):
121125
groups = [(1, 1), (1, 1), (2, 5), (3, 6)],
122126
master_secret = seed,
123127
passphrase = b"",
128+
extendable = False,
129+
iteration_exponent = 1,
124130
)
125131

126132
# print( json.dumps( mnemonics, indent=4 ))

slip39/generator/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ def file_opener(): # noqa: F811
217217
)
218218
return ser
219219

220-
def healthy_reset( file ): # noaq: F811
220+
def healthy_reset( file ): # noqa: F811
221221
file.dtr = False
222222
# Wait for a server to de-assert DTR, discarding input. After the Server has de-asserted, we still need to drain
223223
# the Server's output / Client's input buffers, so keep flushing 'til input buffer empty...

0 commit comments

Comments
 (0)