Browse Source

xmrwallet.py: new MMGenMoneroTX class

The MMGen Project 3 years ago
parent
commit
0bfb03105f
1 changed files with 127 additions and 38 deletions
  1. 127 38
      mmgen/xmrwallet.py

+ 127 - 38
mmgen/xmrwallet.py

@@ -20,13 +20,14 @@
 xmrwallet.py - MoneroWalletOps class
 """
 
-import os,re
+import os,re,time
 from collections import namedtuple
 from .common import *
 from .addr import KeyAddrList,AddrIdxList
 from .rpc import MoneroRPCClientRaw, MoneroWalletRPCClient
 from .daemon import MoneroWalletDaemon
-from .protocol import _b58a
+from .protocol import _b58a,init_proto
+from .obj import CoinAddr,CoinTxID,SeedID,AddrIdx
 
 xmrwallet_uarg_info = (
 	lambda e,hp: {
@@ -39,6 +40,75 @@ xmrwallet_uarg_info = (
 		r'(?:[^:]+):(?:\d+)'
 	)
 
+class MMGenMoneroTX:
+
+	class Base:
+
+		xmrwallet_tx_data = namedtuple('xmrwallet_tx_data',[
+			'op',
+			'time',
+			'network',
+			'seed_id',
+			'source_idx',
+			'source_account',
+			'dest_idx',
+			'dest_address',
+			'txid',
+			'amount',
+			'fee',
+			'blob',
+			'metadata',
+		])
+
+		def get_info(self,indent=''):
+			d = self.data
+			return fmt("""
+				Transaction info [Seed ID: {}. Network: {}]:
+				  TxID: {}
+				  Type: {}
+				  From: Wallet {}, account {}{}
+				  Amt:  {} XMR
+				  Fee:  {} XMR
+				  Dest: {}
+			""",strip_char='\t',indent=indent).format(
+					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 (
+							f'\n{indent}  To:   ' +
+							(f'Wallet {d.dest_idx.hl()}, new account' if d.dest_idx else 'Same account')
+						)
+					),
+					d.amount.hl(),
+					d.fee.hl(),
+					d.dest_address.hl()
+				)
+
+	class New(Base):
+
+		def __init__(self,*args,**kwargs):
+			assert not args, 'Non-keyword args not permitted'
+			d = self.xmrwallet_tx_data(**kwargs)
+			proto = init_proto('xmr',network=d.network)
+			self.data = self.xmrwallet_tx_data(
+				op             = d.op,
+				time           = int(d.time),
+				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_address   = CoinAddr(proto,d.dest_address),
+				txid           = CoinTxID(d.txid),
+				amount         = proto.coin_amt(d.amount,from_unit='atomic'),
+				fee            = proto.coin_amt(d.fee,from_unit='atomic'),
+				blob           = d.blob,
+				metadata       = d.metadata,
+			)
+
 class MoneroWalletOps:
 
 	ops = ('create','sync','transfer','sweep')
@@ -86,7 +156,6 @@ class MoneroWalletOps:
 					cls.check_uopts(self)
 					id_cur = id(cls.check_uopts)
 
-			from .protocol import init_proto
 			self.proto = init_proto('xmr',testnet=g.testnet)
 
 		def check_uopts(self):
@@ -111,8 +180,18 @@ class MoneroWalletOps:
 				if getattr(uopt,opt):
 					check_pat_opt(opt)
 
+		def display_tx_relay_info(self,indent=''):
+			m = re.fullmatch(uarg_info['tx_relay_daemon'].pat,uopt.tx_relay_daemon,re.ASCII)
+			msg(fmt(f"""
+				TX relay info:
+				  Host:  {blue(m[1])}
+				  Proxy: {blue(m[2] or 'None')}
+				""",strip_char='\t',indent=indent))
+
 		def post_main(self): pass
 
+		def stop_daemons(self): pass
+
 	class wallet(base):
 
 		opts = (
@@ -176,7 +255,7 @@ class MoneroWalletOps:
 		def stop_daemons(self):
 			if not uopt.no_stop_wallet_daemon:
 				self.wd.stop()
-				if uopt.tx_relay_daemon:
+				if uopt.tx_relay_daemon and hasattr(self,'wd2'):
 					self.wd2.stop()
 
 		def get_wallet_fn(self,d):
@@ -307,20 +386,6 @@ class MoneroWalletOps:
 				msg('      ' + cyan(ret))
 				return ret
 
-			def display_tx_relay_info(self):
-				msg('\n    TX relay host: {}\n    Proxy:         {}'.format(
-					blue(self.parent.wd2.daemon_addr),
-					blue(self.parent.wd2.proxy or 'None')
-				))
-
-			def display_tx(self,txid,amt,fee):
-				from .obj import CoinTxID
-				msg('    TxID:   {}\n    Amount: {}\n    Fee:    {}'.format(
-					CoinTxID(txid).hl(),
-					hl_amt(amt),
-					hl_amt(fee),
-				))
-
 			async def make_transfer_tx(self,account,addr,amt):
 				res = await self.c.call(
 					'transfer',
@@ -330,10 +395,24 @@ class MoneroWalletOps:
 						'address': addr
 					}],
 					do_not_relay = True,
+					get_tx_hex = True,
 					get_tx_metadata = True
 				)
-				self.display_tx( res['tx_hash'], res['amount'], res['fee'] )
-				return res['tx_metadata']
+				return MMGenMoneroTX.New(
+					op             = uarg.op,
+					time           = time.time(),
+					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_address   = addr,
+					txid           = res['tx_hash'],
+					amount         = res['amount'],
+					fee            = res['fee'],
+					blob           = res['tx_blob'],
+					metadata       = res['tx_metadata'],
+				)
 
 			async def make_sweep_tx(self,account,addr):
 				res = await self.c.call(
@@ -341,25 +420,35 @@ class MoneroWalletOps:
 					address = addr,
 					account_index = account,
 					do_not_relay = True,
+					get_tx_hex = True,
 					get_tx_metadata = True
 				)
 
 				if len(res['tx_hash_list']) > 1:
 					die(3,'More than one TX required.  Cannot perform this sweep')
 
-				self.display_tx( res['tx_hash_list'][0], res['amount_list'][0], res['fee_list'][0] )
-				return res['tx_metadata_list'][0]
-
-			def display_txid(self,data):
-				from .obj import CoinTxID
-				msg('\n    Relayed {}'.format( CoinTxID(data['tx_hash']).hl() ))
+				return MMGenMoneroTX.New(
+					op             = uarg.op,
+					time           = time.time(),
+					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_address   = addr,
+					txid           = res['tx_hash_list'][0],
+					amount         = res['amount_list'][0],
+					fee            = res['fee_list'][0],
+					blob           = res['tx_blob_list'][0],
+					metadata       = res['tx_metadata_list'][0],
+				)
 
 			async def relay_tx(self,tx_hex):
 				ret = await self.c.call('relay_tx',hex=tx_hex)
 				try:
-					self.display_txid(ret)
+					msg('\n    Relayed {}'.format( CoinTxID(ret['tx_hash']).hl() ))
 				except:
-					msg('\n'+str(ret))
+					msg(f'\n   Server returned: {ret!s}')
 
 	class create(wallet):
 		name    = 'create'
@@ -406,7 +495,6 @@ class MoneroWalletOps:
 			chain_height = (await self.dc.call('get_height'))['height']
 			msg(f'  Chain height: {chain_height}')
 
-			import time
 			t_start = time.time()
 
 			msg_r('  Opening wallet...')
@@ -519,7 +607,6 @@ class MoneroWalletOps:
 			self.account = int(m[2])
 
 			if self.name == 'transfer':
-				from mmgen.obj import CoinAddr
 				self.dest_addr = CoinAddr(self.proto,m[3])
 				self.amount = self.proto.coin_amt(m[4])
 
@@ -593,16 +680,19 @@ class MoneroWalletOps:
 				await h2.close_wallet('destination')
 				await h.open_wallet('source',refresh=False)
 
-			msg_r(f'\n    Creating {self.name} transaction: wallet {self.source.idx}, account #{self.account}')
+			msg(f'\n    Creating {self.name} transaction...')
 
 			if self.name == 'transfer':
-				msg(f', {self.amount} XMR => {cyan(new_addr)}')
-				tx_metadata = await h.make_transfer_tx(self.account,new_addr,self.amount)
+				new_tx = await h.make_transfer_tx(self.account,new_addr,self.amount)
 			elif self.name == 'sweep':
-				msg(f' => {cyan(new_addr)}')
-				tx_metadata = await h.make_sweep_tx(self.account,new_addr)
+				new_tx = await h.make_sweep_tx(self.account,new_addr)
+
+			msg('\n' + new_tx.get_info(indent='    '))
+
+			if uopt.tx_relay_daemon:
+				self.display_tx_relay_info(indent='    ')
 
-			if keypress_confirm(f'\nRelay {self.name} transaction?'):
+			if keypress_confirm(f'Relay {self.name} transaction?'):
 				w_desc = 'source'
 				if uopt.tx_relay_daemon:
 					await h.close_wallet('source')
@@ -611,9 +701,8 @@ class MoneroWalletOps:
 					h = self.rpc(self,self.source)
 					w_desc = 'TX relay source'
 					await h.open_wallet(w_desc,refresh=False)
-					h.display_tx_relay_info()
 				msg_r(f'\n    Relaying {self.name} transaction...')
-				await h.relay_tx(tx_metadata)
+				await h.relay_tx(new_tx.data.metadata)
 				await h.close_wallet(w_desc)
 
 				gmsg('\n\nAll done')