mmgen-txcreate: warn user if change address is a used address

This commit is contained in:
The MMGen Project 2022-11-16 17:56:04 +00:00
commit 68caeb3130
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
5 changed files with 59 additions and 8 deletions

View file

@ -242,6 +242,13 @@ class TwAddresses(TwView):
def dump_fn_pfx(self):
return 'listaddresses' + (f'-minconf-{self.minconf}' if self.minconf else '')
def is_used(self,coinaddr):
for e in self.data:
if e.addr == coinaddr:
return bool(e.recvd)
else: # addr not in tracking wallet
return None
class action(TwView.action):
def s_amt(self,parent):

View file

@ -15,7 +15,7 @@ tx.new: new transaction class
from ..globalvars import *
from ..opts import opt
from .base import Base
from ..color import pink
from ..color import pink,yellow
from ..obj import get_obj,MMGenList
from ..util import msg,qmsg,fmt,die,suf,remove_dups,get_extension
from ..addr import is_mmgen_id,CoinAddr,is_coin_addr
@ -235,6 +235,25 @@ class New(Base):
self.add_mmaddrs_to_outputs(ad_w,ad_f)
self.check_dup_addrs('outputs')
chg_idx = self.get_chg_output_idx()
if chg_idx is not None:
await self.warn_chg_addr_used(self.outputs[chg_idx])
async def warn_chg_addr_used(self,chg):
from ..tw.addresses import TwAddresses
if (await TwAddresses(self.proto,get_data=True)).is_used(chg.addr):
from ..ui import keypress_confirm
if not keypress_confirm(
'{a} {b} {c}\n{d}'.format(
a = yellow(f'Requested change address'),
b = (chg.mmid or chg.addr).hl(),
c = yellow('is already used!'),
d = yellow('Address reuse harms your privacy and security. Continue anyway? (y/N): ')
),
complete_prompt = True,
default_yes = False ):
die(1,'Exiting at user request')
# inputs methods
def select_unspent(self,unspent):
prompt = 'Enter a range or space-separated list of outputs to spend: '

View file

@ -0,0 +1,12 @@
import os as overlay_fake_os
from .new_orig import *
if overlay_fake_os.getenv('MMGEN_BOGUS_UNSPENT_DATA'):
class overlay_fake_data:
async def warn_chg_addr_used(foo,us):
from ..util import ymsg
ymsg('Bogus unspent data: skipping change address is used check')
New.warn_chg_addr_used = overlay_fake_data.warn_chg_addr_used

View file

@ -638,9 +638,13 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
sid1 = self._get_user_subsid('bob','29L')
sid2 = self._get_user_subsid('bob','127S')
chg_addr = self._user_sid('bob') + (':B:1',':L:1')[self.proto.coin=='BCH']
outputs_cl = [sid1+':C:2,0.29',sid2+':C:3,0.127',chg_addr]
inputs = ('3','1')[self.proto.coin=='BCH']
return self.user_txdo('bob',rtFee[1],outputs_cl,inputs,extra_args=['--subseeds=127'])
return self.user_txdo(
user = 'bob',
fee = rtFee[1],
outputs_cl = [sid1+':C:2,0.29',sid2+':C:3,0.127',chg_addr],
outputs_list = ('3','1')[self.proto.coin=='BCH'],
extra_args = ['--subseeds=127'],
used_chg_addr_resp = (None,'y')[self.proto.coin=='BCH'] )
def bob_twview2(self):
sid1 = self._get_user_subsid('bob','29L')
@ -779,7 +783,8 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
bad_locktime = False,
full_tx_view = False,
menu = ['M'],
skip_passphrase = False ):
skip_passphrase = False,
used_chg_addr_resp = None ):
t = self.spawn('mmgen-txdo',
['-d',self.tmpdir,'-B','--'+user] +
@ -793,7 +798,9 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
file_desc = 'Signed transaction',
interactive_fee = (tx_fee,'')[bool(fee)],
add_comment = tx_comment_jp,
view = 't',save=True)
view = 't',
save = True,
used_chg_addr_resp = used_chg_addr_resp )
if not skip_passphrase:
t.passphrase(dfl_wcls.desc,rt_pw)
@ -848,7 +855,8 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
outputs_cl = self._create_tx_outputs('alice',(('L',1,',60'),('C',1,',40'))) # alice_sid:L:1, alice_sid:C:1
outputs_cl += [self._user_sid('bob')+':'+rtBobOp3]
return self.user_txdo('bob',rtFee[1],outputs_cl,'3',
extra_args=([],['--rbf'])[self.proto.cap('rbf')])
extra_args = ([],['--rbf'])[self.proto.cap('rbf')],
used_chg_addr_resp = 'y' )
def bob_send_non_mmgen(self):
outputs_cl = self._create_tx_outputs('alice',(

View file

@ -46,13 +46,18 @@ class TestSuiteShared(object):
add_comment = '',
view = 't',
save = True,
tweaks = [] ):
tweaks = [],
used_chg_addr_resp = None ):
txdo = (caller or self.test_name)[:4] == 'txdo'
expect_pat = r'\[q\]uit view, .*?:.'
delete_pat = r'Enter account number .*:.'
confirm_pat = r'Is this what you want.*:.'
if used_chg_addr_resp is not None:
t.expect('reuse harms your privacy.*:.*',used_chg_addr_resp,regex=True)
pat = expect_pat
for choice in menu + ['q']:
t.expect(pat,choice,regex=True)