Browse Source

xmrwallet.py: new XMRWalletAddrSpec class

The MMGen Project 3 years ago
parent
commit
1a11f4d1a4
1 changed files with 75 additions and 35 deletions
  1. 75 35
      mmgen/xmrwallet.py

+ 75 - 35
mmgen/xmrwallet.py

@@ -27,7 +27,7 @@ from .addr import KeyAddrList,AddrIdxList
 from .rpc import MoneroRPCClientRaw, MoneroWalletRPCClient
 from .daemon import MoneroWalletDaemon
 from .protocol import _b58a,init_proto
-from .obj import CoinAddr,CoinTxID,SeedID,AddrIdx
+from .obj import CoinAddr,CoinTxID,SeedID,AddrIdx,Hilite,InitErrors
 
 xmrwallet_uarg_info = (
 	lambda e,hp: {
@@ -40,6 +40,39 @@ xmrwallet_uarg_info = (
 		r'(?:[^:]+):(?:\d+)'
 	)
 
+class XMRWalletAddrSpec(str,Hilite,InitErrors,MMGenObject):
+	color = 'cyan'
+	width = 0
+	trunc_ok = False
+	min_len = 5  # 1:0:0
+	max_len = 14 # 9999:9999:9999
+	def __new__(cls,arg1,arg2=None,arg3=None):
+		if type(arg1) == cls:
+			return arg1
+
+		try:
+			if isinstance(arg1,str):
+				me = str.__new__(cls,arg1)
+				m = re.fullmatch('({n}):({n}):({n}|None)'.format(n=r'[0-9]{1,4}'),arg1)
+				assert m is not None, f'{arg1!r}: invalid XMRWalletAddrSpec'
+				for e in m.groups():
+					if len(e) != 1 and e[0] == '0':
+						die(2,f'{e}: leading zeroes not permitted in XMRWalletAddrSpec element')
+				me.wallet = AddrIdx(m[1])
+				me.account = int(m[2])
+				me.account_address = None if m[3] == 'None' else int(m[3])
+			else:
+				me = str.__new__(cls,f'{arg1}:{arg2}:{arg3}')
+				for arg in [arg1,arg2] + ([] if arg3 is None else [arg3]):
+					assert isinstance(arg,int), f'{arg}: XMRWalletAddrSpec component not of type int'
+					assert arg is None or arg <= 9999, f'{arg}: XMRWalletAddrSpec component greater than 9999'
+				me.wallet = AddrIdx(arg1)
+				me.account = arg2
+				me.account_address = arg3
+			return me
+		except Exception as e:
+			return cls.init_fail(e,me)
+
 class MoneroMMGenTX:
 
 	class Base:
@@ -50,10 +83,8 @@ class MoneroMMGenTX:
 			'sign_time',
 			'network',
 			'seed_id',
-			'source_idx',
-			'source_account',
-			'dest_idx',
-			'dest_account',
+			'source',
+			'dest',
 			'dest_address',
 			'txid',
 			'amount',
@@ -64,10 +95,14 @@ class MoneroMMGenTX:
 
 		def get_info(self,indent=''):
 			d = self.data
-			to_entry = f'\n{indent}  To:   ' + (
-				'Wallet {}, account {}'.format( d.dest_idx.hl(), red(f'#{d.dest_account}') )
-				if d.dest_idx else 'Same account'
-			)
+			if d.dest:
+				to_entry = f'\n{indent}  To:   ' + (
+					'Wallet {}, account {}, address {}'.format(
+						d.dest.wallet.hl(),
+						red(f'#{d.dest.account}'),
+						red(f'#{d.dest.account_address}')
+					)
+				)
 			return fmt("""
 				Transaction info [Seed ID: {}. Network: {}]:
 				  TxID: {}
@@ -80,9 +115,9 @@ class MoneroMMGenTX:
 					d.seed_id.hl(), d.network.upper(),
 					d.txid.hl(),
 					blue(capfirst(d.op)),
-					d.source_idx.hl(),
-					red(f'#{d.source_account}'),
-					'' if d.op == 'transfer' else to_entry,
+					d.source.wallet.hl(),
+					red(f'#{d.source.account}'),
+					to_entry if d.dest else '',
 					d.amount.hl(),
 					d.fee.hl(),
 					d.dest_address.hl()
@@ -101,10 +136,8 @@ class MoneroMMGenTX:
 				sign_time      = now,
 				network        = d.network,
 				seed_id        = SeedID(sid=d.seed_id),
-				source_idx     = AddrIdx(d.source_idx),
-				source_account = d.source_account,
-				dest_idx       = None if d.dest_idx == None else AddrIdx(d.dest_idx),
-				dest_account   = d.dest_account,
+				source         = XMRWalletAddrSpec(d.source),
+				dest           = None if d.dest is None else XMRWalletAddrSpec(d.dest),
 				dest_address   = CoinAddr(proto,d.dest_address),
 				txid           = CoinTxID(d.txid),
 				amount         = proto.coin_amt(d.amount,from_unit='atomic'),
@@ -380,14 +413,17 @@ class MoneroWalletOps:
 				msg(cyan(ret['address']))
 				return ret['address']
 
-			async def get_last_addr(self,account):
-				msg('\n    Getting last address:')
+			async def get_last_addr(self,account,display=True):
+				if display:
+					msg('\n    Getting last address:')
 				ret = (await self.c.call(
 					'get_address',
 					account_index = account,
-				))['addresses'][-1]['address']
-				msg('      ' + cyan(ret))
-				return ret
+				))['addresses']
+				addr = ret[-1]['address']
+				if display:
+					msg('      ' + cyan(addr))
+				return ( addr, len(ret) - 1 )
 
 			async def make_transfer_tx(self,account,addr,amt):
 				res = await self.c.call(
@@ -405,10 +441,8 @@ class MoneroWalletOps:
 					op             = uarg.op,
 					network        = self.parent.proto.network,
 					seed_id        = self.parent.kal.al_id.sid,
-					source_idx     = self.parent.source.idx,
-					source_account = self.parent.account,
-					dest_idx       = None,
-					dest_account   = None,
+					source         = XMRWalletAddrSpec(self.parent.source.idx,self.parent.account,None),
+					dest           = None,
 					dest_address   = addr,
 					txid           = res['tx_hash'],
 					amount         = res['amount'],
@@ -417,7 +451,7 @@ class MoneroWalletOps:
 					metadata       = res['tx_metadata'],
 				)
 
-			async def make_sweep_tx(self,account,dest_acct,addr):
+			async def make_sweep_tx(self,account,dest_acct,dest_addr_idx,addr):
 				res = await self.c.call(
 					'sweep_all',
 					address = addr,
@@ -434,10 +468,11 @@ class MoneroWalletOps:
 					op             = uarg.op,
 					network        = self.parent.proto.network,
 					seed_id        = self.parent.kal.al_id.sid,
-					source_idx     = self.parent.source.idx,
-					source_account = self.parent.account,
-					dest_idx       = self.parent.dest.idx if self.parent.dest else None,
-					dest_account   = dest_acct,
+					source         = XMRWalletAddrSpec(self.parent.source.idx,self.parent.account,None),
+					dest           = XMRWalletAddrSpec(
+										(self.parent.dest or self.parent.source).idx,
+										dest_acct,
+										dest_addr_idx),
 					dest_address   = addr,
 					txid           = res['tx_hash_list'][0],
 					amount         = res['amount_list'][0],
@@ -659,13 +694,15 @@ class MoneroWalletOps:
 			if self.name == 'transfer':
 				dest_addr = self.dest_addr
 			elif self.dest == None:
-				dest_acct = None
+				dest_acct = self.account
 				if keypress_confirm(f'\nCreate new address for account #{self.account}?'):
-					dest_addr = await h.create_new_addr(self.account)
+					dest_addr_chk = await h.create_new_addr(self.account)
 				elif keypress_confirm(f'Sweep to last existing address of account #{self.account}?'):
-					dest_addr = await h.get_last_addr(self.account)
+					dest_addr_chk = None
 				else:
 					die(1,'Exiting at user request')
+				dest_addr,dest_addr_idx = await h.get_last_addr(self.account,display=not dest_addr_chk)
+				assert dest_addr_chk in (None,dest_addr), 'dest_addr_chk1'
 				await h.print_addrs(accts_data,self.account)
 			else:
 				await h.close_wallet('source')
@@ -676,9 +713,12 @@ class MoneroWalletOps:
 
 				if keypress_confirm(f'\nCreate new account for wallet {bn!r}?'):
 					dest_acct,dest_addr = await h2.create_acct()
+					dest_addr_idx = 0
 					await h2.get_accts()
 				elif keypress_confirm(f'Sweep to last existing account of wallet {bn!r}?'):
-					dest_acct,dest_addr = h2.get_last_acct(accts_data)
+					dest_acct,dest_addr_chk = h2.get_last_acct(accts_data)
+					dest_addr,dest_addr_idx = await h2.get_last_addr(dest_acct,display=False)
+					assert dest_addr_chk == dest_addr, 'dest_addr_chk2'
 				else:
 					die(1,'Exiting at user request')
 
@@ -690,7 +730,7 @@ class MoneroWalletOps:
 			if self.name == 'transfer':
 				new_tx = await h.make_transfer_tx(self.account,dest_addr,self.amount)
 			elif self.name == 'sweep':
-				new_tx = await h.make_sweep_tx(self.account,dest_acct,dest_addr)
+				new_tx = await h.make_sweep_tx(self.account,dest_acct,dest_addr_idx,dest_addr)
 
 			msg('\n' + new_tx.get_info(indent='    '))