Browse Source

addr.py: support extended MMGen IDs

The MMGen Project 1 month ago
parent
commit
797912283c
3 changed files with 55 additions and 2 deletions
  1. 13 2
      mmgen/addr.py
  2. 1 0
      test/objtest_d/btc_mainnet.py
  3. 41 0
      test/objtest_d/xmr_mainnet.py

+ 13 - 2
mmgen/addr.py

@@ -23,7 +23,7 @@ addr: MMGen address-related types
 from collections import namedtuple
 from collections import namedtuple
 
 
 from .objmethods import HiliteStr, InitErrors, MMGenObject
 from .objmethods import HiliteStr, InitErrors, MMGenObject
-from .obj import ImmutableAttr, MMGenIdx, get_obj
+from .obj import ImmutableAttr, MMGenIdx, Int, get_obj
 from .seed import SeedID
 from .seed import SeedID
 from . import color as color_mod
 from . import color as color_mod
 
 
@@ -93,6 +93,10 @@ class MMGenPasswordType(MMGenAddrType):
 class AddrIdx(MMGenIdx):
 class AddrIdx(MMGenIdx):
 	max_digits = 7
 	max_digits = 7
 
 
+class MoneroIdx(Int):
+	max_digits = 5
+	min_val = 0
+
 def is_addr_idx(s):
 def is_addr_idx(s):
 	return get_obj(AddrIdx, n=s, silent=True, return_bool=True)
 	return get_obj(AddrIdx, n=s, silent=True, return_bool=True)
 
 
@@ -136,7 +140,14 @@ class MMGenID(HiliteStr, InitErrors, MMGenObject):
 					mmtype = proto.dfl_mmtype
 					mmtype = proto.dfl_mmtype
 				case _:
 				case _:
 					raise ValueError('not 2 or 3 colon-separated items')
 					raise ValueError('not 2 or 3 colon-separated items')
-			me = str.__new__(cls, f'{sid}:{mmtype}:{idx}')
+			if '-' in idx: # extended Monero ID
+				assert proto.coin == 'XMR', 'extended MMGen IDs supported for XMR only'
+				assert id_str.count(':') == 2, 'mmtype letter required for extended MMGen IDs'
+				me = str.__new__(cls, id_str)
+				idx, ext = idx.split('-', 1)
+				me.acct_num, me.acct_addr_num = [MoneroIdx(e) for e in ext.split('/', 1)]
+			else:
+				me = str.__new__(cls, f'{sid}:{mmtype}:{idx}')
 			me.sid = SeedID(sid=sid)
 			me.sid = SeedID(sid=sid)
 			me.mmtype = proto.addr_type(mmtype)
 			me.mmtype = proto.addr_type(mmtype)
 			me.idx = AddrIdx(idx)
 			me.idx = AddrIdx(idx)

+ 1 - 0
test/objtest_d/btc_mainnet.py

@@ -176,6 +176,7 @@ tests = {
 			{'id_str': 'x:L:3',         'proto': proto},
 			{'id_str': 'x:L:3',         'proto': proto},
 			{'id_str': 'F00BAA12',      'proto': proto},
 			{'id_str': 'F00BAA12',      'proto': proto},
 			{'id_str': 'F00BAA12:Z:99', 'proto': proto},
 			{'id_str': 'F00BAA12:Z:99', 'proto': proto},
+			{'id_str': 'F00BAA12:B:1-0/0', 'proto': proto},
 		),
 		),
 		'good': (
 		'good': (
 			{'id_str': 'F00BAA12:99',   'proto': proto, 'ret': 'F00BAA12:L:99'},
 			{'id_str': 'F00BAA12:99',   'proto': proto, 'ret': 'F00BAA12:L:99'},

+ 41 - 0
test/objtest_d/xmr_mainnet.py

@@ -0,0 +1,41 @@
+#!/usr/bin/env python3
+#
+# MMGen Wallet, a terminal-based cryptocurrency wallet
+# Copyright (C)2013-2025 The MMGen Project <mmgen@tuta.io>
+
+"""
+test.objtest_d.xmr_mainnet: XMR mainnet test vectors for MMGen data objects
+"""
+
+from mmgen.protocol import init_proto
+
+from mmgen.addr import MMGenID
+
+from ..include.common import cfg
+
+proto = init_proto(cfg, 'xmr', need_amt=True)
+
+tests = {
+	'MMGenID': {
+		'arg1': 'id_str',
+		'bad': (
+			{'id_str': 'F00BAA12',          'proto': proto},
+			{'id_str': 'F00BAA12:C:99',     'proto': proto},
+			{'id_str': 'F00BAA12:M',        'proto': proto},
+			{'id_str': 'F00BAA12:M:',       'proto': proto},
+			{'id_str': 'F00BAA12:M:99-FOO', 'proto': proto},
+			{'id_str': 'F00BAA12:M:99-1',   'proto': proto},
+			{'id_str': 'F00BAA12:M:99-x/y', 'proto': proto},
+			{'id_str': 'F00BAA12:M:99-1/y', 'proto': proto},
+			{'id_str': 'F00BAA12:M:0-1/1',  'proto': proto},
+			{'id_str': 'F00BAA12:M:1-0/-1', 'proto': proto},
+			{'id_str': 'F00BAA12:1-0/0',    'proto': proto},
+			{'id_str': 'F00BAA12:M:1-111111/3', 'proto': proto},
+		),
+		'good': (
+			{'id_str': 'F00BAA12:M:1', 'proto': proto},
+			{'id_str': 'F00BAA12:M:1-0/0', 'proto': proto},
+			{'id_str': 'F00BAA12:M:9999999-99999/99999', 'proto': proto},
+		),
+	},
+}