Browse Source

mmgen-xmrwallet: new `label` operation

The MMGen Project 2 years ago
parent
commit
333cbcf3ac
4 changed files with 105 additions and 3 deletions
  1. 1 1
      mmgen/data/version
  2. 13 1
      mmgen/main_xmrwallet.py
  3. 68 1
      mmgen/xmrwallet.py
  4. 23 0
      test/test_py_d/ts_xmrwallet.py

+ 1 - 1
mmgen/data/version

@@ -1 +1 @@
-13.3.dev39
+13.3.dev40

+ 13 - 1
mmgen/main_xmrwallet.py

@@ -32,6 +32,7 @@ opts_data = {
 			'[opts] create   <xmr_keyaddrfile> [wallets]',
 			'[opts] sync     <xmr_keyaddrfile> [wallets]',
 			'[opts] list     <xmr_keyaddrfile> [wallets]',
+			'[opts] label    <xmr_keyaddrfile> LABEL_SPEC',
 			'[opts] new      <xmr_keyaddrfile> NEW_ADDRESS_SPEC',
 			'[opts] transfer <xmr_keyaddrfile> TRANSFER_SPEC',
 			'[opts] sweep    <xmr_keyaddrfile> SWEEP_SPEC',
@@ -80,6 +81,7 @@ plain HTTP is not supported.
 create    - create wallet for all or specified addresses in key-address file
 sync      - sync wallet for all or specified addresses in key-address file
 list      - same as 'sync', but also list detailed address info for accounts
+label     - set a label for an address
 new       - create a new account in a wallet, or a new address in an account
 transfer  - transfer specified XMR amount from specified wallet:account to
             specified address
@@ -99,6 +101,16 @@ in the specified key-address file, each corresponding to a Monero wallet
 to be created, synced or listed.  If omitted, all wallets are operated upon.
 
 
+                          'LABEL' OPERATION NOTES
+
+This operation takes a LABEL_SPEC arg with the following format:
+
+    WALLET:ACCOUNT:ADDRESS,"label text"
+
+where WALLET is a wallet number, ACCOUNT an account index, and ADDRESS an
+address index.
+
+
                            'NEW' OPERATION NOTES
 
 This operation takes a NEW_ADDRESS_SPEC arg with the following format:
@@ -231,7 +243,7 @@ elif op in ('create','sync','list'):
 		opts.usage()
 	if cmd_args:
 		wallets = cmd_args[0]
-elif op in ('new','transfer','sweep'):
+elif op in ('new','transfer','sweep','label'):
 	if len(cmd_args) != 1:
 		opts.usage()
 	spec = cmd_args[0]

+ 68 - 1
mmgen/xmrwallet.py

@@ -42,6 +42,7 @@ xmrwallet_uarg_info = (
 		'newaddr_spec':    e('WALLET_NUM[:ACCOUNT][,"label text"]', rf'(\d+)(?::(\d+))?(?:,(.*))?'),
 		'transfer_spec':   e('SOURCE_WALLET_NUM:ACCOUNT:ADDRESS,AMOUNT', rf'(\d+):(\d+):([{b58a}]+),([0-9.]+)'),
 		'sweep_spec':      e('SOURCE_WALLET_NUM:ACCOUNT[,DEST_WALLET_NUM]', r'(\d+):(\d+)(?:,(\d+))?'),
+		'label_spec':      e('WALLET_NUM:ACCOUNT:ADDRESS,"label text"', rf'(\d+):(\d+):(\d+),(.*)'),
 	})(
 		namedtuple('uarg_info_entry',['annot','pat']),
 		r'(?:[^:]+):(?:\d+)'
@@ -237,7 +238,7 @@ class MoneroMMGenTX:
 
 class MoneroWalletOps:
 
-	ops = ('create','sync','list','new','transfer','sweep','relay','txview')
+	ops = ('create','sync','list','new','transfer','sweep','relay','txview','label')
 	opts = (
 		'wallet_dir',
 		'daemon',
@@ -508,6 +509,7 @@ class MoneroWalletOps:
 						e['label'],
 						e['used']
 					))
+				return ret
 
 			def create_new_addr(self,account,label=None):
 				msg_r('\n    Creating new address: ')
@@ -531,6 +533,13 @@ class MoneroWalletOps:
 					msg('      ' + cyan(addr))
 				return ( addr, len(ret) - 1 )
 
+			def set_label(self,account,address_idx,label):
+				return self.c.call(
+					'label_address',
+					index = { 'major': account, 'minor': address_idx },
+					label = label
+				)
+
 			def make_transfer_tx(self,account,addr,amt):
 				res = self.c.call(
 					'transfer',
@@ -788,6 +797,9 @@ class MoneroWalletOps:
 				self.amount = self.proto.coin_amt(m[4])
 			elif self.name == 'new':
 				self.label = strip_quotes(m[3])
+			elif self.name == 'label':
+				self.address_idx = int(m[3])
+				self.label = strip_quotes(m[4])
 
 	class sweep(spec):
 		name     = 'sweep'
@@ -933,6 +945,61 @@ class MoneroWalletOps:
 
 			msg('')
 
+	class label(spec):
+		name     = 'label'
+		spec_id  = 'label_spec'
+		spec_key = ( (1,'source'), )
+		opts     = ()
+
+		async def main(self):
+
+			gmsg('\n{} label for wallet {}, account #{}, address #{}'.format(
+				'Setting' if self.label else 'Removing',
+				self.source.idx,
+				self.account,
+				self.address_idx
+			))
+			h = self.rpc(self,self.source)
+
+			h.open_wallet('source')
+			accts_data = h.get_accts()[0]
+
+			max_acct = len(accts_data['subaddress_accounts']) - 1
+			if self.account > max_acct:
+				die(1,f'{self.account}: requested account index out of bounds (>{max_acct})')
+
+			ret = h.print_addrs(accts_data,self.account)
+
+			if self.address_idx > len(ret['addresses']) - 1:
+				die(1,'{}: requested address index out of bounds (>{})'.format(
+					self.account,
+					len(ret['addresses']) - 1 ))
+
+			addr = ret['addresses'][self.address_idx]
+
+			msg('\n  {} {}\n  {} {}\n  {} {}'.format(
+					'Address:       ',
+					cyan(addr['address'][:15] + '...'),
+					'Existing label:',
+					pink(addr['label']) if addr['label'] else '[none]',
+					'New label:     ',
+					pink(self.label) if self.label else '[none]' ))
+
+			if addr['label'] == self.label:
+				ymsg('\nLabel is unchanged, operation cancelled')
+			elif keypress_confirm('  {} label?'.format('Set' if self.label else 'Remove')):
+				h.set_label( self.account, self.address_idx, self.label )
+				accts_data = h.get_accts(print=False)[0]
+				ret = h.print_addrs(accts_data,self.account)
+				new_label = ret['addresses'][self.address_idx]['label']
+				if new_label != self.label:
+					ymsg(f'Warning: new label {new_label!r} does not match requested value!')
+					return False
+				else:
+					msg(cyan('\nLabel successfully {}'.format('set' if self.label else 'removed')))
+			else:
+				ymsg('\nOperation cancelled by user request')
+
 	class relay(base):
 		name = 'relay'
 		opts = ('tx_relay_daemon',)

+ 23 - 0
test/test_py_d/ts_xmrwallet.py

@@ -49,6 +49,7 @@ class TestSuiteXMRWallet(TestSuiteBase):
 		('daemon_version',            'checking daemon version'),
 		('gen_kafiles',               'generating key-address files'),
 		('create_wallets_miner',      'creating Monero wallets (Miner)'),
+		('set_label_miner',           'setting an address label (Miner, primary account)'),
 		('mine_initial_coins',        'mining initial coins'),
 		('create_wallets_alice',      'creating Monero wallets (Alice)'),
 		('fund_alice',                'sending funds'),
@@ -58,6 +59,8 @@ class TestSuiteXMRWallet(TestSuiteBase):
 		('new_account_alice_label',   'creating a new account (Alice, with label)'),
 		('new_address_alice',         'creating a new address (Alice)'),
 		('new_address_alice_label',   'creating a new address (Alice, with label)'),
+		('remove_label_alice',        'removing an address label (Alice, subaddress)'),
+		('set_label_alice',           'setting an address label (Alice, subaddress)'),
 		('sync_wallets_selected',     'syncing selected wallets'),
 
 		('sweep_to_address_proxy',    'sweeping to new address (via TX relay + proxy)'),
@@ -374,6 +377,26 @@ class TestSuiteXMRWallet(TestSuiteBase):
 			random_txs = self.dfl_random_txs
 		)
 
+	def set_label_miner(self):
+		return self.set_label_user( 'miner', '1:0:0,"Miner’s new primary account label [1:0:0]"' )
+
+	def remove_label_alice(self):
+		return self.set_label_user( 'alice', '4:2:2,""' )
+
+	def set_label_alice(self):
+		return self.set_label_user( 'alice', '4:2:2,"Alice’s new subaddress label [4:2:2]"' )
+
+	def set_label_user(self,user,label_spec):
+		data = self.users[user]
+		cmd_opts = [f'--wallet-dir={data.udir}', f'--daemon=localhost:{data.md.rpc_port}']
+		t = self.spawn(
+			'mmgen-xmrwallet',
+			self.extra_opts + cmd_opts + ['label', data.kafile, label_spec]
+		)
+		t.expect('(y/N): ','y')
+		t.expect(['successfully set','successfully removed'])
+		return t
+
 	def sync_wallets_all(self):
 		return self.sync_wallets('alice',add_opts=['--rescan-blockchain'])