Skip to content

Commit 4d73171

Browse files
committed
appease pylint
1 parent e4b5a32 commit 4d73171

File tree

1 file changed

+57
-36
lines changed

1 file changed

+57
-36
lines changed

src/codex32/codex32.py

Lines changed: 57 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,6 @@
33
# License: BSD-3-Clause
44
"""Complete BIP-93 Codex32 implementation"""
55

6-
import hashlib
7-
import hmac
8-
import secrets
9-
106
CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
117
MS32_CONST = 0x10CE0795C2FD1E62A
128
MS32_LONG_CONST = 0x43381E570BF4798AB26
@@ -17,7 +13,8 @@
1713

1814

1915
def ms32_polymod(values):
20-
GEN = [
16+
"""Compute the ms32 polymod."""
17+
gen = [
2118
0x19DC500CE73FDE210,
2219
0x1BFAE00DEF77FE529,
2320
0x1FBD920FFFE7BEE52,
@@ -29,11 +26,12 @@ def ms32_polymod(values):
2926
b = residue >> 60
3027
residue = (residue & 0x0FFFFFFFFFFFFFFF) << 5 ^ v
3128
for i in range(5):
32-
residue ^= GEN[i] if ((b >> i) & 1) else 0
29+
residue ^= gen[i] if ((b >> i) & 1) else 0
3330
return residue
3431

3532

3633
def ms32_verify_checksum(data):
34+
"""Determine long or short checksum and verify it."""
3735
if len(data) >= 96: # See Long codex32 Strings
3836
return ms32_verify_long_checksum(data)
3937
if len(data) <= 93:
@@ -42,6 +40,7 @@ def ms32_verify_checksum(data):
4240

4341

4442
def ms32_create_checksum(data):
43+
"""Determine long or short checksum, create and return it."""
4544
if len(data) > 80: # See Long codex32 Strings
4645
return ms32_create_long_checksum(data)
4746
values = data
@@ -50,7 +49,8 @@ def ms32_create_checksum(data):
5049

5150

5251
def ms32_long_polymod(values):
53-
GEN = [
52+
"""Compute the ms32 long polymod."""
53+
gen = [
5454
0x3D59D273535EA62D897,
5555
0x7A9BECB6361C6C51507,
5656
0x543F9B7E6C38D8A2A0E,
@@ -62,21 +62,24 @@ def ms32_long_polymod(values):
6262
b = residue >> 70
6363
residue = (residue & 0x3FFFFFFFFFFFFFFFFF) << 5 ^ v
6464
for i in range(5):
65-
residue ^= GEN[i] if ((b >> i) & 1) else 0
65+
residue ^= gen[i] if ((b >> i) & 1) else 0
6666
return residue
6767

6868

6969
def ms32_verify_long_checksum(data):
70+
"""Verify the long codex32 checksum."""
7071
return ms32_long_polymod(data) == MS32_LONG_CONST
7172

7273

7374
def ms32_create_long_checksum(data):
75+
"""Create the long codex32 checksum."""
7476
values = data
7577
polymod = ms32_long_polymod(values + [0] * 15) ^ MS32_LONG_CONST
7678
return [(polymod >> 5 * (14 - i)) & 31 for i in range(15)]
7779

7880

7981
def bech32_mul(a, b):
82+
"""Multiply two bech32 values."""
8083
res = 0
8184
for i in range(5):
8285
res ^= a if ((b >> i) & 1) else 0
@@ -86,7 +89,8 @@ def bech32_mul(a, b):
8689

8790

8891
# noinspection PyPep8
89-
def bech32_lagrange(l, x):
92+
def bech32_lagrange(l, x): # noqa: E741
93+
"""Compute bech32 lagrange."""
9094
n = 1
9195
c = []
9296
for i in l:
@@ -98,18 +102,20 @@ def bech32_lagrange(l, x):
98102
return [bech32_mul(n, bech32_inv[i]) for i in c]
99103

100104

101-
def ms32_interpolate(l, x):
105+
def ms32_interpolate(l, x): # noqa: E741
106+
"""Interpolate codex32."""
102107
w = bech32_lagrange([s[5] for s in l], x)
103108
res = []
104109
for i in range(len(l[0])):
105110
n = 0
106-
for j in range(len(l)):
107-
n ^= bech32_mul(w[j], l[j][i])
111+
for j, val in enumerate(l):
112+
n ^= bech32_mul(w[j], val[i])
108113
res.append(n)
109114
return res
110115

111116

112-
def ms32_recover(l):
117+
def ms32_recover(l): # noqa: E741
118+
"""Recover the codex32 secret."""
113119
return ms32_interpolate(l, 16)
114120

115121

@@ -133,8 +139,10 @@ def ms32_recover(l):
133139
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
134140
# THE SOFTWARE.
135141

142+
# pylint: disable=missing-class-docstring
136143

137144
class Codex32Error(Exception):
145+
msg = "Base Codex32 error class"
138146
def __init__(self, extra: str | None = None):
139147
self.extra = extra
140148
super().__init__(extra)
@@ -219,7 +227,7 @@ def bech32_to_u5(bech=''):
219227

220228

221229
def bech32_decode(bech='', hrp='ms'):
222-
"""Validate a Bech32/Bech32m string, and determine HRP and data."""
230+
"""Validate a Bech32/Codex32 string, and determine HRP and data."""
223231
for i, ch in enumerate(bech):
224232
if ord(ch) < 33 or ord(ch) > 126:
225233
raise InvalidChar(f"non-printable U+{ord(ch):04X} at pos={i}")
@@ -237,7 +245,7 @@ def bech32_decode(bech='', hrp='ms'):
237245
return hrp, data
238246

239247

240-
def crc(crc_len, values):
248+
def compute_crc(crc_len, values):
241249
"""Internal function that computes a CRC checksum for padding."""
242250
if not 0 <= crc_len < 5: # Codex32 string CRC padding
243251
raise InvalidLength(f"{crc_len!r} (expected int in 0..4)")
@@ -253,7 +261,7 @@ def crc(crc_len, values):
253261
return crc & (2 ** crc_len - 1) # Return last crc_len bits as CRC
254262

255263

256-
def convertbits(data, frombits, tobits, pad=True, pad_val=-1, verify_pad=False):
264+
def convertbits(data, frombits, tobits, pad=True, pad_val=-1):
257265
"""General power-of-2 base conversion with CRC padding."""
258266
acc = 0
259267
bits = 0
@@ -269,23 +277,25 @@ def convertbits(data, frombits, tobits, pad=True, pad_val=-1, verify_pad=False):
269277
bits -= tobits
270278
ret.append((acc >> bits) & maxv)
271279
acc = acc & ((1 << bits) - 1)
272-
if verify_pad:
273-
pad = False
274280
if pad and bits:
275281
if pad_val == -1: # Use CRC padding
276282
data_bits = convertbits(ret, tobits, 1) + convertbits([acc], bits, 1)
277-
pad_val = crc(tobits - bits, convertbits(data_bits, tobits, 1))
283+
pad_val = compute_crc(tobits - bits, convertbits(data_bits, tobits, 1))
278284
ret.append(((acc << (tobits - bits)) + pad_val) & maxv)
279285
elif bits >= frombits:
280286
raise IncompleteGroup(f"{bits} bits left over")
281-
elif verify_pad:
282-
if data != convertbits(ret, tobits, frombits, True, pad_val):
283-
pad_str = "CRC" if pad_val < 0 else bin(pad_val)
284-
raise InvalidChecksum(f"Padding bits do not match expected {pad_str} padding.")
285287
return ret
286288

289+
def verify_crc(data, pad_val):
290+
"""Verify the codex32 padding matches the specified type."""
291+
unpadded = convertbits(data, 5, 8, False)
292+
if data != convertbits(unpadded, 8, 5, pad_val=pad_val):
293+
pad_str = "CRC" if pad_val < 0 else bin(pad_val)
294+
raise InvalidChecksum(f"Padding bits do not match expected {pad_str} padding.")
295+
287296

288297
class Codex32String:
298+
"""Class representing a Codex32 string."""
289299
def __init__(self, s=''):
290300
self.s = s
291301

@@ -301,20 +311,23 @@ def __hash__(self):
301311
return hash(self.s)
302312

303313
def sanity_check(self):
314+
"""Perform sanity check on the codex32 string."""
304315
parts = self.parts_inner()
305316
incomplete_group = (len(parts.payload) * 5) % 8
306317
if incomplete_group > 4:
307318
raise IncompleteGroup(str(incomplete_group))
308319

309320
@classmethod
310321
def from_unchecksummed_string(cls, s, hrp="ms"):
322+
"""Create Codex32String from unchecksummed string."""
311323
hrp, data = bech32_decode(s, hrp=hrp)
312324
ret = cls(bech32_encode(data + ms32_create_checksum(data), hrp))
313325
ret.sanity_check()
314326
return ret
315327

316328
@classmethod
317329
def from_string(cls, s, hrp="ms"):
330+
"""Create Codex32String from a codex32 string."""
318331
_, data = bech32_decode(s, hrp=hrp)
319332
if not ms32_verify_checksum(data):
320333
raise InvalidChecksum(f"string={s}")
@@ -323,6 +336,7 @@ def from_string(cls, s, hrp="ms"):
323336
return ret
324337

325338
def parts_inner(self):
339+
"""Get inner parts of the codex32 string."""
326340
hrp, s = self.s.rsplit('1', 1) if '1' in self.s else ("", self.s)
327341
if len(s) < 94 and len(s) > 44:
328342
checksum_len = 13
@@ -338,7 +352,7 @@ def parts_inner(self):
338352
ret = Parts(
339353
hrp=hrp,
340354
k=k,
341-
id=s[1:5],
355+
ident=s[1:5],
342356
share_index=s[5],
343357
payload=s[6:len(s) - checksum_len],
344358
checksum=s[-checksum_len:],
@@ -348,10 +362,12 @@ def parts_inner(self):
348362
return ret
349363

350364
def parts(self):
365+
"""Get parts of the codex32 string."""
351366
return self.parts_inner()
352367

353368
@classmethod
354369
def interpolate_at(cls, shares, target):
370+
"""Interpolate to a specific target share index."""
355371
indices = []
356372
ms32_shares = []
357373
s0_parts = shares[0].parts()
@@ -365,54 +381,59 @@ def interpolate_at(cls, shares, target):
365381
raise MismatchedHrp(f"{s0_parts.hrp}, {parts.hrp}")
366382
if s0_parts.k != parts.k:
367383
raise MismatchedThreshold(f"{s0_parts.k}, {parts.k}")
368-
if s0_parts.id != parts.id:
369-
raise MismatchedId(f"{s0_parts.id}, {parts.id}")
384+
if s0_parts.ident != parts.ident:
385+
raise MismatchedId(f"{s0_parts.ident}, {parts.ident}")
370386
if parts.share_index in indices:
371387
raise RepeatedIndex(parts.share_index)
372388
indices.append(parts.share_index)
373389
ms32_shares.append(bech32_decode(share.s)[1])
374-
for i in range(len(shares)):
390+
for i, share in enumerate(shares):
375391
if indices[i] == target:
376-
return shares[i]
392+
return share
377393
result = ms32_interpolate(ms32_shares, CHARSET.index(target.lower()))
378394
ret = bech32_encode(result, s0_parts.hrp)
379395
return cls(ret)
380396

381397
@classmethod
382-
def from_seed(cls, data, hrp='ms', k=0, id='', share_idx='s', pad_val=-1):
398+
# pylint: disable=too-many-positional-arguments,too-many-arguments
399+
def from_seed(cls, data, ident, hrp='ms', k=0, share_idx='s', pad_val=-1):
400+
"""Create Codex32String from seed bytes."""
383401
if 16 > len(data) or len(data) > 64:
384402
raise InvalidLength(f"{len(data)} bytes data MUST be 16 to 64 bytes")
385-
if len(id) != 4:
386-
raise IdNotLength4(f"{len(id)}")
403+
if len(ident) != 4:
404+
raise IdNotLength4(f"{len(ident)}")
387405
if not (1 < k <= 9 or k == 0):
388406
raise InvalidThresholdN(str(k))
389407
payload = convertbits(data, 8, 5, pad_val=pad_val)
390-
header = bech32_to_u5(str(k) + id + share_idx)
408+
header = bech32_to_u5(str(k) + ident + share_idx)
391409
combined = header + payload
392410
ret = bech32_encode(combined + ms32_create_checksum(combined), hrp)
393411
return cls(ret)
394412

395413
class Parts:
396-
def __init__(self, hrp, k, id, share_index, payload, checksum):
414+
"""Class representing parts of a Codex32 string."""
415+
def __init__(self, hrp, k, ident, share_index, payload, checksum):
416+
# pylint: disable=too-many-arguments
397417
self.hrp = hrp
398418
self.k = k
399-
self.id = id
419+
self.ident = ident
400420
self.share_index = share_index
401421
self.payload = payload
402422
self.checksum = checksum
403423

404424
def data(self):
425+
"""Get data from parts."""
405426
return bytes(convertbits(bech32_to_u5(self.payload), 5, 8, False))
406427

407428
def __eq__(self, other):
408429
if not isinstance(other, Parts):
409430
return False
410431
return (self.hrp == other.hrp and
411432
self.k == other.k and
412-
self.id == other.id and
433+
self.ident == other.ident and
413434
self.share_index == other.share_index and
414435
self.payload == other.payload and
415436
self.checksum == other.checksum)
416437

417438
def __hash__(self):
418-
return hash((self.hrp, self.k, self.id, self.share_index, self.payload, self.checksum))
439+
return hash((self.hrp, self.k, self.ident, self.share_index, self.payload, self.checksum))

0 commit comments

Comments
 (0)