From d49c8627aa1dd8f7b3219c9d4716be3637960cb5 Mon Sep 17 00:00:00 2001 From: MMGen Date: Fri, 9 Mar 2018 15:16:20 +0000 Subject: [PATCH] Support 40-character UTF-8 tracking wallet comments --- mmgen/obj.py | 7 +++---- mmgen/rpc.py | 15 +++++---------- mmgen/tool.py | 4 ++-- mmgen/tw.py | 20 ++++++++++---------- mmgen/tx.py | 2 +- test/test.py | 16 ++++++++++------ 6 files changed, 31 insertions(+), 33 deletions(-) diff --git a/mmgen/obj.py b/mmgen/obj.py index c3ca0a30..f2ab48ff 100755 --- a/mmgen/obj.py +++ b/mmgen/obj.py @@ -494,7 +494,7 @@ class TwMMGenID(str,Hilite,InitErrors,MMGenObject): return me # contains TwMMGenID,TwComment. Not for display -class TwLabel(str,InitErrors,MMGenObject): +class TwLabel(unicode,InitErrors,MMGenObject): def __new__(cls,s,on_fail='die'): if type(s) == cls: return s cls.arg_chk(cls,on_fail) @@ -502,7 +502,7 @@ class TwLabel(str,InitErrors,MMGenObject): ss = s.split(None,1) mmid = TwMMGenID(ss[0],on_fail='raise') comment = TwComment(ss[1] if len(ss) == 2 else '',on_fail='raise') - me = str.__new__(cls,'{}{}'.format(mmid,' {}'.format(comment) if comment else '')) + me = unicode.__new__(cls,u'{}{}'.format(mmid,u' {}'.format(comment) if comment else '')) me.mmid = mmid me.comment = comment return me @@ -671,8 +671,7 @@ class MMGenWalletLabel(MMGenLabel): desc = 'wallet label' class TwComment(MMGenLabel): - max_len = 32 - allowed = map(unichr,range(32,127)) + max_len = 40 desc = 'tracking wallet comment' class MMGenTXLabel(MMGenLabel): diff --git a/mmgen/rpc.py b/mmgen/rpc.py index 2d910a22..d944cc44 100755 --- a/mmgen/rpc.py +++ b/mmgen/rpc.py @@ -103,20 +103,16 @@ class CoinDaemonRPCConnection(object): dmsg_rpc('=== request() debug ===') dmsg_rpc(' RPC POST data ==> {}\n'.format(p)) - caller = self + class MyJSONEncoder(json.JSONEncoder): def default(self, obj): if isinstance(obj,g.proto.coin_amt): return g.proto.get_rpc_coin_amt_type()(obj) return json.JSONEncoder.default(self, obj) - # TODO: UTF-8 labels - # if type(p) != list and p['method'] == 'importaddress': - # dump = json.dumps(p,cls=MyJSONEncoder,ensure_ascii=False) - # print(dump) - dmsg_rpc(' RPC AUTHORIZATION data ==> raw: [{}]\n{}enc: [Basic {}]\n'.format( self.auth_str,' '*31,base64.b64encode(self.auth_str))) + try: hc.request('POST', '/', json.dumps(p,cls=MyJSONEncoder), { 'Host': self.host, @@ -146,15 +142,14 @@ class CoinDaemonRPCConnection(object): e2 = str(e1) return do_fail(r,1,e2) - r2 = r.read() + r2 = r.read().decode('utf8') - dmsg_rpc(' RPC REPLY data ==> {}\n'.format(r2)) + dmsg_rpc(u' RPC REPLY data ==> {}\n'.format(r2)) if not r2: return do_fail(r,2,'Error: empty reply') -# from decimal import Decimal - r3 = json.loads(r2.decode('utf8'), parse_float=Decimal) + r3 = json.loads(r2,parse_float=Decimal) ret = [] for resp in r3 if cf['batch'] else [r3]: diff --git a/mmgen/tool.py b/mmgen/tool.py index 2ef64ba2..2df4c744 100755 --- a/mmgen/tool.py +++ b/mmgen/tool.py @@ -725,7 +725,7 @@ def Listaddresses(addrs='',minconf=1, out = ([],[green('Chain: {}'.format(g.chain.upper()))])[g.chain in ('testnet','regtest')] - fs = '{{mid}}{} {{cmt}} {{amt}}{}'.format(('',' {addr}')[showbtcaddrs],('',' {age}')[show_age]) + fs = u'{{mid}}{} {{cmt}} {{amt}}{}'.format(('',' {addr}')[showbtcaddrs],('',' {age}')[show_age]) mmaddrs = [k for k in addrs.keys() if k.type == 'mmgen'] max_mmid_len = max(len(k) for k in mmaddrs) + 2 if mmaddrs else 10 max_cmt_len = max(max(len(v['lbl'].comment) for v in addrs.values()),7) @@ -835,6 +835,6 @@ def Twview(pager=False,reverse=False,wide=False,minconf=1,sort='age',show_days=T def Add_label(mmaddr_or_coin_addr,label): rpc_init() from mmgen.tw import MMGenTrackingWallet - MMGenTrackingWallet.add_label(mmaddr_or_coin_addr,label) # dies on failure + MMGenTrackingWallet.add_label(mmaddr_or_coin_addr,label,on_fail='raise') def Remove_label(mmaddr_or_coin_addr): Add_label(mmaddr_or_coin_addr,'') diff --git a/mmgen/tw.py b/mmgen/tw.py index fe31dff8..7ae7e470 100755 --- a/mmgen/tw.py +++ b/mmgen/tw.py @@ -162,7 +162,7 @@ watch-only wallet using '{}-addrimport' and then re-run this program. out = [hdr_fmt.format(' '.join(self.sort_info()),g.coin,self.total.hl())] if g.chain in ('testnet','regtest'): out += [green('Chain: {}'.format(g.chain.upper()))] - fs = ' {:%s} {:%s} {:2} {} {} {:<}' % (col1_w,tx_w) + fs = u' {:%s} {:%s} {:2} {} {} {:<}' % (col1_w,tx_w) out += [fs.format('Num', 'TXid'.ljust(tx_w - 5) + ' Vout', '', 'Address'.ljust(addr_w), @@ -175,10 +175,10 @@ watch-only wallet using '{}-addrimport' and then re-run this program. else i.twmmid if i.twmmid.type=='mmgen' else 'Non-{}'.format(g.proj_name),width=mmid_w,color=True) if self.show_mmid: - addr_out = '{} {}'.format( + addr_out = u'{} {}'.format( type(i.addr).fmtc(addr_dots,width=btaddr_w,color=True) if i.skip == 'addr' \ else i.addr.fmt(width=btaddr_w,color=True), - '{} {}'.format(mmid_disp,i.label.fmt(width=label_w,color=True) \ + u'{} {}'.format(mmid_disp,i.label.fmt(width=label_w,color=True) \ if label_w > 0 else '')) else: addr_out = type(i.addr).fmtc(addr_dots,width=addr_w,color=True) \ @@ -280,10 +280,10 @@ Display options: show [D]ays, [g]roup, show [m]mgen addr, r[e]draw screen idx,lbl = self.get_idx_and_label_from_user() if idx: e = self.unspent[idx-1] - if type(self).add_label(e.twmmid,lbl,addr=e.addr): + if type(self).add_label(e.twmmid,lbl.decode('utf8'),addr=e.addr): self.get_unspent_data() self.do_sort() - msg('{}\n{}\n{}'.format(self.fmt_display,prompt,p)) + msg(u'{}\n{}\n{}'.format(self.fmt_display,prompt,p)) else: msg('Label could not be added\n{}\n{}'.format(prompt,p)) elif reply == 'M': self.do_sort('twmmid'); self.show_mmid = True @@ -314,7 +314,7 @@ Display options: show [D]ays, [g]roup, show [m]mgen addr, r[e]draw screen # returns on failure @classmethod - def add_label(cls,arg1,label='',addr=None,silent=False): + def add_label(cls,arg1,label='',addr=None,silent=False,on_fail='return'): from mmgen.tx import is_mmgen_id,is_coin_addr mmaddr,coinaddr = None,None if is_coin_addr(addr or arg1): @@ -345,10 +345,10 @@ Display options: show [D]ays, [g]roup, show [m]mgen addr, r[e]draw screen mmaddr = TwMMGenID(mmaddr) - cmt = TwComment(label,on_fail='return') + cmt = TwComment(label,on_fail=on_fail) if cmt in (False,None): return False - lbl = TwLabel(mmaddr + ('',' '+cmt)[bool(cmt)]) # label is ASCII for now + lbl = TwLabel(mmaddr + ('',' '+cmt)[bool(cmt)],on_fail=on_fail) # NOTE: this works because importaddress() removes the old account before # associating the new account with the address. @@ -366,8 +366,8 @@ Display options: show [D]ays, [g]roup, show [m]mgen addr, r[e]draw screen m = mmaddr.type.replace('mmg','MMG') a = mmaddr.replace(g.proto.base_coin.lower()+':','') s = '{} address {} in tracking wallet'.format(m,a) - if label: msg("Added label '{}' to {}".format(label,s)) - else: msg('Removed label from {}'.format(s)) + if label: msg(u"Added label '{}' to {}".format(label,s)) + else: msg(u'Removed label from {}'.format(s)) return True @classmethod diff --git a/mmgen/tx.py b/mmgen/tx.py index 4258132b..e504413d 100755 --- a/mmgen/tx.py +++ b/mmgen/tx.py @@ -949,7 +949,7 @@ class MMGenTX(MMGenObject): ('','confirmations:','{} (around {} days)'.format(confs,days) if blockcount!=None else '') ] if ip else icommon + [ ('','change:',green('True') if e.is_chg else '')] - io_out += '\n'.join([('{:>3} {:<8} {}'.format(*d)) for d in items if d[2]]) + '\n\n' + io_out += '\n'.join([(u'{:>3} {:<8} {}'.format(*d)) for d in items if d[2]]) + '\n\n' return io_out hdr_fs = ( diff --git a/test/test.py b/test/test.py index b2de11d6..f42be974 100755 --- a/test/test.py +++ b/test/test.py @@ -2713,9 +2713,9 @@ class MMGenTestSuite(object): sid = self.regtest_user_sid('alice') return self.regtest_user_remove_label(name,'alice',sid+':C:1') - def regtest_user_chk_label(self,name,user,addr,label): + def regtest_user_chk_label(self,name,user,addr,label,label_pat=None): t = MMGenExpect(name,'mmgen-tool',['--'+user,'listaddresses','all_labels=1']) - t.expect('{}\s+\S{{30}}\S+\s+{}\s+'.format(addr,label),regex=True) + t.expect(ur'{}\s+\S{{30}}\S+\s+{}\s+'.format(addr,label_pat or label),regex=True) t.ok() def regtest_alice_chk_label1(self,name): @@ -2726,9 +2726,16 @@ class MMGenTestSuite(object): sid = self.regtest_user_sid('alice') return self.regtest_user_chk_label(name,'alice',sid+':C:1','Replacement Label') + utf8_label = u'Edited label (40 characters, UTF8) α-β-γ' + utf8_label_pat = ur'Edited label \(40 characters, UTF8\) ..-..-..' + + def regtest_alice_edit_label1(self,name): + return self.regtest_user_edit_label(name,'alice','1',self.utf8_label) + def regtest_alice_chk_label3(self,name): sid = self.regtest_user_sid('alice') - return self.regtest_user_chk_label(name,'alice',sid+':C:1','Edited Label') + return self.regtest_user_chk_label(name,'alice',sid+':C:1',self.utf8_label, + label_pat=self.utf8_label_pat) def regtest_alice_chk_label4(self,name): sid = self.regtest_user_sid('alice') @@ -2743,9 +2750,6 @@ class MMGenTestSuite(object): t.expect(r"'q'=quit view, .*?:.",'q',regex=True) t.ok() - def regtest_alice_edit_label1(self,name): - return self.regtest_user_edit_label(name,'alice','1','Edited Label') - def regtest_stop(self,name): t = MMGenExpect(name,'mmgen-regtest',['stop']) t.ok()