From d54ed9d427fafe183a94473e1f8b7916e3ca99ec Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 26 Sep 2025 10:40:23 +0000 Subject: [PATCH] modtest.py seedsplit, subseed: refactor --- test/modtest_d/seedsplit.py | 312 +++++++++++++++++----------------- test/modtest_d/subseed.py | 326 ++++++++++++++++++------------------ 2 files changed, 315 insertions(+), 323 deletions(-) diff --git a/test/modtest_d/seedsplit.py b/test/modtest_d/seedsplit.py index 2195d9eb..53262f2c 100755 --- a/test/modtest_d/seedsplit.py +++ b/test/modtest_d/seedsplit.py @@ -1,182 +1,178 @@ #!/usr/bin/env python3 """ -test.modtest_d.seedsplit: seed splitting unit test for the MMGen suite +test.modtest_d.seedsplit: seed splitting unit tests for the MMGen suite """ from mmgen.util import msg, msg_r +from mmgen.color import pink +from mmgen.seed import Seed +from mmgen.seedsplit import SeedShareList, SeedShareIdx from ..include.common import cfg, vmsg, vmsg_r -class unit_test: +cfg.debug_subseed = bool(cfg.verbose) - def run_test(self, name, ut): - from mmgen.seed import Seed - from mmgen.seedsplit import SeedShareList, SeedShareIdx +def basic_ops(master_idx): + test_data = { + 'default': ( + (8, '4710FBF0', 'B3D9411B', '2670E83D', 'D1FC57ED', 'AE49CABE', '63FFBA62', 0, 0), + (6, '9D07ABBD', 'AF5DC2F6', '1A3BBDAC', '2548AEE9', 'B94F7450', '1F4E5A12', 0, 0), + (4, '43670520', '1F72C066', 'E5AA8DA1', 'A33966A0', 'D2BCE0A5', 'A568C315', 0, 0), + ), + 'φυβαρ': ( + (8, '4710FBF0', '269D658C', '9D25889E', '6D730ECB', 'C61A963F', '9FE99C05', 0, 0), + (6, '9D07ABBD', '4998B33E', 'F00CE041', 'C612BEE5', '35CD3675', '41B3BE61', 0, 0), + (4, '43670520', '77140076', 'EA82CB30', '80F7AEDE', 'D168D768', '77BE57AA', 0, 0), + ) + } + test_data_master = { + '1': { + 'default': ( + (8, '4710FBF0', '6AE6177F', 'AC12090C', '6AE6177F', + '3E87A907', '7D1FEA56', 'BFEBFFFF', '629A9808'), + (4, '43670520', '6739535C', 'ABF4DD38', '6739535C', + '778E9C60', '89CBCFD2', '689FABF5', '70BED76B'), + ), + 'φυβαρ': ( + (8, '4710FBF0', '6AE6177F', 'AC5FA32E', '6AE6177F', + '9777A750', 'C7CF2AFC', '035AAACB', 'C777FBE4'), + (4, '43670520', '6739535C', '37EBA2F5', '6739535C', + '927549D2', '29BADEE7', '9CA73A03', '313F5528')) + }, + '5': { + 'default': ( + (8, '4710FBF0', '5EFAC3D6', 'B489167D', '5EFAC3D6', + 'BB004DC5', '1A0381C0', '4EA182E3', '547FB2DC'), + (4, '43670520', 'EE93DB0E', '44962A7D', 'EE93DB0E', + '07339882', '376A05B1', 'CE51D022', '00149CA3'), + ), + 'φυβαρ': ( + (8, '4710FBF0', '5EFAC3D6', 'A6E27EE3', '5EFAC3D6', + '32C24668', 'B4C54297', '1EC9B71B', '8C5C6B1C'), + (4, '43670520', 'EE93DB0E', 'B584E963', 'EE93DB0E', + '4BEA2AB2', '4BEA65C7', '140FC43F', 'BBD19461')) + } + } + if master_idx: + test_data = test_data_master[str(master_idx)] - cfg.debug_subseed = bool(cfg.verbose) + for id_str in (None, 'default', 'φυβαρ'): + vmsg(f'Testing ID str: {pink(str(id_str))}') - def basic_ops(master_idx): - test_data = { - 'default': ( - (8, '4710FBF0', 'B3D9411B', '2670E83D', 'D1FC57ED', 'AE49CABE', '63FFBA62', 0, 0), - (6, '9D07ABBD', 'AF5DC2F6', '1A3BBDAC', '2548AEE9', 'B94F7450', '1F4E5A12', 0, 0), - (4, '43670520', '1F72C066', 'E5AA8DA1', 'A33966A0', 'D2BCE0A5', 'A568C315', 0, 0), - ), - 'φυβαρ': ( - (8, '4710FBF0', '269D658C', '9D25889E', '6D730ECB', 'C61A963F', '9FE99C05', 0, 0), - (6, '9D07ABBD', '4998B33E', 'F00CE041', 'C612BEE5', '35CD3675', '41B3BE61', 0, 0), - (4, '43670520', '77140076', 'EA82CB30', '80F7AEDE', 'D168D768', '77BE57AA', 0, 0), - ) - } - test_data_master = { - '1': { - 'default': ( - (8, '4710FBF0', '6AE6177F', 'AC12090C', '6AE6177F', - '3E87A907', '7D1FEA56', 'BFEBFFFF', '629A9808'), - (4, '43670520', '6739535C', 'ABF4DD38', '6739535C', - '778E9C60', '89CBCFD2', '689FABF5', '70BED76B'), - ), - 'φυβαρ': ( - (8, '4710FBF0', '6AE6177F', 'AC5FA32E', '6AE6177F', - '9777A750', 'C7CF2AFC', '035AAACB', 'C777FBE4'), - (4, '43670520', '6739535C', '37EBA2F5', '6739535C', - '927549D2', '29BADEE7', '9CA73A03', '313F5528')) - }, - '5': { - 'default': ( - (8, '4710FBF0', '5EFAC3D6', 'B489167D', '5EFAC3D6', - 'BB004DC5', '1A0381C0', '4EA182E3', '547FB2DC'), - (4, '43670520', 'EE93DB0E', '44962A7D', 'EE93DB0E', - '07339882', '376A05B1', 'CE51D022', '00149CA3'), - ), - 'φυβαρ': ( - (8, '4710FBF0', '5EFAC3D6', 'A6E27EE3', '5EFAC3D6', - '32C24668', 'B4C54297', '1EC9B71B', '8C5C6B1C'), - (4, '43670520', 'EE93DB0E', 'B584E963', 'EE93DB0E', - '4BEA2AB2', '4BEA65C7', '140FC43F', 'BBD19461')) - } - } - if master_idx: - test_data = test_data_master[str(master_idx)] - - for id_str in (None, 'default', 'φυβαρ'): - msg_r(f'Testing basic ops (id_str={id_str!r}, master_idx={master_idx})...') - vmsg('') - - for a, b, c, d, e, f, h, i, p in test_data[id_str if id_str is not None else 'default']: - seed_bin = bytes.fromhex('deadbeef' * a) - seed = Seed(cfg, seed_bin=seed_bin) - assert seed.sid == b, seed.sid - - for share_count, j, k, l, m in ( - (2, c, c, d, i), - (5, e, f, h, p)): - - shares = seed.split(share_count, id_str=id_str, master_idx=master_idx) - A = len(shares) - assert A == share_count, A - - s = shares.format() - vmsg_r(f'\n{s}') - assert len(s.strip().split('\n')) == share_count+6, s - - if master_idx: - A = shares.get_share_by_idx(1, base_seed=False).sid - B = shares.get_share_by_seed_id(j, base_seed=False).sid - assert A == B == m, A - - A = shares.get_share_by_idx(1, base_seed=True).sid - B = shares.get_share_by_seed_id(j, base_seed=True).sid - assert A == B == j, A - - A = shares.get_share_by_idx(share_count-1, base_seed=True).sid - B = shares.get_share_by_seed_id(k, base_seed=True).sid - assert A == B == k, A - - A = shares.get_share_by_idx(share_count).sid - B = shares.get_share_by_seed_id(l).sid - assert A == B == l, A - - A = shares.join().sid - assert A == b, A - - if master_idx: - slist = [shares.get_share_by_idx(i+1, base_seed=True) for i in range(len(shares))] - A = Seed.join_shares(cfg, slist, master_idx=master_idx, id_str=id_str).sid - assert A == b, A - - msg('OK') - - def defaults_and_limits(): - msg_r('Testing defaults and limits...') - - seed_bin = bytes.fromhex('deadbeef' * 8) + for a, b, c, d, e, f, h, i, p in test_data[id_str if id_str is not None else 'default']: + seed_bin = bytes.fromhex('deadbeef' * a) seed = Seed(cfg, seed_bin=seed_bin) + assert seed.sid == b, seed.sid - shares = seed.split(SeedShareIdx.max_val) - s = shares.format() -# vmsg_r(f'\n{s}') - assert len(s.strip().split('\n')) == 1030, s + for share_count, j, k, l, m in ( + (2, c, c, d, i), + (5, e, f, h, p)): - A = shares.get_share_by_idx(1024).sid - B = shares.get_share_by_seed_id('4BA23728').sid - assert A == '4BA23728', A - assert B == '4BA23728', B + shares = seed.split(share_count, id_str=id_str, master_idx=master_idx) + A = len(shares) + assert A == share_count, A - A = shares.join().sid - B = seed.sid - assert A == B, A + s = shares.format() + vmsg_r(f'\n{s}') + assert len(s.strip().split('\n')) == share_count+6, s - msg('OK') + if master_idx: + A = shares.get_share_by_idx(1, base_seed=False).sid + B = shares.get_share_by_seed_id(j, base_seed=False).sid + assert A == B == m, A - def collisions(seed_hex, ss_count, last_sid, collisions_chk, master_idx): + A = shares.get_share_by_idx(1, base_seed=True).sid + B = shares.get_share_by_seed_id(j, base_seed=True).sid + assert A == B == j, A - msg_r(f'Testing Seed ID collisions ({ss_count} seed shares, master_idx={master_idx})...') - vmsg('') + A = shares.get_share_by_idx(share_count-1, base_seed=True).sid + B = shares.get_share_by_seed_id(k, base_seed=True).sid + assert A == B == k, A - seed_bin = bytes.fromhex(seed_hex) - seed = Seed(cfg, seed_bin=seed_bin) + A = shares.get_share_by_idx(share_count).sid + B = shares.get_share_by_seed_id(l).sid + assert A == B == l, A - SeedShareIdx.max_val = ss_count - shares = seed.split(ss_count, master_idx=master_idx) - A = shares.get_share_by_idx(ss_count).sid - B = shares.get_share_by_seed_id(last_sid).sid - assert A == last_sid, A - assert B == last_sid, B + A = shares.join().sid + assert A == b, A - assert shares.nonce_start == 0, shares.nonce_start - - collisions = 0 - for sid in shares.data['long']: - collisions += shares.data['long'][sid][1] - - assert collisions == collisions_chk, collisions - vmsg_r(f'{collisions} collisions, last_sid {last_sid}') - msg('OK') - - def last_share_collisions(): - msg_r('Testing last share collisions with shortened Seed IDs') - vmsg('') - seed_bin = bytes.fromhex('2eadbeef'*8) - seed = Seed(cfg, seed_bin=seed_bin) - ssm_save = SeedShareIdx.max_val - ssm = SeedShareIdx.max_val = 2048 - shares = SeedShareList(seed, count=ssm, id_str='foo', master_idx=1, debug_last_share=True) - lsid = shares.last_share.sid - collisions = shares.data['long'][lsid][1] - assert collisions == 2, collisions - assert lsid == 'B5B8AD09', lsid - SeedShareIdx.max_val = ssm_save - vmsg_r(f'{collisions} collisions, last_share sid {lsid}') - msg('..OK') - - basic_ops(master_idx=None) - basic_ops(master_idx=1) - basic_ops(master_idx=5) - defaults_and_limits() - last_share_collisions() - collisions('1dabcdef'*4, 65535, 'B5CBCE0A', 3, master_idx=None) - collisions('18abcdef'*4, 65535, 'FF03CE82', 3, master_idx=1) + if master_idx: + slist = [shares.get_share_by_idx(i+1, base_seed=True) for i in range(len(shares))] + A = Seed.join_shares(cfg, slist, master_idx=master_idx, id_str=id_str).sid + assert A == b, A return True + +def collisions(seed_hex, ss_count, last_sid, collisions_chk, master_idx): + + seed_bin = bytes.fromhex(seed_hex) + seed = Seed(cfg, seed_bin=seed_bin) + + SeedShareIdx.max_val = ss_count + shares = seed.split(ss_count, master_idx=master_idx) + A = shares.get_share_by_idx(ss_count).sid + B = shares.get_share_by_seed_id(last_sid).sid + assert A == last_sid, A + assert B == last_sid, B + + assert shares.nonce_start == 0, shares.nonce_start + + collisions = 0 + for sid in shares.data['long']: + collisions += shares.data['long'][sid][1] + + assert collisions == collisions_chk, collisions + vmsg(f'{collisions} collisions, last_sid {last_sid}') + + return True + +class unit_tests: + + def ops1(self, name, ut, desc='basic ops (master_idx=None)'): + return basic_ops(master_idx=None) + + def ops2(self, name, ut, desc='basic ops (master_idx=1)'): + return basic_ops(master_idx=1) + + def ops3(self, name, ut, desc='basic ops (master_idx=5)'): + return basic_ops(master_idx=5) + + def limits(self, name, ut, desc='defaults and limits...'): + seed_bin = bytes.fromhex('deadbeef' * 8) + seed = Seed(cfg, seed_bin=seed_bin) + + shares = seed.split(SeedShareIdx.max_val) + s = shares.format() + assert len(s.strip().split('\n')) == 1030, s + + A = shares.get_share_by_idx(1024).sid + B = shares.get_share_by_seed_id('4BA23728').sid + assert A == '4BA23728', A + assert B == '4BA23728', B + + A = shares.join().sid + B = seed.sid + assert A == B, A + + return True + + def collisions1(self, name, ut, desc='last share collisions with shortened Seed IDs'): + seed_bin = bytes.fromhex('2eadbeef'*8) + seed = Seed(cfg, seed_bin=seed_bin) + ssm_save = SeedShareIdx.max_val + ssm = SeedShareIdx.max_val = 2048 + shares = SeedShareList(seed, count=ssm, id_str='foo', master_idx=1, debug_last_share=True) + lsid = shares.last_share.sid + collisions = shares.data['long'][lsid][1] + assert collisions == 2, collisions + assert lsid == 'B5B8AD09', lsid + SeedShareIdx.max_val = ssm_save + vmsg(f'{collisions} collisions, last_share sid {lsid}') + return True + + def collisions2(self, name, ut, desc='Seed ID collisions (65535 seed shares, master_idx=None)'): + return collisions('1dabcdef'*4, 65535, 'B5CBCE0A', 3, master_idx=None) + + def collisions3(self, name, ut, desc='Seed ID collisions (65535 seed shares, master_idx=1)'): + return collisions('18abcdef'*4, 65535, 'FF03CE82', 3, master_idx=1) diff --git a/test/modtest_d/subseed.py b/test/modtest_d/subseed.py index 40875d35..d9948091 100755 --- a/test/modtest_d/subseed.py +++ b/test/modtest_d/subseed.py @@ -1,196 +1,192 @@ #!/usr/bin/env python3 """ -test.modtest_d.subseed: subseed unit test for the MMGen suite +test.modtest_d.subseed: subseed unit tests for the MMGen suite """ -from mmgen.util import msg, msg_r +from mmgen.util import msg +from mmgen.seed import Seed +from mmgen.subseed import SubSeedList, SubSeedIdxRange -from ..include.common import cfg, vmsg_r +from ..include.common import cfg, vmsg -class unit_test: +nSubseeds = SubSeedList.dfl_len - def run_test(self, name, ut): - from mmgen.seed import Seed - from mmgen.subseed import SubSeedList, SubSeedIdxRange +class unit_tests: - nSubseeds = SubSeedList.dfl_len - - def basic_ops(): - msg_r('Testing basic ops...') - for a, b, c, d, e, f, h in ( - (8, '4710FBF0', '0C1B0615', '803B165C', '2669AC64', 256, '10L'), - (6, '9D07ABBD', 'EBA9C33F', '20787E6A', '192E2AA2', 192, '10L'), - (4, '43670520', '04A4CCB3', 'B5F21D7B', 'C1934CFF', 128, '10L'), - ): - - seed_bin = bytes.fromhex('deadbeef' * a) - seed = Seed(cfg, seed_bin=seed_bin) - assert seed.sid == b, seed.sid - - subseed = seed.subseed('2s') - assert subseed.sid == c, subseed.sid - - subseed = seed.subseed('3') - assert subseed.sid == d, subseed.sid - - subseed = seed.subseed_by_seed_id(e) - assert subseed.bitlen == f, subseed.bitlen - assert subseed.sid == e, subseed.sid - assert subseed.idx == 10, subseed.idx - assert subseed.ss_idx == h, subseed.ss_idx - - seed2 = Seed(cfg, seed_bin=seed_bin) - ss2_list = seed2.subseeds - - seed2.subseeds._generate(1) - assert len(ss2_list) == 1, len(ss2_list) - - seed2.subseeds._generate(1) # do nothing - seed2.subseeds._generate(2) # append one item - - seed2.subseeds._generate(5) - assert len(ss2_list) == 5, len(ss2_list) - - seed2.subseeds._generate(3) # do nothing - assert len(ss2_list) == 5, len(ss2_list) - - seed2.subseeds._generate(10) - assert len(ss2_list) == 10, len(ss2_list) - - assert seed.pfmt() == seed2.pfmt() - assert seed.subseeds.pfmt() == seed2.subseeds.pfmt() - - s = seed.subseeds.format(1, nSubseeds) - s_lines = s.strip().split('\n') - assert len(s_lines) == nSubseeds + 4, s - - a = seed.subseed('2L').sid - b = [e for e in s_lines if ' 2L:' in e][0].strip().split()[1] - assert a == b, b - - c = seed.subseed('2').sid - assert c == a, c - - a = seed.subseed('5S').sid - b = [e for e in s_lines if ' 5S:' in e][0].strip().split()[3] - assert a == b, b - - s = seed.subseeds.format(nSubseeds+1, nSubseeds+2) - s_lines = s.strip().split('\n') - assert len(s_lines) == 6, s - - ss_idx = str(nSubseeds+2) + 'S' - a = seed.subseed(ss_idx).sid - b = [e for e in s_lines if f' {ss_idx}:' in e][0].strip().split()[3] - assert a == b, b - - s = seed.subseeds.format(1, 10) - s_lines = s.strip().split('\n') - assert len(s_lines) == 14, s - - vmsg_r(f'\n{s}') - - msg('OK') - - def defaults_and_limits(): - msg_r('Testing defaults and limits...') - - seed_bin = bytes.fromhex('deadbeef' * 8) - - seed = Seed(cfg, seed_bin=seed_bin, nSubseeds=11) - seed.subseeds._generate() - ss = seed.subseeds - assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short']) - assert len(ss) == 11, len(ss) + def ops(self, name, ut, desc='basic ops...'): + for a, b, c, d, e, f, h in ( + (8, '4710FBF0', '0C1B0615', '803B165C', '2669AC64', 256, '10L'), + (6, '9D07ABBD', 'EBA9C33F', '20787E6A', '192E2AA2', 192, '10L'), + (4, '43670520', '04A4CCB3', 'B5F21D7B', 'C1934CFF', 128, '10L'), + ): + seed_bin = bytes.fromhex('deadbeef' * a) seed = Seed(cfg, seed_bin=seed_bin) - seed.subseeds._generate() - ss = seed.subseeds - assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short']) - assert len(ss) == nSubseeds, len(ss) + assert seed.sid == b, seed.sid - seed = Seed(cfg, seed_bin=seed_bin) - seed.subseed_by_seed_id('EEEEEEEE') - ss = seed.subseeds - assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short']) - assert len(ss) == nSubseeds, len(ss) + subseed = seed.subseed('2s') + assert subseed.sid == c, subseed.sid - seed = Seed(cfg, seed_bin=seed_bin) - subseed = seed.subseed_by_seed_id('803B165C') - assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short']) - assert subseed.sid == '803B165C', subseed.sid - assert subseed.idx == 3, subseed.idx + subseed = seed.subseed('3') + assert subseed.sid == d, subseed.sid - seed = Seed(cfg, seed_bin=seed_bin) - subseed = seed.subseed_by_seed_id('803B165C', last_idx=1) - assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short']) - assert subseed is None, subseed + subseed = seed.subseed_by_seed_id(e) + assert subseed.bitlen == f, subseed.bitlen + assert subseed.sid == e, subseed.sid + assert subseed.idx == 10, subseed.idx + assert subseed.ss_idx == h, subseed.ss_idx - r = SubSeedIdxRange('1-5') - r2 = SubSeedIdxRange(1, 5) - assert r2 == r, r2 - assert r == (r.first, r.last), r - assert r.first == 1, r.first - assert r.last == 5, r.last - assert r.items == [1, 2, 3, 4, 5], r.items - assert list(r.iterate()) == r.items, list(r.iterate()) + seed2 = Seed(cfg, seed_bin=seed_bin) + ss2_list = seed2.subseeds - r = SubSeedIdxRange('22') - r2 = SubSeedIdxRange(22, 22) - assert r2 == r, r2 - assert r == (r.first, r.last), r - assert r.first == 22, r.first - assert r.last == 22, r.last - assert r.items == [22], r - assert list(r.iterate()) == r.items, list(r.iterate()) + seed2.subseeds._generate(1) + assert len(ss2_list) == 1, len(ss2_list) - r = SubSeedIdxRange('3-3') - assert r.items == [3], r.items + seed2.subseeds._generate(1) # do nothing + seed2.subseeds._generate(2) # append one item - r = SubSeedIdxRange(f'{nSubseeds-1}-{nSubseeds}') - assert r.items == [nSubseeds-1, nSubseeds], r.items + seed2.subseeds._generate(5) + assert len(ss2_list) == 5, len(ss2_list) - for n, e in enumerate(SubSeedIdxRange('1-5').iterate(), 1): - assert n == e, e + seed2.subseeds._generate(3) # do nothing + assert len(ss2_list) == 5, len(ss2_list) - assert n == 5, n + seed2.subseeds._generate(10) + assert len(ss2_list) == 10, len(ss2_list) - msg('OK') + assert seed.pfmt() == seed2.pfmt() + assert seed.subseeds.pfmt() == seed2.subseeds.pfmt() - def collisions(): - ss_count, ltr, last_sid, collisions_chk = ( - (SubSeedIdxRange.max_idx, 'S', '2788F26B', 470), - (49509, 'L', '8D1FE500', 2) - )[bool(cfg.fast)] + s = seed.subseeds.format(1, nSubseeds) + s_lines = s.strip().split('\n') + assert len(s_lines) == nSubseeds + 4, s - last_idx = str(ss_count) + ltr + a = seed.subseed('2L').sid + b = [e for e in s_lines if ' 2L:' in e][0].strip().split()[1] + assert a == b, b - msg_r(f'Testing Seed ID collisions ({ss_count} subseed pairs)...') + c = seed.subseed('2').sid + assert c == a, c - seed_bin = bytes.fromhex('12abcdef' * 8) # 95B3D78D - seed = Seed(cfg, seed_bin=seed_bin) + a = seed.subseed('5S').sid + b = [e for e in s_lines if ' 5S:' in e][0].strip().split()[3] + assert a == b, b - seed.subseeds._generate(ss_count) - ss = seed.subseeds + s = seed.subseeds.format(nSubseeds+1, nSubseeds+2) + s_lines = s.strip().split('\n') + assert len(s_lines) == 6, s - assert seed.subseed(last_idx).sid == last_sid, seed.subseed(last_idx).sid + ss_idx = str(nSubseeds+2) + 'S' + a = seed.subseed(ss_idx).sid + b = [e for e in s_lines if f' {ss_idx}:' in e][0].strip().split()[3] + assert a == b, b - for sid in ss.data['long']: - # msg(sid) - assert sid not in ss.data['short'] + s = seed.subseeds.format(1, 10) + s_lines = s.strip().split('\n') + assert len(s_lines) == 14, s - collisions = 0 - for k in ('short', 'long'): - for sid in ss.data[k]: - collisions += ss.data[k][sid][1] - - assert collisions == collisions_chk, collisions - vmsg_r(f'\n{collisions} collisions, last_sid {last_sid}') - msg('OK') - - basic_ops() - defaults_and_limits() - collisions() + vmsg(f'{s}') + + return True + + def limits(self, name, ut, desc='limits and ranges'): + + seed_bin = bytes.fromhex('deadbeef' * 8) + + vmsg('Testing limits') + + seed = Seed(cfg, seed_bin=seed_bin, nSubseeds=11) + seed.subseeds._generate() + ss = seed.subseeds + assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short']) + assert len(ss) == 11, len(ss) + + seed = Seed(cfg, seed_bin=seed_bin) + seed.subseeds._generate() + ss = seed.subseeds + assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short']) + assert len(ss) == nSubseeds, len(ss) + + seed = Seed(cfg, seed_bin=seed_bin) + seed.subseed_by_seed_id('EEEEEEEE') + ss = seed.subseeds + assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short']) + assert len(ss) == nSubseeds, len(ss) + + seed = Seed(cfg, seed_bin=seed_bin) + subseed = seed.subseed_by_seed_id('803B165C') + assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short']) + assert subseed.sid == '803B165C', subseed.sid + assert subseed.idx == 3, subseed.idx + + seed = Seed(cfg, seed_bin=seed_bin) + subseed = seed.subseed_by_seed_id('803B165C', last_idx=1) + assert len(ss.data['long']) == len(ss.data['short']), len(ss.data['short']) + assert subseed is None, subseed + + vmsg('Testing SubSeedIdxRange()') + + r = SubSeedIdxRange('1-5') + r2 = SubSeedIdxRange(1, 5) + assert r2 == r, r2 + assert r == (r.first, r.last), r + assert r.first == 1, r.first + assert r.last == 5, r.last + assert r.items == [1, 2, 3, 4, 5], r.items + assert list(r.iterate()) == r.items, list(r.iterate()) + + r = SubSeedIdxRange('22') + r2 = SubSeedIdxRange(22, 22) + assert r2 == r, r2 + assert r == (r.first, r.last), r + assert r.first == 22, r.first + assert r.last == 22, r.last + assert r.items == [22], r + assert list(r.iterate()) == r.items, list(r.iterate()) + + r = SubSeedIdxRange('3-3') + assert r.items == [3], r.items + + r = SubSeedIdxRange(f'{nSubseeds-1}-{nSubseeds}') + assert r.items == [nSubseeds-1, nSubseeds], r.items + + for n, e in enumerate(SubSeedIdxRange('1-5').iterate(), 1): + assert n == e, e + + assert n == 5, n + + return True + + def collisions(self, name, ut, desc='Seed ID collisions'): + + ss_count, ltr, last_sid, collisions_chk, mode_desc = ( + (SubSeedIdxRange.max_idx, 'S', '2788F26B', 470, 'normal'), + (49509, 'L', '8D1FE500', 2, 'fast') + )[bool(cfg.fast)] + + last_idx = str(ss_count) + ltr + + vmsg(f'Using {ss_count} subseed pairs ({mode_desc} mode)') + + seed_bin = bytes.fromhex('12abcdef' * 8) # 95B3D78D + seed = Seed(cfg, seed_bin=seed_bin) + + seed.subseeds._generate(ss_count) + ss = seed.subseeds + + assert seed.subseed(last_idx).sid == last_sid, seed.subseed(last_idx).sid + + for sid in ss.data['long']: + # msg(sid) + assert sid not in ss.data['short'] + + collisions = 0 + for k in ('short', 'long'): + for sid in ss.data[k]: + collisions += ss.data[k][sid][1] + + assert collisions == collisions_chk, collisions + vmsg(f'{collisions} collisions, last_sid {last_sid}') return True