Skip to content

Commit

Permalink
new command: mmgen-tool rescan_address
Browse files Browse the repository at this point in the history
- updates an address balance using the `scantxoutset` RPC call
  • Loading branch information
mmgen committed May 26, 2022
1 parent c96833c commit 5488c51
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 1 deletion.
67 changes: 66 additions & 1 deletion mmgen/base_proto/bitcoin/tw/ctl.py
Expand Up @@ -14,7 +14,7 @@

from ....globalvars import g
from ....tw.ctl import TrackingWallet
from ....util import msg,msg_r,rmsg,die,write_mode
from ....util import msg,msg_r,rmsg,vmsg,die,suf,fmt_list,write_mode

class BitcoinTrackingWallet(TrackingWallet):

Expand Down Expand Up @@ -88,3 +88,68 @@ def gen_chunks(start,stop,tip):
tip = await self.rpc.call('getblockcount')

msg('Done')

@write_mode
async def rescan_address(self,addrspec):
res = await self.resolve_address(addrspec,None)
if not res:
return False
return await self.rescan_addresses([res.coinaddr])

@write_mode
async def rescan_addresses(self,coin_addrs):

import asyncio

async def do_scan():
return await self.rpc.call(
'scantxoutset',
'start',
[f'addr({a})' for a in coin_addrs],
timeout = 720 ) # call may take several minutes to complete

async def do_status():
m = f'{CR}Scanning UTXO set: '
msg_r(m)
while True:
await asyncio.sleep(2)
res = await self.rpc.call('scantxoutset','status')
if res:
msg_r(m + f'{res["progress"]}% completed ')
if task1.done():
msg('')
return

CR = '\r'

if self.rpc.backend.name == 'aiohttp':
task1 = asyncio.create_task( do_scan() )
task2 = asyncio.create_task( do_status() )
res = await task1
await task2
else:
msg_r(f'Scanning UTXO set, this could take several minutes...')
res = await do_scan()
msg('done')

if not res['success']:
msg('UTXO scanning failed or was interrupted')
return False
elif res['unspents']:
blocks = sorted({ i['height'] for i in res['unspents'] })
msg('Found {} unspent output{} in {} block{}'.format(
len(res['unspents']),
suf(res['unspents']),
len(blocks),
suf(blocks) ))
vmsg(f'Blocks to rescan: {fmt_list(blocks,fmt="bare")}')
for n,block in enumerate(blocks):
msg_r(f'{CR}Rescanning block: {block} ({n+1}/{len(blocks)})')
# httplib seems to require fresh connection here, so specify timeout
await self.rpc.call('rescanblockchain',block,block,timeout=60)
msg('\nRescan completed OK')
return True
else:
msg('Imported address has no balance' if len(coin_addrs) == 1 else
'Imported addresses have no balances' )
return True
1 change: 1 addition & 0 deletions mmgen/main_tool.py
Expand Up @@ -161,6 +161,7 @@
'listaddresses',
'remove_address',
'remove_label',
'rescan_address',
'rescan_blockchain',
'resolve_address',
'twview',
Expand Down
5 changes: 5 additions & 0 deletions mmgen/tool/rpc.py
Expand Up @@ -179,6 +179,11 @@ async def resolve_address(self,mmgen_or_coin_addr:str):
else:
return False

async def rescan_address(self,mmgen_or_coin_addr:str):
"rescan an address in the tracking wallet to update its balance"
from ..tw.ctl import TrackingWallet
return await (await TrackingWallet(self.proto,mode='w')).rescan_address( mmgen_or_coin_addr )

async def rescan_blockchain(self,
start_block: int = None,
stop_block: int = None ):
Expand Down
8 changes: 8 additions & 0 deletions test/test_py_d/ts_regtest.py
Expand Up @@ -203,6 +203,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
('bob_import_list', 'importing flat address list'),
('bob_import_list_rescan', 'importing flat address list with --rescan'),
('bob_resolve_addr', 'resolving an address in the tracking wallet'),
('bob_rescan_addr', 'rescanning an address'),
('bob_rescan_blockchain_all','rescanning the blockchain (full rescan)'),
('bob_rescan_blockchain_gb', 'rescanning the blockchain (Genesis block)'),
('bob_rescan_blockchain_one','rescanning the blockchain (single block)'),
Expand Down Expand Up @@ -931,6 +932,13 @@ def bob_import_list_rescan(self):
addrfile = joinpath(self.tmpdir,'non-mmgen.addrs')
return self.user_import('bob',['--quiet','--rescan','--addrlist',addrfile],nAddr=5)

def bob_rescan_addr(self):
sid = self._user_sid('bob')
t = self.spawn('mmgen-tool',['--bob','rescan_address',f'{sid}:C:1'])
t.expect('Found 1 unspent output')
t.expect('completed OK')
return t

def bob_rescan_blockchain(self,add_args,expect):
t = self.spawn('mmgen-tool',['--bob','rescan_blockchain'] + add_args)
t.expect(f'Scanning blocks {expect}')
Expand Down

0 comments on commit 5488c51

Please sign in to comment.