From 4a0a199e853eefe2a02e244e1e0864df20a54490 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 18 Oct 2024 10:32:08 +0000 Subject: [PATCH] whitespace: proto.btc --- mmgen/proto/btc/addrdata.py | 10 +-- mmgen/proto/btc/addrgen.py | 6 +- mmgen/proto/btc/common.py | 8 +- mmgen/proto/btc/daemon.py | 50 ++++++------ mmgen/proto/btc/misc.py | 16 ++-- mmgen/proto/btc/msg.py | 10 +-- mmgen/proto/btc/params.py | 30 +++---- mmgen/proto/btc/regtest.py | 97 ++++++++++++----------- mmgen/proto/btc/rpc.py | 104 ++++++++++++------------ mmgen/proto/btc/tw/addresses.py | 10 +-- mmgen/proto/btc/tw/bal.py | 12 +-- mmgen/proto/btc/tw/ctl.py | 56 ++++++------- mmgen/proto/btc/tw/json.py | 28 +++---- mmgen/proto/btc/tw/prune.py | 4 +- mmgen/proto/btc/tw/rpc.py | 34 ++++---- mmgen/proto/btc/tw/txhistory.py | 136 ++++++++++++++++---------------- mmgen/proto/btc/tw/unspent.py | 19 ++++- mmgen/proto/btc/tx/base.py | 4 +- mmgen/proto/btc/tx/bump.py | 12 +-- mmgen/proto/btc/tx/completed.py | 34 ++++---- mmgen/proto/btc/tx/info.py | 52 ++++++------ mmgen/proto/btc/tx/new.py | 22 +++--- mmgen/proto/btc/tx/online.py | 16 ++-- mmgen/proto/btc/tx/signed.py | 8 +- mmgen/proto/btc/tx/status.py | 34 ++++---- mmgen/proto/btc/tx/unsigned.py | 26 +++--- 26 files changed, 428 insertions(+), 410 deletions(-) diff --git a/mmgen/proto/btc/addrdata.py b/mmgen/proto/btc/addrdata.py index 692917ed..58057211 100755 --- a/mmgen/proto/btc/addrdata.py +++ b/mmgen/proto/btc/addrdata.py @@ -25,14 +25,14 @@ class BitcoinTwAddrData(TwAddrData): """ } - async def get_tw_data(self,twctl=None): + async def get_tw_data(self, twctl=None): self.cfg._util.vmsg('Getting address data from tracking wallet') c = self.rpc if 'label_api' in c.caps: accts = await c.call('listlabels') - ll = await c.batch_call('getaddressesbylabel',[(k,) for k in accts]) + ll = await c.batch_call('getaddressesbylabel', [(k,) for k in accts]) alists = [list(a.keys()) for a in ll] else: - accts = await c.call('listaccounts',0,True) - alists = await c.batch_call('getaddressesbyaccount',[(k,) for k in accts]) - return list(zip(accts,alists)) + accts = await c.call('listaccounts', 0, True) + alists = await c.batch_call('getaddressesbyaccount', [(k,) for k in accts]) + return list(zip(accts, alists)) diff --git a/mmgen/proto/btc/addrgen.py b/mmgen/proto/btc/addrgen.py index 2ee06679..3b3152e1 100755 --- a/mmgen/proto/btc/addrgen.py +++ b/mmgen/proto/btc/addrgen.py @@ -30,14 +30,14 @@ class compressed(p2pkh): class segwit(addr_generator.base): @check_data - def to_addr(self,data): + def to_addr(self, data): return self.proto.pubhash2segwitaddr(hash160(data.pubkey)) - def to_segwit_redeem_script(self,data): # NB: returns hex + def to_segwit_redeem_script(self, data): # NB: returns hex return self.proto.pubhash2redeem_script(hash160(data.pubkey)).hex() class bech32(addr_generator.base): @check_data - def to_addr(self,data): + def to_addr(self, data): return self.proto.pubhash2bech32addr(hash160(data.pubkey)) diff --git a/mmgen/proto/btc/common.py b/mmgen/proto/btc/common.py index b9459add..fbfece99 100755 --- a/mmgen/proto/btc/common.py +++ b/mmgen/proto/btc/common.py @@ -17,7 +17,7 @@ import hashlib b58a = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' def hash160(in_bytes): # OP_HASH160 - return hashlib.new('ripemd160',hashlib.sha256(in_bytes).digest()).digest() + return hashlib.new('ripemd160', hashlib.sha256(in_bytes).digest()).digest() def hash256(in_bytes): # OP_HASH256 return hashlib.sha256(hashlib.sha256(in_bytes).digest()).digest() @@ -35,13 +35,13 @@ def b58chk_encode(in_bytes): while n: yield b58a[n % 58] n //= 58 - return ('1' * lzeroes) + ''.join(do_enc(int.from_bytes(in_bytes+hash256(in_bytes)[:4],'big')))[::-1] + return ('1' * lzeroes) + ''.join(do_enc(int.from_bytes(in_bytes+hash256(in_bytes)[:4], 'big')))[::-1] def b58chk_decode(s): lzeroes = len(s) - len(s.lstrip('1')) - res = sum(b58a.index(ch) * 58**n for n,ch in enumerate(s[::-1])) + res = sum(b58a.index(ch) * 58**n for n, ch in enumerate(s[::-1])) bl = res.bit_length() - out = b'\x00' * lzeroes + res.to_bytes(bl//8 + bool(bl%8),'big') + out = b'\x00' * lzeroes + res.to_bytes(bl//8 + bool(bl%8), 'big') if out[-4:] != hash256(out[:-4])[:4]: raise ValueError('b58chk_decode(): incorrect checksum') return out[:-4] diff --git a/mmgen/proto/btc/daemon.py b/mmgen/proto/btc/daemon.py index 9b093a05..36cd5061 100755 --- a/mmgen/proto/btc/daemon.py +++ b/mmgen/proto/btc/daemon.py @@ -16,7 +16,7 @@ import os from ...cfg import gc from ...util import list_gen -from ...daemon import CoinDaemon,_nw,_dd +from ...daemon import CoinDaemon, _nw, _dd class bitcoin_core_daemon(CoinDaemon): daemon_data = _dd('Bitcoin Core', 270100, '27.1.0') @@ -28,15 +28,15 @@ class bitcoin_core_daemon(CoinDaemon): cfg_file = 'bitcoin.conf' nonstd_datadir = False datadirs = { - 'linux': [gc.home_dir,'.bitcoin'], + 'linux': [gc.home_dir, '.bitcoin'], 'darwin': [gc.home_dir, 'Library', 'Application Support', 'Bitcoin'], - 'win32': [os.getenv('APPDATA'),'Bitcoin'] + 'win32': [os.getenv('APPDATA'), 'Bitcoin'] } avail_opts = ('no_daemonize', 'online', 'bdb_wallet') def init_datadir(self): if self.network == 'regtest' and not self.test_suite: - return os.path.join( self.cfg.data_dir_root, 'regtest', self.cfg.coin.lower() ) + return os.path.join(self.cfg.data_dir_root, 'regtest', self.cfg.coin.lower()) else: return super().init_datadir() @@ -48,11 +48,11 @@ class bitcoin_core_daemon(CoinDaemon): 'mainnet': '', 'testnet': self.testnet_dir, 'regtest': 'regtest', - }[self.network] ) + }[self.network]) @property def auth_cookie_fn(self): - return os.path.join(self.network_datadir,'.cookie') + return os.path.join(self.network_datadir, '.cookie') def init_subclass(self): @@ -85,15 +85,15 @@ class bitcoin_core_daemon(CoinDaemon): ['--addresstype=bech32', self.coin == 'LTC' and self.network == 'regtest'], ) - self.lockfile = os.path.join(self.network_datadir,'.cookie') + self.lockfile = os.path.join(self.network_datadir, '.cookie') @property def state(self): - cp = self.cli('getblockcount',silent=True) + cp = self.cli('getblockcount', silent=True) err = cp.stderr.decode() if ("error: couldn't connect" in err or "error: Could not connect" in err - or "does not exist" in err ): + or "does not exist" in err): # regtest has no cookie file, so test will always fail ret = 'busy' if (self.lockfile and os.path.exists(self.lockfile)) else 'stopped' elif cp.returncode == 0: @@ -108,19 +108,19 @@ class bitcoin_core_daemon(CoinDaemon): def stop_cmd(self): return self.cli_cmd('stop') - def set_comment_args(self,rpc,coinaddr,lbl): + def set_comment_args(self, rpc, coinaddr, lbl): if 'label_api' in rpc.caps: - return ('setlabel',coinaddr,lbl) + return ('setlabel', coinaddr, lbl) else: # NOTE: this works because importaddress() removes the old account before # associating the new account with the address. - # RPC args: addr,label,rescan[=true],p2sh[=none] - return ('importaddress',coinaddr,lbl,False) + # RPC args: addr, label, rescan[=true], p2sh[=none] + return ('importaddress', coinaddr, lbl, False) - def estimatefee_args(self,rpc): + def estimatefee_args(self, rpc): return (self.cfg.fee_estimate_confs,) - def sigfail_errmsg(self,e): + def sigfail_errmsg(self, e): return e.args[0] class bitcoin_cash_node_daemon(bitcoin_core_daemon): @@ -131,24 +131,24 @@ class bitcoin_cash_node_daemon(bitcoin_core_daemon): cfg_file_hdr = '# Bitcoin Cash Node config file\n' nonstd_datadir = True datadirs = { - 'linux': [gc.home_dir,'.bitcoin-bchn'], + 'linux': [gc.home_dir, '.bitcoin-bchn'], 'darwin': [gc.home_dir, 'Library', 'Application Support', 'Bitcoin-Cash-Node'], - 'win32': [os.getenv('APPDATA'),'Bitcoin-Cash-Node'] + 'win32': [os.getenv('APPDATA'), 'Bitcoin-Cash-Node'] } - def set_comment_args(self,rpc,coinaddr,lbl): - # bitcoin-{abc,bchn} 'setlabel' RPC is broken, so use old 'importaddress' method to set label + def set_comment_args(self, rpc, coinaddr, lbl): + # bitcoin-{abc, bchn} 'setlabel' RPC is broken, so use old 'importaddress' method to set label # Broken behavior: new label is set OK, but old label gets attached to another address - return ('importaddress',coinaddr,lbl,False) + return ('importaddress', coinaddr, lbl, False) - def estimatefee_args(self,rpc): + def estimatefee_args(self, rpc): return () if rpc.daemon_version >= 190100 else (self.cfg.fee_estimate_confs,) - def sigfail_errmsg(self,e): + def sigfail_errmsg(self, e): return ( 'This is not the BCH chain.\nRe-run the script without the --coin=bch option.' if 'Invalid sighash param' in e.args[0] else - e.args[0] ) + e.args[0]) class litecoin_core_daemon(bitcoin_core_daemon): # v0.21.2rc5 crashes when mining more than 431 blocks in regtest mode: @@ -161,7 +161,7 @@ class litecoin_core_daemon(bitcoin_core_daemon): cfg_file = 'litecoin.conf' cfg_file_hdr = '# Litecoin Core config file\n' datadirs = { - 'linux': [gc.home_dir,'.litecoin'], + 'linux': [gc.home_dir, '.litecoin'], 'darwin': [gc.home_dir, 'Library', 'Application Support', 'Litecoin'], - 'win32': [os.getenv('APPDATA'),'Litecoin'] + 'win32': [os.getenv('APPDATA'), 'Litecoin'] } diff --git a/mmgen/proto/btc/misc.py b/mmgen/proto/btc/misc.py index 3da6bfb5..3fce016a 100755 --- a/mmgen/proto/btc/misc.py +++ b/mmgen/proto/btc/misc.py @@ -12,9 +12,9 @@ proto.btc.misc: miscellaneous functions for Bitcoin base protocol """ -from ...util import msg,msg_r +from ...util import msg, msg_r -async def scantxoutset(cfg,rpc,descriptor_list): +async def scantxoutset(cfg, rpc, descriptor_list): import asyncio @@ -23,7 +23,7 @@ async def scantxoutset(cfg,rpc,descriptor_list): 'scantxoutset', 'start', descriptor_list, - timeout = 720 ) # call may take several minutes to complete + timeout = 720) # call may take several minutes to complete async def do_status(): @@ -34,23 +34,23 @@ async def scantxoutset(cfg,rpc,descriptor_list): while True: await asyncio.sleep(sleep_secs) - res = await rpc.call('scantxoutset','status') + res = await rpc.call('scantxoutset', 'status') if res: msg_r(m + f'{res["progress"]}% completed ') if task1.done(): msg(m + '100% completed') return - res = await rpc.call('scantxoutset','status') + res = await rpc.call('scantxoutset', 'status') if res and res.get('progress'): msg_r('Aborting scan in progress...') - await rpc.call('scantxoutset','abort') + await rpc.call('scantxoutset', 'abort') await asyncio.sleep(1) msg('done') if rpc.backend.name == 'aiohttp': - task1 = asyncio.create_task( do_scan() ) - task2 = asyncio.create_task( do_status() ) + task1 = asyncio.create_task(do_scan()) + task2 = asyncio.create_task(do_status()) ret = await task1 await task2 else: diff --git a/mmgen/proto/btc/msg.py b/mmgen/proto/btc/msg.py index 137023f1..308db741 100755 --- a/mmgen/proto/btc/msg.py +++ b/mmgen/proto/btc/msg.py @@ -22,13 +22,13 @@ class coin_msg(coin_msg): class unsigned(coin_msg.unsigned): - async def do_sign(self,wif,message,msghash_type): - return await self.rpc.call( 'signmessagewithprivkey', wif, message ) + async def do_sign(self, wif, message, msghash_type): + return await self.rpc.call('signmessagewithprivkey', wif, message) class signed_online(coin_msg.signed_online): - async def do_verify(self,addr,sig,message,msghash_type): - return await self.rpc.call( 'verifymessage', addr, sig, message ) + async def do_verify(self, addr, sig, message, msghash_type): + return await self.rpc.call('verifymessage', addr, sig, message) - class exported_sigs(coin_msg.exported_sigs,signed_online): + class exported_sigs(coin_msg.exported_sigs, signed_online): pass diff --git a/mmgen/proto/btc/params.py b/mmgen/proto/btc/params.py index 4930f9af..4668778a 100755 --- a/mmgen/proto/btc/params.py +++ b/mmgen/proto/btc/params.py @@ -21,20 +21,20 @@ class mainnet(CoinProtocol.Secp256k1): # chainparams.cpp All Bitcoin code and chain forks inherit from this class """ mod_clsname = 'Bitcoin' - network_names = _nw('mainnet','testnet','regtest') - addr_ver_info = { '00': 'p2pkh', '05': 'p2sh' } + network_names = _nw('mainnet', 'testnet', 'regtest') + addr_ver_info = {'00': 'p2pkh', '05': 'p2sh'} addr_len = 20 - wif_ver_num = { 'std': '80' } - mmtypes = ('L','C','S','B') + wif_ver_num = {'std': '80'} + mmtypes = ('L', 'C', 'S', 'B') dfl_mmtype = 'L' coin_amt = 'BTCAmt' max_tx_fee = '0.003' sighash_type = 'ALL' block0 = '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f' forks = [ - _finfo(478559,'00000000000000000019f112ec0a9982926f1258cdcc558dd7c3b7e5dc7fa148','BCH',False), + _finfo(478559, '00000000000000000019f112ec0a9982926f1258cdcc558dd7c3b7e5dc7fa148', 'BCH', False), ] - caps = ('rbf','segwit') + caps = ('rbf', 'segwit') mmcaps = ('rpc', 'rpc_init', 'tw', 'msg') base_proto = 'Bitcoin' base_proto_coin = 'BTC' @@ -42,7 +42,7 @@ class mainnet(CoinProtocol.Secp256k1): # chainparams.cpp # From BIP173: witness version 'n' is stored as 'OP_n'. OP_0 is encoded as 0x00, # but OP_1 through OP_16 are encoded as 0x51 though 0x60 (81 to 96 in decimal). witness_vernum_hex = '00' - witness_vernum = int(witness_vernum_hex,16) + witness_vernum = int(witness_vernum_hex, 16) bech32_hrp = 'bc' sign_mode = 'daemon' avg_bdi = int(9.7 * 60) # average block discovery interval (historical) @@ -53,15 +53,15 @@ class mainnet(CoinProtocol.Secp256k1): # chainparams.cpp ignore_daemon_version = False max_int = 0xffffffff - def encode_wif(self,privbytes,pubkey_type,compressed): # input is preprocessed hex + def encode_wif(self, privbytes, pubkey_type, compressed): # input is preprocessed hex assert len(privbytes) == self.privkey_len, f'{len(privbytes)} bytes: incorrect private key length!' assert pubkey_type in self.wif_ver_bytes, f'{pubkey_type!r}: invalid pubkey_type' return b58chk_encode( self.wif_ver_bytes[pubkey_type] + privbytes - + (b'',b'\x01')[bool(compressed)]) + + (b'', b'\x01')[bool(compressed)]) - def decode_wif(self,wif): + def decode_wif(self, wif): key_data = b58chk_decode(wif) vlen = self.wif_ver_bytes_len or self.get_wif_ver_bytes_len(key_data) key = key_data[vlen:] @@ -74,13 +74,13 @@ class mainnet(CoinProtocol.Secp256k1): # chainparams.cpp return decoded_wif( sec = key[:self.privkey_len], pubkey_type = self.wif_ver_bytes_to_pubkey_type[key_data[:vlen]], - compressed = len(key) == self.privkey_len + 1 ) + compressed = len(key) == self.privkey_len + 1) def decode_addr(self, addr): if 'B' in self.mmtypes and addr[:len(self.bech32_hrp)] == self.bech32_hrp: from ...contrib import bech32 - ret = bech32.decode(self.bech32_hrp,addr) + ret = bech32.decode(self.bech32_hrp, addr) if ret[0] != self.witness_vernum: from ...util import msg @@ -115,11 +115,11 @@ class mainnet(CoinProtocol.Secp256k1): # chainparams.cpp self, bech32.bech32_encode( hrp = self.bech32_hrp, - data = [self.witness_vernum] + bech32.convertbits(list(pubhash),8,5))) + data = [self.witness_vernum] + bech32.convertbits(list(pubhash), 8, 5))) class testnet(mainnet): - addr_ver_info = { '6f': 'p2pkh', 'c4': 'p2sh' } - wif_ver_num = { 'std': 'ef' } + addr_ver_info = {'6f': 'p2pkh', 'c4': 'p2sh'} + wif_ver_num = {'std': 'ef'} bech32_hrp = 'tb' class regtest(testnet): diff --git a/mmgen/proto/btc/regtest.py b/mmgen/proto/btc/regtest.py index eb7b5ca5..9a48dd60 100755 --- a/mmgen/proto/btc/regtest.py +++ b/mmgen/proto/btc/regtest.py @@ -20,16 +20,16 @@ proto.btc.regtest: Coin daemon regression test mode setup and operations """ -import os,shutil,json -from ...util import msg,gmsg,die,capfirst,suf +import os, shutil, json +from ...util import msg, gmsg, die, capfirst, suf from ...protocol import init_proto -from ...rpc import rpc_init,json_encoder +from ...rpc import rpc_init, json_encoder from ...objmethods import MMGenObject from ...daemon import CoinDaemon -def create_data_dir(cfg,data_dir): +def create_data_dir(cfg, data_dir): try: - os.stat(os.path.join(data_dir,'regtest')) + os.stat(os.path.join(data_dir, 'regtest')) except: pass else: @@ -39,7 +39,7 @@ def create_data_dir(cfg,data_dir): f'Delete your existing MMGen regtest setup at {data_dir!r} and create a new one?'): shutil.rmtree(data_dir) else: - die(1,'Exiting') + die(1, 'Exiting') try: os.makedirs(data_dir) @@ -60,10 +60,19 @@ class MMGenRegtest(MMGenObject): rpc_user = 'bobandalice' rpc_password = 'hodltothemoon' - users = ('bob','alice','carol','miner') - coins = ('btc','bch','ltc') - usr_cmds = ('setup','generate','send','start','stop', 'state', 'balances','mempool','cli','wallet_cli') - + users = ('bob', 'alice', 'carol', 'miner') + coins = ('btc', 'bch', 'ltc') + usr_cmds = ( + 'setup', + 'generate', + 'send', + 'start', + 'stop', + 'state', + 'balances', + 'mempool', + 'cli', + 'wallet_cli') bdb_hdseed = 'beadcafe' * 8 bdb_miner_wif = 'cTyMdQ2BgfAsjopRVZrj7AoEGp97pKfrC2NkqLuwHr4KHfPNAKwp' bdb_miner_addrs = { @@ -95,7 +104,7 @@ class MMGenRegtest(MMGenObject): @property async def miner_addr(self): - if not hasattr(self,'_miner_addr'): + if not hasattr(self, '_miner_addr'): self._miner_addr = ( self.bdb_miner_addrs[self.coin] if self.bdb_wallet else await self.rpc_call('getnewaddress', wallet='miner')) @@ -103,7 +112,7 @@ class MMGenRegtest(MMGenObject): @property async def miner_wif(self): - if not hasattr(self,'_miner_wif'): + if not hasattr(self, '_miner_wif'): self._miner_wif = ( self.bdb_miner_wif if self.bdb_wallet else await self.rpc_call('dumpprivkey', (await self.miner_addr), wallet='miner')) @@ -112,16 +121,16 @@ class MMGenRegtest(MMGenObject): def create_hdseed_wif(self): from ...tool.api import tool_api t = tool_api(self.cfg) - t.init_coin(self.proto.coin,self.proto.network) + t.init_coin(self.proto.coin, self.proto.network) t.addrtype = 'compressed' if self.proto.coin == 'BCH' else 'bech32' return t.hex2wif(self.bdb_hdseed) - async def generate(self,blocks=1,silent=False): + async def generate(self, blocks=1, silent=False): blocks = int(blocks) if self.d.state == 'stopped': - die(1,'Regtest daemon is not running') + die(1, 'Regtest daemon is not running') self.d.wait_for_state('ready') @@ -130,15 +139,15 @@ class MMGenRegtest(MMGenObject): 'generatetoaddress', blocks, await self.miner_addr, - wallet = 'miner' ) + wallet = 'miner') if len(out) != blocks: - die(4,'Error generating blocks') + die(4, 'Error generating blocks') if not silent: gmsg(f'Mined {blocks} block{suf(blocks)}') - async def create_wallet(self,user): + async def create_wallet(self, user): return await (await self.rpc).icall( 'createwallet', wallet_name = user, @@ -157,13 +166,13 @@ class MMGenRegtest(MMGenObject): if self.d.state != 'stopped': await self.rpc_call('stop') - create_data_dir( self.cfg, self.d.datadir ) + create_data_dir(self.cfg, self.d.datadir) gmsg(f'Starting {self.coin.upper()} regtest setup') self.d.start(silent=True) - for user in ('miner','bob','alice'): + for user in ('miner', 'bob', 'alice'): gmsg(f'Creating {capfirst(user)}’s tracking wallet') await self.create_wallet(user) @@ -194,18 +203,18 @@ class MMGenRegtest(MMGenObject): msg('Stopping regtest daemon') await self.rpc_call('stop') - def init_daemon(self,reindex=False): + def init_daemon(self, reindex=False): if reindex: self.d.usr_coind_args.append('--reindex') - async def start_daemon(self,reindex=False,silent=True): + async def start_daemon(self, reindex=False, silent=True): self.init_daemon(reindex=reindex) self.d.start(silent=silent) - for user in ('miner','bob','alice'): + for user in ('miner', 'bob', 'alice'): msg(f'Loading {capfirst(user)}’s wallet') - await self.rpc_call('loadwallet',user,start_daemon=False) + await self.rpc_call('loadwallet', user, start_daemon=False) - async def rpc_call(self,*args,wallet=None,start_daemon=True): + async def rpc_call(self, *args, wallet=None, start_daemon=True): if start_daemon and self.d.state == 'stopped': await self.start_daemon() return await (await self.rpc).call(*args, wallet=wallet) @@ -228,41 +237,41 @@ class MMGenRegtest(MMGenObject): async def balances(self): bal = {} - users = ('bob','alice') + users = ('bob', 'alice') for user in users: - out = await self.rpc_call('listunspent',0,wallet=user) + out = await self.rpc_call('listunspent', 0, wallet=user) bal[user] = sum(self.proto.coin_amt(e['amount']) for e in out) fs = '{:<16} {:18.8f}' for user in users: - msg(fs.format(user.capitalize()+"'s balance:",bal[user])) - msg(fs.format('Total balance:',sum(v for k,v in bal.items()))) + msg(fs.format(user.capitalize()+"'s balance:", bal[user])) + msg(fs.format('Total balance:', sum(v for k, v in bal.items()))) - async def send(self,addr,amt): + async def send(self, addr, amt): gmsg(f'Sending {amt} miner {self.d.coin} to address {addr}') - await self.rpc_call('sendtoaddress',addr,str(amt),wallet='miner') + await self.rpc_call('sendtoaddress', addr, str(amt), wallet='miner') await self.generate(1) async def mempool(self): await self.cli('getrawmempool') - async def cli(self,*args): + async def cli(self, *args): ret = await self.rpc_call(*cliargs_convert(args)) - print(ret if isinstance(ret,str) else json.dumps(ret,cls=json_encoder,indent=4)) + print(ret if isinstance(ret, str) else json.dumps(ret, cls=json_encoder, indent=4)) - async def wallet_cli(self,wallet,*args): - ret = await self.rpc_call(*cliargs_convert(args),wallet=wallet) - print(ret if isinstance(ret,str) else json.dumps(ret,cls=json_encoder,indent=4)) + async def wallet_cli(self, wallet, *args): + ret = await self.rpc_call(*cliargs_convert(args), wallet=wallet) + print(ret if isinstance(ret, str) else json.dumps(ret, cls=json_encoder, indent=4)) - async def cmd(self,args): - ret = getattr(self,args[0])(*args[1:]) + async def cmd(self, args): + ret = getattr(self, args[0])(*args[1:]) return (await ret) if type(ret).__name__ == 'coroutine' else ret - async def fork(self,coin): # currently disabled + async def fork(self, coin): # currently disabled - proto = init_proto( self.cfg, coin, False ) + proto = init_proto(self.cfg, coin, False) if not [f for f in proto.forks if f[2] == proto.coin.lower() and f[3] is True]: - die(1,f'Coin {proto.coin} is not a replayable fork of coin {coin}') + die(1, f'Coin {proto.coin} is not a replayable fork of coin {coin}') gmsg(f'Creating fork from coin {coin} to coin {proto.coin}') @@ -271,7 +280,7 @@ class MMGenRegtest(MMGenObject): try: os.stat(source_rt.d.datadir) except: - die(1,f'Source directory {source_rt.d.datadir!r} does not exist!') + die(1, f'Source directory {source_rt.d.datadir!r} does not exist!') # stop the source daemon if source_rt.d.state != 'stopped': @@ -286,9 +295,9 @@ class MMGenRegtest(MMGenObject): except: pass - create_data_dir( self.cfg, self.d.datadir ) + create_data_dir(self.cfg, self.d.datadir) os.rmdir(self.d.datadir) - shutil.copytree(source_rt.d.datadir,self.d.datadir,symlinks=True) + shutil.copytree(source_rt.d.datadir, self.d.datadir, symlinks=True) await self.start_daemon(reindex=True) await self.rpc_call('stop') diff --git a/mmgen/proto/btc/rpc.py b/mmgen/proto/btc/rpc.py index 8e80991a..de459981 100755 --- a/mmgen/proto/btc/rpc.py +++ b/mmgen/proto/btc/rpc.py @@ -16,9 +16,9 @@ import os from ...base_obj import AsyncInit from ...obj import TrackingWalletName -from ...util import ymsg,die,fmt +from ...util import ymsg, die, fmt from ...fileutil import get_lines_from_file -from ...rpc import RPCClient,auth_data +from ...rpc import RPCClient, auth_data no_credentials_errmsg = """ Error: no {proto_name} RPC authentication method found @@ -65,23 +65,21 @@ class CallSigs: """ return ( 'createwallet', - wallet_name, # 1. wallet_name - no_keys, # 2. disable_private_keys - blank, # 3. blank (no keys or seed) - passphrase, # 4. passphrase (empty string for non-encrypted) - False, # 5. avoid_reuse (track address reuse) - descriptors, # 6. descriptors (native descriptor wallet) - load_on_startup # 7. load_on_startup - ) + wallet_name, # 1. wallet_name + no_keys, # 2. disable_private_keys + blank, # 3. blank (no keys or seed) + passphrase, # 4. passphrase (empty string for non-encrypted) + False, # 5. avoid_reuse (track address reuse) + descriptors, # 6. descriptors (native descriptor wallet) + load_on_startup) # 7. load_on_startup def gettransaction(self, txid, include_watchonly, verbose): return ( 'gettransaction', txid, # 1. transaction id include_watchonly, # 2. optional, default=true for watch-only wallets, otherwise false - verbose, # 3. optional, default=false -- include a `decoded` field containing - # the decoded transaction (equivalent to RPC decoderawtransaction) - ) + verbose) # 3. optional, default=false -- include a `decoded` field containing + # => the decoded transaction (equivalent to RPC decoderawtransaction) class litecoin_core(bitcoin_core): @@ -97,20 +95,18 @@ class CallSigs: 'createwallet', wallet_name, # 1. wallet_name no_keys, # 2. disable_private_keys - blank, # 3. blank (no keys or seed) - ) + blank) # 3. blank (no keys or seed) def gettransaction(self, txid, include_watchonly, verbose): return ( 'gettransaction', txid, # 1. transaction id - include_watchonly, # 2. optional, default=true for watch-only wallets, otherwise false - ) + include_watchonly) # 2. optional, default=true for watch-only wallets, otherwise false class bitcoin_cash_node(litecoin_core): pass -class BitcoinRPCClient(RPCClient,metaclass=AsyncInit): +class BitcoinRPCClient(RPCClient, metaclass=AsyncInit): auth_type = 'basic' has_auth_cookie = True @@ -123,17 +119,17 @@ class BitcoinRPCClient(RPCClient,metaclass=AsyncInit): proto, daemon, backend, - ignore_wallet ): + ignore_wallet): self.proto = proto self.daemon = daemon - self.call_sigs = getattr(CallSigs,daemon.id)(cfg) + self.call_sigs = getattr(CallSigs, daemon.id)(cfg) self.twname = TrackingWalletName(cfg.regtest_user or cfg.tw_name or self.dfl_twname) super().__init__( cfg = cfg, host = 'localhost' if cfg.test_suite or cfg.network == 'regtest' else (cfg.rpc_host or 'localhost'), - port = daemon.rpc_port ) + port = daemon.rpc_port) self.set_auth() @@ -142,20 +138,20 @@ class BitcoinRPCClient(RPCClient,metaclass=AsyncInit): self.cached = {} self.caps = ('full_node',) - for func,cap in ( - ('setlabel','label_api'), - ('getdeploymentinfo','deployment_info'), - ('signrawtransactionwithkey','sign_with_key') ): - if len((await self.call('help',func)).split('\n')) > 3: + for func, cap in ( + ('setlabel', 'label_api'), + ('getdeploymentinfo', 'deployment_info'), + ('signrawtransactionwithkey', 'sign_with_key')): + if len((await self.call('help', func)).split('\n')) > 3: self.caps += (cap,) call_group = [ - ('getblockcount',()), - ('getblockhash',(0,)), - ('getnetworkinfo',()), - ('getblockchaininfo',()), + ('getblockcount', ()), + ('getblockhash', (0,)), + ('getnetworkinfo', ()), + ('getblockchaininfo', ()), ] + ( - [('getdeploymentinfo',())] if 'deployment_info' in self.caps else [] + [('getdeploymentinfo', ())] if 'deployment_info' in self.caps else [] ) ( @@ -165,7 +161,7 @@ class BitcoinRPCClient(RPCClient,metaclass=AsyncInit): self.cached['blockchaininfo'], self.cached['deploymentinfo'], ) = ( - await self.gathered_call(None,tuple(call_group)) + await self.gathered_call(None, tuple(call_group)) ) + ( [] if 'deployment_info' in self.caps else [None] ) @@ -174,8 +170,8 @@ class BitcoinRPCClient(RPCClient,metaclass=AsyncInit): self.daemon_version_str = self.cached['networkinfo']['subversion'] self.chain = self.cached['blockchaininfo']['chain'] - tip = await self.call('getblockhash',self.blockcount) - self.cur_date = (await self.call('getblockheader',tip))['time'] + tip = await self.call('getblockhash', self.blockcount) + self.cur_date = (await self.call('getblockheader', tip))['time'] if self.chain != 'regtest': self.chain += 'net' assert self.chain in self.proto.networks @@ -187,10 +183,10 @@ class BitcoinRPCClient(RPCClient,metaclass=AsyncInit): for fork in self.proto.forks: if fork.height is None or self.blockcount < fork.height: break - if fork.hash != await self.call('getblockhash',fork.height): - die(3,f'Bad block hash at fork block {fork.height}. Is this the {fork.name} chain?') + if fork.hash != await self.call('getblockhash', fork.height): + die(3, f'Bad block hash at fork block {fork.height}. Is this the {fork.name} chain?') except Exception as e: - die(2,'{!s}\n{c!r} requested, but this is not the {c} chain!'.format(e,c=self.proto.coin)) + die(2, '{!s}\n{c!r} requested, but this is not the {c} chain!'.format(e, c=self.proto.coin)) if self.chain == 'mainnet': # skip this for testnet, as Genesis block may change await check_chainfork_mismatch(block0) @@ -204,7 +200,7 @@ class BitcoinRPCClient(RPCClient,metaclass=AsyncInit): @property async def walletinfo(self): - if not hasattr(self,'_walletinfo'): + if not hasattr(self, '_walletinfo'): self._walletinfo = await self.call('getwalletinfo') return self._walletinfo @@ -214,17 +210,17 @@ class BitcoinRPCClient(RPCClient,metaclass=AsyncInit): """ if self.cfg.network == 'regtest': from .regtest import MMGenRegtest - user,passwd = (MMGenRegtest.rpc_user, MMGenRegtest.rpc_password) + user, passwd = (MMGenRegtest.rpc_user, MMGenRegtest.rpc_password) elif self.cfg.rpc_user: - user,passwd = (self.cfg.rpc_user,self.cfg.rpc_password) + user, passwd = (self.cfg.rpc_user, self.cfg.rpc_password) else: - user,passwd = self.get_daemon_cfg_options(('rpcuser','rpcpassword')).values() + user, passwd = self.get_daemon_cfg_options(('rpcuser', 'rpcpassword')).values() if not (user and passwd): - user,passwd = (self.daemon.rpc_user,self.daemon.rpc_password) + user, passwd = (self.daemon.rpc_user, self.daemon.rpc_password) if user and passwd: - self.auth = auth_data(user,passwd) + self.auth = auth_data(user, passwd) return if self.has_auth_cookie: @@ -233,12 +229,12 @@ class BitcoinRPCClient(RPCClient,metaclass=AsyncInit): self.auth = auth_data(*cookie.split(':')) return - die(1, '\n\n' + fmt(no_credentials_errmsg,strip_char='\t',indent=' ').format( + die(1, '\n\n' + fmt(no_credentials_errmsg, strip_char='\t', indent=' ').format( proto_name = self.proto.name, cf_name = (self.proto.is_fork_of or self.proto.name).lower(), )) - def make_host_path(self,wallet): + def make_host_path(self, wallet): return f'/wallet/{wallet}' if wallet else self.wallet_path @property @@ -264,41 +260,41 @@ class BitcoinRPCClient(RPCClient,metaclass=AsyncInit): # Use dirname() to remove 'bob' or 'alice' component return os.path.join( (os.path.dirname(self.cfg.data_dir) if self.proto.regtest else self.daemon.datadir), - self.daemon.cfg_file ) + self.daemon.cfg_file) - def get_daemon_cfg_options(self,req_keys): + def get_daemon_cfg_options(self, req_keys): fn = self.get_daemon_cfg_fn() try: - lines = get_lines_from_file( self.cfg, fn, 'daemon config file', silent=not self.cfg.verbose ) + lines = get_lines_from_file(self.cfg, fn, 'daemon config file', silent=not self.cfg.verbose) except: self.cfg._util.vmsg(f'Warning: {fn!r} does not exist or is unreadable') - return dict((k,None) for k in req_keys) + return dict((k, None) for k in req_keys) def gen(): for key in req_keys: val = None for l in lines: if l.startswith(key): - res = l.split('=',1) + res = l.split('=', 1) if len(res) == 2 and not ' ' in res[1].strip(): val = res[1].strip() - yield (key,val) + yield (key, val) return dict(gen()) def get_daemon_auth_cookie(self): fn = self.daemon.auth_cookie_fn - return get_lines_from_file( self.cfg, fn, 'cookie', quiet=True )[0] if os.access(fn,os.R_OK) else '' + return get_lines_from_file(self.cfg, fn, 'cookie', quiet=True)[0] if os.access(fn, os.R_OK) else '' - def info(self,info_id): + def info(self, info_id): def segwit_is_active(): if 'deployment_info' in self.caps: return ( self.cached['deploymentinfo']['deployments']['segwit']['active'] - or ( self.cfg.test_suite and not self.chain == 'regtest' ) + or (self.cfg.test_suite and not self.chain == 'regtest') ) d = self.cached['blockchaininfo'] diff --git a/mmgen/proto/btc/tw/addresses.py b/mmgen/proto/btc/tw/addresses.py index 223843f5..5b57fb9c 100755 --- a/mmgen/proto/btc/tw/addresses.py +++ b/mmgen/proto/btc/tw/addresses.py @@ -14,11 +14,11 @@ proto.btc.tw.addresses: Bitcoin base protocol tracking wallet address list class from ....tw.addresses import TwAddresses from ....tw.shared import TwLabel -from ....util import msg,msg_r +from ....util import msg, msg_r from ....obj import get_obj from .rpc import BitcoinTwRPC -class BitcoinTwAddresses(TwAddresses,BitcoinTwRPC): +class BitcoinTwAddresses(TwAddresses, BitcoinTwRPC): has_age = True prompt_fs_in = [ @@ -43,7 +43,7 @@ class BitcoinTwAddresses(TwAddresses,BitcoinTwRPC): 'v':'a_view', 'w':'a_view_detail', 'p':'a_print_detail', - 'l':'i_comment_add' } + 'l':'i_comment_add'} async def get_rpc_data(self): @@ -68,8 +68,8 @@ class BitcoinTwAddresses(TwAddresses,BitcoinTwRPC): msg_r('Getting received funds data...') # args: 1:minconf, 2:include_empty, 3:include_watchonly, 4:include_immature_coinbase (>=v23.0.0) - for d in await self.rpc.call( 'listreceivedbylabel', 1, True, True ): - label = get_obj( TwLabel, proto=self.proto, text=d['label'] ) + for d in await self.rpc.call('listreceivedbylabel', 1, True, True): + label = get_obj(TwLabel, proto=self.proto, text=d['label']) if label: assert label.mmid in addrs, f'{label.mmid!r} not found in addrlist!' addrs[label.mmid]['recvd'] = coin_amt(d['amount']) diff --git a/mmgen/proto/btc/tw/bal.py b/mmgen/proto/btc/tw/bal.py index 8cc2319a..9ee22bed 100755 --- a/mmgen/proto/btc/tw/bal.py +++ b/mmgen/proto/btc/tw/bal.py @@ -23,7 +23,7 @@ class BitcoinTwGetBalance(TwGetBalance): self.walletinfo = await self.rpc.walletinfo await super().__init__(cfg, proto, minconf, quiet) - start_labels = ('TOTAL','Non-MMGen','Non-wallet') + start_labels = ('TOTAL', 'Non-MMGen', 'Non-wallet') conf_cols = { 'unconfirmed': 'Unconfirmed', 'lt_minconf': '<{minconf} confs', @@ -31,9 +31,9 @@ class BitcoinTwGetBalance(TwGetBalance): } async def create_data(self): - lbl_id = ('account','label')['label_api' in self.rpc.caps] - for d in await self.rpc.call('listunspent',0): - tw_lbl = get_tw_label(self.proto,d[lbl_id]) + lbl_id = ('account', 'label')['label_api' in self.rpc.caps] + for d in await self.rpc.call('listunspent', 0): + tw_lbl = get_tw_label(self.proto, d[lbl_id]) if tw_lbl: if tw_lbl.mmid.type == 'mmgen': label = tw_lbl.mmid.obj.sid @@ -50,7 +50,7 @@ class BitcoinTwGetBalance(TwGetBalance): self.data['TOTAL']['unconfirmed'] += amt self.data[label]['unconfirmed'] += amt - col_key = ('lt_minconf','ge_minconf')[d['confirmations'] >= self.minconf] + col_key = ('lt_minconf', 'ge_minconf')[d['confirmations'] >= self.minconf] self.data['TOTAL'][col_key] += amt self.data[label][col_key] += amt @@ -61,7 +61,7 @@ class BitcoinTwGetBalance(TwGetBalance): def gen_spendable_warning(): if check_spendable: - for k,v in self.data.items(): + for k, v in self.data.items(): if v['spendable']: yield red(f'Warning: this wallet contains PRIVATE KEYS for {k} outputs!') diff --git a/mmgen/proto/btc/tw/ctl.py b/mmgen/proto/btc/tw/ctl.py index 0b2c69fe..b3c211a1 100755 --- a/mmgen/proto/btc/tw/ctl.py +++ b/mmgen/proto/btc/tw/ctl.py @@ -12,18 +12,18 @@ proto.btc.tw.ctl: Bitcoin base protocol tracking wallet control class """ -from ....tw.ctl import TwCtl,write_mode -from ....util import msg,msg_r,rmsg,die,suf,fmt_list +from ....tw.ctl import TwCtl, write_mode +from ....util import msg, msg_r, rmsg, die, suf, fmt_list class BitcoinTwCtl(TwCtl): def init_empty(self): - self.data = { 'coin': self.proto.coin, 'addresses': {} } + self.data = {'coin': self.proto.coin, 'addresses': {}} def upgrade_wallet_maybe(self): pass - async def rpc_get_balance(self,addr): + async def rpc_get_balance(self, addr): raise NotImplementedError('not implemented') @write_mode @@ -34,7 +34,7 @@ class BitcoinTwCtl(TwCtl): return await self.rpc.call('importaddress', addr, label, rescan) @write_mode - async def batch_import_address(self,arg_list): + async def batch_import_address(self, arg_list): if (await self.rpc.walletinfo).get('descriptors'): from ....contrib.descriptors import descsum_create return await self.rpc.call( @@ -48,12 +48,12 @@ class BitcoinTwCtl(TwCtl): return await self.rpc.batch_call('importaddress', arg_list) @write_mode - async def remove_address(self,addr): + async def remove_address(self, addr): raise NotImplementedError(f'address removal not implemented for coin {self.proto.coin}') @write_mode - async def set_label(self,coinaddr,lbl): - args = self.rpc.daemon.set_comment_args( self.rpc, coinaddr, lbl ) + async def set_label(self, coinaddr, lbl): + args = self.rpc.daemon.set_comment_args(self.rpc, coinaddr, lbl) try: await self.rpc.call(*args) return True @@ -62,80 +62,80 @@ class BitcoinTwCtl(TwCtl): return False @write_mode - async def rescan_blockchain(self,start,stop): + async def rescan_blockchain(self, start, stop): start = start or 0 endless = stop is None CR = '\n' if self.cfg.test_suite else '\r' if not (start >= 0 and (stop if stop is not None else start) >= start): - die(1,f'{start} {stop}: invalid range') + die(1, f'{start} {stop}: invalid range') - async def do_scan(chunks,tip): + async def do_scan(chunks, tip): res = None - for a,b in chunks: + for a, b in chunks: msg_r(f'{CR}Scanning blocks {a}-{b} ') - res = await self.rpc.call('rescanblockchain',a,b,timeout=7200) + res = await self.rpc.call('rescanblockchain', a, b, timeout=7200) if res['start_height'] != a or res['stop_height'] != b: - die(1,f'\nAn error occurred in block range {a}-{b}') + die(1, f'\nAn error occurred in block range {a}-{b}') msg('') return b if res else tip - def gen_chunks(start,stop,tip): + def gen_chunks(start, stop, tip): n = start if endless: stop = tip elif stop > tip: - die(1,f'{stop}: stop value is higher than chain tip') + die(1, f'{stop}: stop value is higher than chain tip') while n <= stop: - yield ( n, min(n+99,stop) ) + yield (n, min(n+99, stop)) n += 100 - last_block = await do_scan(gen_chunks(start,stop,self.rpc.blockcount),self.rpc.blockcount) + last_block = await do_scan(gen_chunks(start, stop, self.rpc.blockcount), self.rpc.blockcount) if endless: tip = await self.rpc.call('getblockcount') while last_block < tip: - last_block = await do_scan(gen_chunks(last_block+1,tip,tip),tip) + last_block = await do_scan(gen_chunks(last_block+1, tip, tip), tip) tip = await self.rpc.call('getblockcount') msg('Done') @write_mode - async def rescan_address(self,addrspec): + async def rescan_address(self, addrspec): res = await self.resolve_address(addrspec) if not res: return False return await self.rescan_addresses([res.coinaddr]) @write_mode - async def rescan_addresses(self,coin_addrs): + async def rescan_addresses(self, coin_addrs): from ..misc import scantxoutset - res = await scantxoutset( self.cfg, self.rpc, [f'addr({addr})' for addr in coin_addrs] ) + res = await scantxoutset(self.cfg, self.rpc, [f'addr({addr})' for addr in coin_addrs]) 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'] }) + 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) )) - self.cfg._util.vmsg(f'Blocks to rescan: {fmt_list(blocks,fmt="bare")}') + suf(blocks))) + self.cfg._util.vmsg(f'Blocks to rescan: {fmt_list(blocks, fmt="bare")}') CR = '\n' if self.cfg.test_suite else '\r' - for n,block in enumerate(blocks): + 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) + await self.rpc.call('rescanblockchain', block, block, timeout=60) msg(f'\nAddress balance{suf(coin_addrs)} updated successfully') return True else: msg('Address has no balance' if len(coin_addrs) == 1 else - 'Addresses have no balances' ) + 'Addresses have no balances') return True async def get_label_addr_pairs(self): diff --git a/mmgen/proto/btc/tw/json.py b/mmgen/proto/btc/tw/json.py index 2e0582f1..b08a721e 100755 --- a/mmgen/proto/btc/tw/json.py +++ b/mmgen/proto/btc/tw/json.py @@ -24,13 +24,13 @@ class BitcoinTwJSON(TwJSON): @property def mappings_json(self): - return self.json_dump([(e.mmgen_id,e.address) for e in self.entries]) + return self.json_dump([(e.mmgen_id, e.address) for e in self.entries]) @property def num_entries(self): return len(self.entries) - class Import(TwJSON.Import,Base): + class Import(TwJSON.Import, Base): info_msg = """ This utility will create a new tracking wallet, import the addresses from @@ -63,48 +63,48 @@ class BitcoinTwJSON(TwJSON): entries_in = [self.entry_tuple_in(*e) for e in self.data['data']['entries']] return sorted( [self.entry_tuple( - TwMMGenID(self.proto,d.mmgen_id), + TwMMGenID(self.proto, d.mmgen_id), d.address, - getattr(d,'amount',None), + getattr(d, 'amount', None), d.comment) for d in entries_in], - key = lambda x: x.mmgen_id.sort_key ) + key = lambda x: x.mmgen_id.sort_key) - async def do_import(self,batch): - import_tuple = namedtuple('import_data',['addr','twmmid','comment']) + async def do_import(self, batch): + import_tuple = namedtuple('import_data', ['addr', 'twmmid', 'comment']) await self.twctl.import_address_common( [import_tuple(e.address, e.mmgen_id, e.comment) for e in self.entries], - batch = batch ) + batch = batch) return [e.address for e in self.entries] - class Export(TwJSON.Export,Base): + class Export(TwJSON.Export, Base): @property async def addrlist(self): - if not hasattr(self,'_addrlist'): + if not hasattr(self, '_addrlist'): if self.prune: from .prune import TwAddressesPrune self._addrlist = al = await TwAddressesPrune( self.cfg, self.proto, get_data = True, - warn_used = self.warn_used ) + warn_used = self.warn_used) await al.view_filter_and_sort() self.pruned = al.do_prune() else: from .addresses import TwAddresses - self._addrlist = await TwAddresses(self.cfg,self.proto,get_data=True) + self._addrlist = await TwAddresses(self.cfg, self.proto, get_data=True) return self._addrlist async def get_entries(self): # TODO: include 'received' field return sorted( [self.entry_tuple(d.twmmid, d.addr, d.amt, d.comment) for d in (await self.addrlist).data], - key = lambda x: x.mmgen_id.sort_key ) + key = lambda x: x.mmgen_id.sort_key) @property async def entries_out(self): - return [[getattr(d,k) for k in self.keys] for d in self.entries] + return [[getattr(d, k) for k in self.keys] for d in self.entries] @property async def total(self): diff --git a/mmgen/proto/btc/tw/prune.py b/mmgen/proto/btc/tw/prune.py index 1fa7bccd..4e4a67a3 100755 --- a/mmgen/proto/btc/tw/prune.py +++ b/mmgen/proto/btc/tw/prune.py @@ -15,7 +15,7 @@ proto.btc.tw.prune: Bitcoin base protocol tracking wallet address list prune cla from ....tw.prune import TwAddressesPrune from .addresses import BitcoinTwAddresses -class BitcoinTwAddressesPrune(BitcoinTwAddresses,TwAddressesPrune): +class BitcoinTwAddressesPrune(BitcoinTwAddresses, TwAddressesPrune): prompt_fs_in = [ 'Sort options: [a]mt, [A]ge, [M]mgen addr, [r]everse', @@ -40,4 +40,4 @@ class BitcoinTwAddressesPrune(BitcoinTwAddresses,TwAddressesPrune): 'w':'a_view_detail', 'p':'a_prune', 'u':'a_unprune', - 'c':'a_clear_prune_list' } + 'c':'a_clear_prune_list'} diff --git a/mmgen/proto/btc/tw/rpc.py b/mmgen/proto/btc/tw/rpc.py index 077dfde4..4258066e 100755 --- a/mmgen/proto/btc/tw/rpc.py +++ b/mmgen/proto/btc/tw/rpc.py @@ -23,32 +23,32 @@ class BitcoinTwRPC(TwRPC): async def get_label_addr_pairs(self): """ Get all the accounts in the tracking wallet and their associated addresses. - Returns list of (label,address) tuples. + Returns list of (label, address) tuples. """ def check_dup_mmid(acct_labels): - mmid_prev,err = None,False + mmid_prev, err = None, False for mmid in sorted(label.mmid for label in acct_labels if label): if mmid == mmid_prev: err = True msg(f'Duplicate MMGen ID ({mmid}) discovered in tracking wallet!\n') mmid_prev = mmid if err: - die(4,'Tracking wallet is corrupted!') + die(4, 'Tracking wallet is corrupted!') async def get_acct_list(): if 'label_api' in self.rpc.caps: return await self.rpc.call('listlabels') else: - return (await self.rpc.call('listaccounts',0,True)).keys() + return (await self.rpc.call('listaccounts', 0, True)).keys() async def get_acct_addrs(acct_list): if 'label_api' in self.rpc.caps: return [list(a.keys()) - for a in await self.rpc.batch_call('getaddressesbylabel',[(k,) for k in acct_list])] + for a in await self.rpc.batch_call('getaddressesbylabel', [(k,) for k in acct_list])] else: - return await self.rpc.batch_call('getaddressesbyaccount',[(a,) for a in acct_list]) + return await self.rpc.batch_call('getaddressesbyaccount', [(a,) for a in acct_list]) - acct_labels = [get_tw_label(self.proto,a) for a in await get_acct_list()] + acct_labels = [get_tw_label(self.proto, a) for a in await get_acct_list()] if not acct_labels: return [] @@ -57,23 +57,23 @@ class BitcoinTwRPC(TwRPC): acct_addrs = await get_acct_addrs(acct_labels) - for n,a in enumerate(acct_addrs): + for n, a in enumerate(acct_addrs): if len(a) != 1: raise ValueError(f'{a}: label {acct_labels[n]!r} has != 1 associated address!') - return [label_addr_pair(label, CoinAddr(self.proto,addrs[0])) + return [label_addr_pair(label, CoinAddr(self.proto, addrs[0])) for label, addrs in zip(acct_labels, acct_addrs)] - async def get_unspent_by_mmid(self,minconf=1,mmid_filter=[]): + async def get_unspent_by_mmid(self, minconf=1, mmid_filter=[]): """ get unspent outputs in tracking wallet, compute balances per address - and return a dict with elements { 'twmmid': {'addr','lbl','amt'} } + and return a dict with elements {'twmmid': {'addr', 'lbl', 'amt'}} """ data = {} - lbl_id = ('account','label')['label_api' in self.rpc.caps] + lbl_id = ('account', 'label')['label_api' in self.rpc.caps] amt0 = self.proto.coin_amt('0') - for d in await self.rpc.call('listunspent',0): + for d in await self.rpc.call('listunspent', 0): if not lbl_id in d: continue # skip coinbase outputs with missing account @@ -81,7 +81,7 @@ class BitcoinTwRPC(TwRPC): if d['confirmations'] < minconf: continue - label = get_tw_label(self.proto,d[lbl_id]) + label = get_tw_label(self.proto, d[lbl_id]) if label: lm = label.mmid @@ -89,10 +89,10 @@ class BitcoinTwRPC(TwRPC): continue if lm in data: if data[lm]['addr'] != d['address']: - die(2,'duplicate {} address ({}) for this MMGen address! ({})'.format( + die(2, 'duplicate {} address ({}) for this MMGen address! ({})'.format( self.proto.coin, d['address'], - data[lm]['addr'] )) + data[lm]['addr'])) else: lm.confs = d['confirmations'] lm.txid = d['txid'] @@ -101,7 +101,7 @@ class BitcoinTwRPC(TwRPC): data[lm] = { 'amt': amt0, 'lbl': label, - 'addr': CoinAddr(self.proto,d['address']) } + 'addr': CoinAddr(self.proto, d['address'])} data[lm]['amt'] += self.proto.coin_amt(d['amount']) diff --git a/mmgen/proto/btc/tw/txhistory.py b/mmgen/proto/btc/tw/txhistory.py index 4bff4d91..a87bb4e1 100755 --- a/mmgen/proto/btc/tw/txhistory.py +++ b/mmgen/proto/btc/tw/txhistory.py @@ -14,19 +14,19 @@ proto.btc.tw.txhistory: Bitcoin base protocol tracking wallet transaction histor from collections import namedtuple from ....tw.txhistory import TwTxHistory -from ....tw.shared import get_tw_label,TwMMGenID +from ....tw.shared import get_tw_label, TwMMGenID from ....addr import CoinAddr -from ....util import msg,msg_r -from ....color import nocolor,red,pink,gray -from ....obj import TwComment,CoinTxID,Int +from ....util import msg, msg_r +from ....color import nocolor, red, pink, gray +from ....obj import TwComment, CoinTxID, Int from .rpc import BitcoinTwRPC class BitcoinTwTransaction: - def __init__(self,parent,proto,rpc, + def __init__(self, parent, proto, rpc, idx, # unique numeric identifier of this transaction in listing - unspent_info, # addrs in wallet with balances: { 'mmid': {'addr','comment','amt'} } - mm_map, # all addrs in wallet: ['addr', ['twmmid','comment']] + unspent_info, # addrs in wallet with balances: {'mmid': {'addr', 'comment', 'amt'}} + mm_map, # all addrs in wallet: ['addr', ['twmmid', 'comment']] tx, # the decoded transaction data wallet_vouts, # list of ints - wallet-related vouts prevouts, # list of (txid,vout) pairs @@ -41,21 +41,21 @@ class BitcoinTwTransaction: self.tx = tx def gen_prevouts_data(): - _d = namedtuple('prevout_data',['txid','data']) + _d = namedtuple('prevout_data', ['txid', 'data']) for tx in prevout_txs: for e in prevouts: if e.txid == tx['txid']: - yield _d( e.txid, tx['vout'][e.vout] ) + yield _d(e.txid, tx['vout'][e.vout]) def gen_wallet_vouts_data(): - _d = namedtuple('wallet_vout_data',['txid','data']) + _d = namedtuple('wallet_vout_data', ['txid', 'data']) txid = self.tx['txid'] vouts = self.tx['decoded']['vout'] for n in wallet_vouts: - yield _d( txid, vouts[n] ) + yield _d(txid, vouts[n]) def gen_vouts_info(data): - _d = namedtuple('vout_info',['txid','coin_addr','twlabel','data']) + _d = namedtuple('vout_info', ['txid', 'coin_addr', 'twlabel', 'data']) def gen(): for d in data: addr = d.data['scriptPubKey'].get('address') or d.data['scriptPubKey']['addresses'][0] @@ -63,7 +63,7 @@ class BitcoinTwTransaction: txid = d.txid, coin_addr = addr, twlabel = mm_map[addr] if (addr in mm_map and mm_map[addr].twmmid) else None, - data = d.data ) + data = d.data) return sorted( gen(), # if address is not MMGen, ignore address and sort by TxID + vout only @@ -79,7 +79,7 @@ class BitcoinTwTransaction: if e.twlabel: mmid = e.twlabel.twmmid yield ( - (mmid if mmid.type == 'mmgen' else mmid.split(':',1)[1]) + + (mmid if mmid.type == 'mmgen' else mmid.split(':', 1)[1]) + ('*' if mmid in self.unspent_info else '') ) else: @@ -93,15 +93,15 @@ class BitcoinTwTransaction: find the most relevant comment for tabular (squeezed) display """ def vouts_labels(src): - return [ d.twlabel.comment for d in self.vouts_info[src] if d.twlabel and d.twlabel.comment ] + return [d.twlabel.comment for d in self.vouts_info[src] if d.twlabel and d.twlabel.comment] ret = vouts_labels('outputs') or vouts_labels('inputs') return ret[0] if ret else TwComment('') coin_amt = self.proto.coin_amt # 'outputs' refers to wallet-related outputs only self.vouts_info = { - 'inputs': gen_vouts_info( gen_prevouts_data() ), - 'outputs': gen_vouts_info( gen_wallet_vouts_data() ) + 'inputs': gen_vouts_info(gen_prevouts_data()), + 'outputs': gen_vouts_info(gen_wallet_vouts_data()) } self.max_addrlen = { 'inputs': max(len(addr) for addr in gen_all_addrs('inputs')), @@ -123,13 +123,13 @@ class BitcoinTwTransaction: self.time = self.tx.get('blocktime') or self.tx['time'] self.time_received = self.tx.get('timereceived') - def blockheight_disp(self,color): + def blockheight_disp(self, color): return ( # old/altcoin daemons return no 'blockheight' field, so use confirmations instead - Int( self.rpc.blockcount + 1 - self.confirmations ).hl(color=color) - if self.confirmations > 0 else None ) + Int(self.rpc.blockcount + 1 - self.confirmations).hl(color=color) + if self.confirmations > 0 else None) - def age_disp(self,age_fmt,width,color): + def age_disp(self, age_fmt, width, color): if age_fmt == 'confs': ret_str = str(self.confirmations).ljust(width) return gray(ret_str) if self.confirmations < 0 and color else ret_str @@ -138,17 +138,17 @@ class BitcoinTwTransaction: ret_str = str(ret).ljust(width) return gray(ret_str) if ret < 0 and color else ret_str else: - return self.parent.date_formatter[age_fmt](self.rpc,self.tx.get('blocktime',0)) + return self.parent.date_formatter[age_fmt](self.rpc, self.tx.get('blocktime', 0)) - def txdate_disp(self,age_fmt): - return self.parent.date_formatter[age_fmt](self.rpc,self.time) + def txdate_disp(self, age_fmt): + return self.parent.date_formatter[age_fmt](self.rpc, self.time) - def txid_disp(self,color,width=None): - return self.txid.hl(color=color) if width is None else self.txid.truncate(width=width,color=color) + def txid_disp(self, color, width=None): + return self.txid.hl(color=color) if width is None else self.txid.truncate(width=width, color=color) def vouts_list_disp(self, src, color, indent, addr_view_pref): - fs1,fs2 = { + fs1, fs2 = { 'inputs': ('{i},{n} {a} {A}', '{i},{n} {a} {A} {l}'), 'outputs': ( '{n} {a} {A}', '{n} {a} {A} {l}') }[src] @@ -159,27 +159,27 @@ class BitcoinTwTransaction: if not mmid: yield fs1.format( i = CoinTxID(e.txid).hl(color=color), - n = (nocolor,red)[color](str(e.data['n']).ljust(3)), + n = (nocolor, red)[color](str(e.data['n']).ljust(3)), a = CoinAddr(self.proto, e.coin_addr).fmt( addr_view_pref, width=self.max_addrlen[src], color=color), - A = self.proto.coin_amt( e.data['value'] ).fmt(color=color) + A = self.proto.coin_amt(e.data['value']).fmt(color=color) ).rstrip() else: - bal_star,co = ('*','melon') if mmid in self.unspent_info else ('','brown') - addr_out = mmid if mmid.type == 'mmgen' else mmid.split(':',1)[1] + bal_star, co = ('*', 'melon') if mmid in self.unspent_info else ('', 'brown') + addr_out = mmid if mmid.type == 'mmgen' else mmid.split(':', 1)[1] yield fs2.format( i = CoinTxID(e.txid).hl(color=color), - n = (nocolor,red)[color](str(e.data['n']).ljust(3)), + n = (nocolor, red)[color](str(e.data['n']).ljust(3)), a = TwMMGenID.hl2( TwMMGenID, - s = '{:{w}}'.format( addr_out + bal_star, w=self.max_addrlen[src] ), + s = '{:{w}}'.format(addr_out + bal_star, w=self.max_addrlen[src]), color = color, - color_override = co ), - A = self.proto.coin_amt( e.data['value'] ).fmt(color=color), + color_override = co), + A = self.proto.coin_amt(e.data['value']).fmt(color=color), l = e.twlabel.comment.hl(color=color) ).rstrip() - return f'\n{indent}'.join( gen_output() ).strip() + return f'\n{indent}'.join(gen_output()).strip() def vouts_disp(self, src, width, color, addr_view_pref): @@ -189,7 +189,7 @@ class BitcoinTwTransaction: for e in self.vouts_info[src]: mmid = e.twlabel.twmmid if e.twlabel else None - bal_star,addr_w,co = ('*',16,'melon') if mmid in self.unspent_info else ('',15,'brown') + bal_star, addr_w, co = ('*', 16, 'melon') if mmid in self.unspent_info else ('', 15, 'brown') if not mmid: if width and space_left < addr_w: break @@ -199,16 +199,16 @@ class BitcoinTwTransaction: mmid_disp = mmid + bal_star if width and space_left < len(mmid_disp): break - yield TwMMGenID.hl2( TwMMGenID, s=mmid_disp, color=color, color_override=co ) + yield TwMMGenID.hl2(TwMMGenID, s=mmid_disp, color=color, color_override=co) space_left -= len(mmid_disp) else: if width and space_left < addr_w: break yield TwMMGenID.hl2( TwMMGenID, - s = CoinAddr.fmtc( mmid.split(':',1)[1] + bal_star, width=addr_w ), + s = CoinAddr.fmtc(mmid.split(':', 1)[1] + bal_star, width=addr_w), color = color, - color_override = co ) + color_override = co) space_left -= addr_w space_left -= 1 @@ -216,20 +216,20 @@ class BitcoinTwTransaction: return ' '.join(gen_output()) + ' ' * (space_left + 1 if width else 0) - def amt_disp(self,show_total_amt): + def amt_disp(self, show_total_amt): return ( self.outputs_total if show_total_amt else - self.wallet_outputs_total ) + self.wallet_outputs_total) - def fee_disp(self,color): + def fee_disp(self, color): atomic_unit = self.proto.coin_amt.units[0] return '{} {}'.format( self.fee.hl(color=color), - (nocolor,pink)[color]('({:,} {}s/byte)'.format( + (nocolor, pink)[color]('({:,} {}s/byte)'.format( self.fee.to_unit(atomic_unit) // self.vsize, - atomic_unit )) ) + atomic_unit))) -class BitcoinTwTxHistory(TwTxHistory,BitcoinTwRPC): +class BitcoinTwTxHistory(TwTxHistory, BitcoinTwRPC): has_age = True hdr_lbl = 'transaction history' @@ -259,12 +259,12 @@ class BitcoinTwTxHistory(TwTxHistory,BitcoinTwRPC): 'v':'a_view', 'V':'a_view_detail', 'p':'a_print_squeezed', - 'P':'a_print_detail' } + 'P':'a_print_detail'} async def get_rpc_data(self): blockhash = ( - await self.rpc.call( 'getblockhash', self.sinceblock ) - if self.sinceblock else '' ) + await self.rpc.call('getblockhash', self.sinceblock) + if self.sinceblock else '') # bitcoin-cli help listsinceblock: # Arguments: # 1. blockhash (string, optional) If set, the block hash to list transactions since, @@ -277,14 +277,14 @@ class BitcoinTwTxHistory(TwTxHistory,BitcoinTwRPC): # 4. include_removed (boolean, optional, default=true) Show transactions that were removed # due to a reorg in the "removed" array (not guaranteed to work on # pruned nodes) - return (await self.rpc.call('listsinceblock',blockhash,1,True,False))['transactions'] + return (await self.rpc.call('listsinceblock', blockhash, 1, True, False))['transactions'] - async def gen_data(self,rpc_data,lbl_id): + async def gen_data(self, rpc_data, lbl_id): def gen_parsed_data(): for o in rpc_data: if lbl_id in o: - l = get_tw_label(self.proto,o[lbl_id]) + l = get_tw_label(self.proto, o[lbl_id]) else: assert o['category'] == 'send', f"{o['address']}: {o['category']} != 'send'" l = None @@ -301,18 +301,18 @@ class BitcoinTwTxHistory(TwTxHistory,BitcoinTwRPC): from ....rpc import json_encoder def do_json_dump(*data): nw = f'{self.proto.coin.lower()}-{self.proto.network}' - for d,fn_stem in data: - with open(f'/tmp/{fn_stem}-{nw}.json','w') as fh: - fh.write(json.dumps(d,cls=json_encoder)) + for d, fn_stem in data: + with open(f'/tmp/{fn_stem}-{nw}.json', 'w') as fh: + fh.write(json.dumps(d, cls=json_encoder)) - _mmp = namedtuple('mmap_datum',['twmmid','comment']) + _mmp = namedtuple('mmap_datum', ['twmmid', 'comment']) mm_map = { i['address']: ( - _mmp( TwMMGenID(self.proto,i['twmmid']), TwComment(i['comment']) ) - if i['twmmid'] else _mmp(None,None) + _mmp(TwMMGenID(self.proto, i['twmmid']), TwComment(i['comment'])) + if i['twmmid'] else _mmp(None, None) ) - for i in data } + for i in data} if self.sinceblock: # mapping data may be incomplete for inputs, so update from 'listlabels' mm_map.update( @@ -323,41 +323,41 @@ class BitcoinTwTxHistory(TwTxHistory,BitcoinTwRPC): msg_r('Getting wallet transactions...') _wallet_txs = await self.rpc.gathered_icall( 'gettransaction', - [ (i,True,True) for i in {d['txid'] for d in data} ] ) + [(i, True, True) for i in {d['txid'] for d in data}]) msg('done') if not 'decoded' in _wallet_txs[0]: _decoded_txs = iter( await self.rpc.gathered_call( 'decoderawtransaction', - [ (d['hex'],) for d in _wallet_txs ] )) + [(d['hex'],) for d in _wallet_txs])) for tx in _wallet_txs: tx['decoded'] = next(_decoded_txs) if self.cfg.debug_tw: do_json_dump((_wallet_txs, 'wallet-txs'),) - _wip = namedtuple('prevout',['txid','vout']) + _wip = namedtuple('prevout', ['txid', 'vout']) txdata = [ { 'tx': tx, 'wallet_vouts': sorted({i.vout for i in - [_wip( CoinTxID(d['txid']), d['vout'] ) for d in data] + [_wip(CoinTxID(d['txid']), d['vout']) for d in data] if i.txid == tx['txid']}), - 'prevouts': [_wip( CoinTxID(vin['txid']), vin['vout'] ) for vin in tx['decoded']['vin']] + 'prevouts': [_wip(CoinTxID(vin['txid']), vin['vout']) for vin in tx['decoded']['vin']] } for tx in _wallet_txs] _prevout_txids = {i.txid for d in txdata for i in d['prevouts']} msg_r('Getting input transactions...') - _prevout_txs = await self.rpc.gathered_call('getrawtransaction', [ (i,True) for i in _prevout_txids ]) + _prevout_txs = await self.rpc.gathered_call('getrawtransaction', [(i, True) for i in _prevout_txids]) msg('done') - _prevout_txs_dict = dict(zip(_prevout_txids,_prevout_txs)) + _prevout_txs_dict = dict(zip(_prevout_txids, _prevout_txs)) for d in txdata: - d['prevout_txs'] = [_prevout_txs_dict[txid] for txid in {i.txid for i in d['prevouts']} ] + d['prevout_txs'] = [_prevout_txs_dict[txid] for txid in {i.txid for i in d['prevouts']}] if self.cfg.debug_tw: do_json_dump( @@ -378,4 +378,4 @@ class BitcoinTwTxHistory(TwTxHistory,BitcoinTwRPC): idx = idx, unspent_info = unspent_info, mm_map = mm_map, - **d ) for idx,d in enumerate(txdata) ) + **d) for idx, d in enumerate(txdata)) diff --git a/mmgen/proto/btc/tw/unspent.py b/mmgen/proto/btc/tw/unspent.py index b63b162a..0dab0a78 100755 --- a/mmgen/proto/btc/tw/unspent.py +++ b/mmgen/proto/btc/tw/unspent.py @@ -18,7 +18,18 @@ class BitcoinTwUnspentOutputs(TwUnspentOutputs): class MMGenTwUnspentOutput(TwUnspentOutputs.MMGenTwUnspentOutput): # required by gen_unspent(); setting valid_attrs explicitly is also more efficient - valid_attrs = {'txid','vout','amt','amt2','comment','twmmid','addr','confs','date','scriptPubKey','skip'} + valid_attrs = { + 'txid', + 'vout', + 'amt', + 'amt2', + 'comment', + 'twmmid', + 'addr', + 'confs', + 'date', + 'scriptPubKey', + 'skip'} invalid_attrs = {'proto'} has_age = True @@ -50,7 +61,7 @@ class BitcoinTwUnspentOutputs(TwUnspentOutputs): 'p':'a_print_detail', 'v':'a_view', 'w':'a_view_detail', - 'l':'i_comment_add' } + 'l':'i_comment_add'} async def get_rpc_data(self): # bitcoin-cli help listunspent: @@ -62,5 +73,5 @@ class BitcoinTwUnspentOutputs(TwUnspentOutputs): # 5. query_options (json object, optional) JSON with query options # for now, self.addrs is just an empty list for Bitcoin and friends - add_args = (9999999,self.addrs) if self.addrs else () - return await self.rpc.call('listunspent',self.minconf,*add_args) + add_args = (9999999, self.addrs) if self.addrs else () + return await self.rpc.call('listunspent', self.minconf, *add_args) diff --git a/mmgen/proto/btc/tx/base.py b/mmgen/proto/btc/tx/base.py index bb7ca3a8..7a993765 100755 --- a/mmgen/proto/btc/tx/base.py +++ b/mmgen/proto/btc/tx/base.py @@ -91,7 +91,7 @@ def DeserializeTX(proto, txhex): raw_tx = bytearray() idx = 0 - d = { 'version': bytes2int(bshift(4)) } + d = {'version': bytes2int(bshift(4))} if d['version'] > 0x7fffffff: # version is signed integer die(3, f"{d['version']}: transaction version greater than maximum allowed value (int32_t)!") @@ -134,7 +134,7 @@ def DeserializeTX(proto, txhex): bshift(1, skip=True) continue txin['witness'] = [ - bshift(readVInt(skip=True), skip=True).hex() for item in range(readVInt(skip=True)) ] + bshift(readVInt(skip=True), skip=True).hex() for item in range(readVInt(skip=True))] else: d['txid'] = make_txid(tx) d['witness_size'] = 0 diff --git a/mmgen/proto/btc/tx/bump.py b/mmgen/proto/btc/tx/bump.py index 5f02c24b..d7d7c541 100755 --- a/mmgen/proto/btc/tx/bump.py +++ b/mmgen/proto/btc/tx/bump.py @@ -18,21 +18,21 @@ from .new import New from .completed import Completed from .unsigned import AutomountUnsigned -class Bump(Completed,New,TxBase.Bump): +class Bump(Completed, New, TxBase.Bump): desc = 'fee-bumped transaction' @property def min_fee(self): return self.sum_inputs() - self.sum_outputs() + self.relay_fee - def bump_fee(self,idx,fee): + def bump_fee(self, idx, fee): self.update_output_amt( idx, self.sum_inputs() - self.sum_outputs(exclude=idx) - fee ) - def convert_and_check_fee(self,fee,desc): - ret = super().convert_and_check_fee(fee,desc) + def convert_and_check_fee(self, fee, desc): + ret = super().convert_and_check_fee(fee, desc) if ret is False: return ret if ret < self.min_fee: @@ -42,7 +42,7 @@ class Bump(Completed,New,TxBase.Bump): self.min_fee, self.fee_abs2rel(self.min_fee), self.rel_fee_desc, - c = self.coin )) + c = self.coin)) return False output_amt = self.outputs[self.bump_output_idx].amt if ret >= output_amt: @@ -50,7 +50,7 @@ class Bump(Completed,New,TxBase.Bump): ret.hl(), desc, output_amt.hl(), - c = self.coin )) + c = self.coin)) return False return ret diff --git a/mmgen/proto/btc/tx/completed.py b/mmgen/proto/btc/tx/completed.py index 2fd47ee7..ea55cc55 100755 --- a/mmgen/proto/btc/tx/completed.py +++ b/mmgen/proto/btc/tx/completed.py @@ -14,10 +14,10 @@ proto.btc.tx.completed: Bitcoin completed transaction class from ....tx import completed as TxBase from ....obj import HexStr -from ....util import msg,die -from .base import Base,scriptPubKey2addr +from ....util import msg, die +from .base import Base, scriptPubKey2addr -class Completed(Base,TxBase.Completed): +class Completed(Base, TxBase.Completed): fn_fee_unit = 'satoshi' # check signature and witness data @@ -30,33 +30,35 @@ class Completed(Base,TxBase.Completed): fs = "Hex TX has {} scriptSig but input is of type '{}'!" for n, ti in enumerate(txins): mmti = self.inputs[n] - if ti['scriptSig'] == '' or ( len(ti['scriptSig']) == 46 and # native P2WPKH or P2SH-P2WPKH - ti['scriptSig'][:6] == '16' + self.proto.witness_vernum_hex + '14' ): + if ti['scriptSig'] == '' or (len(ti['scriptSig']) == 46 and # native P2WPKH or P2SH-P2WPKH + ti['scriptSig'][:6] == '16' + self.proto.witness_vernum_hex + '14'): assert 'witness' in ti, 'missing witness' - assert isinstance(ti['witness'],list) and len(ti['witness']) == 2, 'malformed witness' + assert isinstance(ti['witness'], list) and len(ti['witness']) == 2, 'malformed witness' assert len(ti['witness'][1]) == 66, 'incorrect witness pubkey length' - assert mmti.mmtype == ('S','B')[ti['scriptSig']==''], fs.format('witness-type',mmti.mmtype) + assert mmti.mmtype == ('S', 'B')[ti['scriptSig']==''], fs.format('witness-type', mmti.mmtype) else: # non-witness - assert mmti.mmtype not in ('S','B'), fs.format('signature in',mmti.mmtype) + assert mmti.mmtype not in ('S', 'B'), fs.format('signature in', mmti.mmtype) assert not 'witness' in ti, 'non-witness input has witness' # sig_size 72 (DER format), pubkey_size 'compressed':33, 'uncompressed':65 assert (200 < len(ti['scriptSig']) < 300), 'malformed scriptSig' # VERY rough check return True def check_pubkey_scripts(self): - for n,i in enumerate(self.inputs,1): - addr,fmt = scriptPubKey2addr(self.proto,i.scriptPubKey) + for n, i in enumerate(self.inputs, 1): + addr, fmt = scriptPubKey2addr(self.proto, i.scriptPubKey) if i.addr != addr: if fmt != i.addr.addr_fmt: m = 'Address format of scriptPubKey ({}) does not match that of address ({}) in input #{}' - msg(m.format(fmt,i.addr.addr_fmt,n)) + msg(m.format(fmt, i.addr.addr_fmt, n)) m = 'ERROR: Address and scriptPubKey of transaction input #{} do not match!' - die(3,(m+'\n {:23}{}'*3).format(n, 'address:',i.addr, - 'scriptPubKey:',i.scriptPubKey, - 'scriptPubKey->address:',addr )) + die(3, (m+'\n {:23}{}'*3).format( + n, + 'address:', i.addr, + 'scriptPubKey:', i.scriptPubKey, + 'scriptPubKey->address:', addr)) # def is_replaceable_from_rpc(self): -# dec_tx = await self.rpc.call('decoderawtransaction',self.serialized) +# dec_tx = await self.rpc.call('decoderawtransaction', self.serialized) # return None < dec_tx['vin'][0]['sequence'] <= self.proto.max_int - 2 def is_replaceable(self): @@ -83,4 +85,4 @@ class Completed(Base,TxBase.Completed): return self.sum_outputs() - self.send_amt def get_serialized_locktime(self): - return int(bytes.fromhex(self.serialized[-8:])[::-1].hex(),16) + return int(bytes.fromhex(self.serialized[-8:])[::-1].hex(), 16) diff --git a/mmgen/proto/btc/tx/info.py b/mmgen/proto/btc/tx/info.py index 4e5a29f9..19777f25 100755 --- a/mmgen/proto/btc/tx/info.py +++ b/mmgen/proto/btc/tx/info.py @@ -13,12 +13,12 @@ proto.btc.tx.info: Bitcoin transaction info class """ from ....tx.info import TxInfo -from ....util import fmt,die -from ....color import red,green,pink +from ....util import fmt, die +from ....color import red, green, pink from ....addr import MMGenID class TxInfo(TxInfo): - sort_orders = ('addr','raw') + sort_orders = ('addr', 'raw') txinfo_hdr_fs = 'TRANSACTION DATA\n\nID={i} ({a} {c}) RBF={r} Sig={s} Locktime={l}\n' txinfo_hdr_fs_short = 'TX {i} ({a} {c}) RBF={r} Sig={s} Locktime={l}\n' txinfo_ftr_fs = fmt(""" @@ -33,17 +33,17 @@ class TxInfo(TxInfo): return ' ({} {}, {} of spend amount)'.format( pink(tx.fee_abs2rel(tx.fee)), tx.rel_fee_disp, - pink('{:0.6f}%'.format( tx.fee / tx.send_amt * 100 )) + pink('{:0.6f}%'.format(tx.fee / tx.send_amt * 100)) ) - def format_abs_fee(self,color,iwidth): + def format_abs_fee(self, color, iwidth): return self.tx.fee.fmt(color=color, iwidth=iwidth) def format_verbose_footer(self): tx = self.tx tsize = len(tx.serialized) // 2 if tx.serialized else 'unknown' out = f'Transaction size: Vsize {tx.estimate_size()} (estimated), Total {tsize}' - if tx.name in ('Signed','OnlineSigned'): + if tx.name in ('Signed', 'OnlineSigned'): wsize = tx.deserialized.witness_size out += f', Base {tsize-wsize}, Witness {wsize}' return out + '\n' @@ -51,9 +51,9 @@ class TxInfo(TxInfo): def format_body(self, blockcount, nonmm_str, max_mmwid, enl, terse, sort): if sort not in self.sort_orders: - die(1,'{!r}: invalid transaction view sort order. Valid options: {}'.format( + die(1, '{!r}: invalid transaction view sort order. Valid options: {}'.format( sort, - ','.join(self.sort_orders) )) + ','.join(self.sort_orders))) def get_mmid_fmt(e, is_input): if e.mmid: @@ -61,13 +61,13 @@ class TxInfo(TxInfo): width=max_mmwid, encl='()', color=True, - append_chars=('',' (chg)')[bool(not is_input and e.is_chg and terse)], + append_chars=('', ' (chg)')[bool(not is_input and e.is_chg and terse)], append_color='green') else: - return MMGenID.fmtc( nonmm_str, width=max_mmwid, color=True ) + return MMGenID.fmtc(nonmm_str, width=max_mmwid, color=True) def format_io(desc): - io = getattr(tx,desc) + io = getattr(tx, desc) is_input = desc == 'inputs' yield desc.capitalize() + ':\n' + enl confs_per_day = 60*60*24 // tx.proto.avg_bdi @@ -75,25 +75,25 @@ class TxInfo(TxInfo): io_sorted = { 'addr': lambda: sorted( io, # prepend '+' (sorts before '0') to ensure non-MMGen addrs are displayed first - key = lambda o: (o.mmid.sort_key if o.mmid else f'+{o.addr}') + f'{o.amt:040.20f}' ), + key = lambda o: (o.mmid.sort_key if o.mmid else f'+{o.addr}') + f'{o.amt:040.20f}'), 'raw': lambda: io }[sort] if terse: iwidth = max(len(str(int(e.amt))) for e in io) - addr_w = max(len(e.addr.views[vp1]) for f in (tx.inputs,tx.outputs) for e in f) - for n,e in enumerate(io_sorted()): + addr_w = max(len(e.addr.views[vp1]) for f in (tx.inputs, tx.outputs) for e in f) + for n, e in enumerate(io_sorted()): yield '{:3} {} {} {} {}\n'.format( n+1, e.addr.fmt(vp1, width=addr_w, color=True), get_mmid_fmt(e, is_input), - e.amt.fmt(iwidth=iwidth,color=True), - tx.dcoin ) + e.amt.fmt(iwidth=iwidth, color=True), + tx.dcoin) if have_bch: yield '{:3} [{}]\n'.format('', e.addr.hl(vp2, color=False)) else: col1_w = len(str(len(io))) + 1 - for n,e in enumerate(io_sorted()): + for n, e in enumerate(io_sorted()): mmid_fmt = get_mmid_fmt(e, is_input) if is_input and blockcount: confs = e.confs + blockcount - tx.blockcount @@ -115,7 +115,7 @@ class TxInfo(TxInfo): yield ('', 'confirmations:', f'{confs} (around {days} days)') if not is_input and e.is_chg: yield ('', 'change:', green('True')) - yield '\n'.join('{:>{w}} {:<8} {}'.format(*d,w=col1_w) for d in gen()) + '\n\n' + yield '\n'.join('{:>{w}} {:<8} {}'.format(*d, w=col1_w) for d in gen()) + '\n\n' tx = self.tx @@ -128,12 +128,12 @@ class TxInfo(TxInfo): vp1 = 0 return ( - 'Displaying inputs and outputs in {} sort order'.format({'raw':'raw','addr':'address'}[sort]) - + ('\n\n','\n')[terse] + 'Displaying inputs and outputs in {} sort order'.format({'raw':'raw', 'addr':'address'}[sort]) + + ('\n\n', '\n')[terse] + ''.join(format_io('inputs')) - + ''.join(format_io('outputs')) ) + + ''.join(format_io('outputs'))) - def strfmt_locktime(self,locktime=None,terse=False): + def strfmt_locktime(self, locktime=None, terse=False): # Locktime itself is an unsigned 4-byte integer which can be parsed two ways: # # If less than 500 million, locktime is parsed as a block height. The transaction can be @@ -147,11 +147,11 @@ class TxInfo(TxInfo): if num is None: return '(None)' elif num.bit_length() > 32: - die(2,f'{num!r}: invalid nLockTime value (integer size greater than 4 bytes)!') + die(2, f'{num!r}: invalid nLockTime value (integer size greater than 4 bytes)!') elif num >= 500_000_000: import time - return ' '.join(time.strftime('%c',time.gmtime(num)).split()[1:]) + return ' '.join(time.strftime('%c', time.gmtime(num)).split()[1:]) elif num > 0: - return '{}{}'.format(('block height ','')[terse],num) + return '{}{}'.format(('block height ', '')[terse], num) else: - die(2,f'{num!r}: invalid nLockTime value!') + die(2, f'{num!r}: invalid nLockTime value!') diff --git a/mmgen/proto/btc/tx/new.py b/mmgen/proto/btc/tx/new.py index 1dcfe7e0..bd967ba5 100755 --- a/mmgen/proto/btc/tx/new.py +++ b/mmgen/proto/btc/tx/new.py @@ -18,7 +18,7 @@ from ....util import msg, fmt, make_chksum_6, die, suf from ....color import pink from .base import Base -class New(Base,TxBase.New): +class New(Base, TxBase.New): usr_fee_prompt = 'Enter transaction fee: ' fee_fail_fs = 'Network fee estimation for {c} confirmations failed ({t})' no_chg_msg = 'Warning: Change address will be deleted as transaction produces no change' @@ -68,7 +68,7 @@ class New(Base,TxBase.New): return self.proto.coin_amt(amt_in_units * tx_size, from_unit=units[unit]) # given network fee estimate in BTC/kB, return absolute fee using estimated tx size - def fee_est2abs(self,fee_per_kb,fe_type=None): + def fee_est2abs(self, fee_per_kb, fe_type=None): from decimal import Decimal tx_size = self.estimate_size() ret = self.proto.coin_amt('1') * (fee_per_kb * self.cfg.fee_adjust * tx_size / 1024) @@ -81,8 +81,8 @@ class New(Base,TxBase.New): """).strip()) return ret - def convert_and_check_fee(self,fee,desc): - abs_fee = self.feespec2abs(fee,self.estimate_size()) + def convert_and_check_fee(self, fee, desc): + abs_fee = self.feespec2abs(fee, self.estimate_size()) if abs_fee is None: raise ValueError(f'{fee}: cannot convert {self.rel_fee_desc} to {self.coin}' + ' because transaction size is unknown') @@ -101,7 +101,7 @@ class New(Base,TxBase.New): # Bitcoin full node, call doesn't go to the network, so just call listunspent with addrs=[] return [] - def update_change_output(self,funds_left): + def update_change_output(self, funds_left): if funds_left == 0: # TODO: test msg(self.no_chg_msg) self.outputs.pop(self.chg_idx) @@ -112,12 +112,12 @@ class New(Base,TxBase.New): fee = self.sum_inputs() - self.sum_outputs() if fee > self.proto.max_tx_fee: c = self.proto.coin - die( 'MaxFeeExceeded', f'Transaction fee of {fee} {c} too high! (> {self.proto.max_tx_fee} {c})' ) + die('MaxFeeExceeded', f'Transaction fee of {fee} {c} too high! (> {self.proto.max_tx_fee} {c})') - def final_inputs_ok_msg(self,funds_left): + def final_inputs_ok_msg(self, funds_left): return 'Transaction produces {} {} in change'.format(funds_left.hl(), self.coin) - async def create_serialized(self,locktime=None,bump=None): + async def create_serialized(self, locktime=None, bump=None): if not bump: self.inputs.sort_bip69() @@ -133,15 +133,15 @@ class New(Base,TxBase.New): 'txid': e.txid, 'vout': e.vout, 'sequence': e.sequence - } for e in self.inputs ] + } for e in self.inputs] outputs_dict = {e.addr:e.amt for e in self.outputs} - ret = await self.rpc.call( 'createrawtransaction', inputs_list, outputs_dict ) + ret = await self.rpc.call('createrawtransaction', inputs_list, outputs_dict) if locktime and not bump: msg(f'Setting nLockTime to {self.info.strfmt_locktime(locktime)}!') - assert isinstance(locktime,int), 'locktime value not an integer' + assert isinstance(locktime, int), 'locktime value not an integer' self.locktime = locktime ret = ret[:-8] + bytes.fromhex(f'{locktime:08x}')[::-1].hex() diff --git a/mmgen/proto/btc/tx/online.py b/mmgen/proto/btc/tx/online.py index 6fff9194..e73de869 100755 --- a/mmgen/proto/btc/tx/online.py +++ b/mmgen/proto/btc/tx/online.py @@ -13,27 +13,27 @@ proto.btc.tx.online: Bitcoin online signed transaction class """ from ....tx import online as TxBase -from ....util import msg,die +from ....util import msg, die from ....color import orange from .signed import Signed -class OnlineSigned(Signed,TxBase.OnlineSigned): +class OnlineSigned(Signed, TxBase.OnlineSigned): - async def send(self,prompt_user=True): + async def send(self, prompt_user=True): self.check_correct_chain() if not self.cfg.bogus_send: if self.has_segwit_outputs() and not self.rpc.info('segwit_is_active'): - die(2,'Transaction has Segwit outputs, but this blockchain does not support Segwit' + die(2, 'Transaction has Segwit outputs, but this blockchain does not support Segwit' + ' at the current height') if self.fee > self.proto.max_tx_fee: - die(2,'Transaction fee ({}) greater than {} max_tx_fee ({} {})!'.format( + die(2, 'Transaction fee ({}) greater than {} max_tx_fee ({} {})!'.format( self.fee, self.proto.name, self.proto.max_tx_fee, - self.proto.coin )) + self.proto.coin)) await self.status.display() @@ -45,7 +45,7 @@ class OnlineSigned(Signed,TxBase.OnlineSigned): else: m = 'Transaction sent: {}' try: - ret = await self.rpc.call('sendrawtransaction',self.serialized) + ret = await self.rpc.call('sendrawtransaction', self.serialized) except Exception as e: errmsg = str(e) nl = '\n' @@ -61,7 +61,7 @@ class OnlineSigned(Signed,TxBase.OnlineSigned): m = "Transaction with nLockTime {!r} can’t be included in this block!".format( self.info.strfmt_locktime(self.get_serialized_locktime())) else: - m,nl = ('','') + m, nl = ('', '') msg(orange('\n'+errmsg)) die(2, f'{m}{nl}Send of MMGen transaction {self.txid} failed') else: diff --git a/mmgen/proto/btc/tx/signed.py b/mmgen/proto/btc/tx/signed.py index b943fec5..9081ac17 100755 --- a/mmgen/proto/btc/tx/signed.py +++ b/mmgen/proto/btc/tx/signed.py @@ -13,19 +13,19 @@ proto.btc.tx.signed: Bitcoin signed transaction class """ from ....tx import signed as TxBase -from ....util import fmt,die +from ....util import fmt, die from .completed import Completed -class Signed(Completed,TxBase.Signed): +class Signed(Completed, TxBase.Signed): - def compare_size_and_estimated_size(self,tx_decoded): + def compare_size_and_estimated_size(self, tx_decoded): est_vsize = self.estimate_size() d = tx_decoded vsize = d['vsize'] if 'vsize' in d else d['size'] self.cfg._util.vmsg(f'\nVsize: {vsize} (true) {est_vsize} (estimated)') ratio = float(est_vsize) / vsize if not (0.95 < ratio < 1.05): # allow for 5% error - die( 'BadTxSizeEstimate', fmt(f""" + die('BadTxSizeEstimate', fmt(f""" Estimated transaction vsize is {ratio:1.2f} times the true vsize Your transaction fee estimates will be inaccurate Please re-create and re-sign the transaction using the option --vsize-adj={1/ratio:1.2f} diff --git a/mmgen/proto/btc/tx/status.py b/mmgen/proto/btc/tx/status.py index 5b76576e..8b323261 100755 --- a/mmgen/proto/btc/tx/status.py +++ b/mmgen/proto/btc/tx/status.py @@ -15,12 +15,12 @@ proto.btc.tx.status: Bitcoin transaction status class import time from ....tx import status as TxBase -from ....util import msg,suf,die +from ....util import msg, suf, die from ....util2 import format_elapsed_hr class Status(TxBase.Status): - async def display(self,usr_req=False): + async def display(self, usr_req=False): tx = self.tx @@ -33,10 +33,10 @@ class Status(TxBase.Status): 'gettransaction', txid = tx.coin_txid, include_watchonly = True, - verbose = False ) + verbose = False) except: return False - if ret.get('confirmations',0) > 0: + if ret.get('confirmations', 0) > 0: r.confs = ret['confirmations'] return True else: @@ -44,13 +44,13 @@ class Status(TxBase.Status): async def is_in_utxos(): try: - return 'txid' in await tx.rpc.call('getrawtransaction',tx.coin_txid,True) + return 'txid' in await tx.rpc.call('getrawtransaction', tx.coin_txid, True) except: return False async def is_in_mempool(): try: - await tx.rpc.call('getmempoolentry',tx.coin_txid) + await tx.rpc.call('getmempoolentry', tx.coin_txid) return True except: return False @@ -63,11 +63,11 @@ class Status(TxBase.Status): 'gettransaction', txid = tx.coin_txid, include_watchonly = True, - verbose = False ) + verbose = False) except: return False else: - if 'bip125-replaceable' in ret and ret.get('confirmations',1) <= 0: + if 'bip125-replaceable' in ret and ret.get('confirmations', 1) <= 0: r.replacing_confs = -ret['confirmations'] r.replacing_txs = ret['walletconflicts'] return True @@ -80,34 +80,34 @@ class Status(TxBase.Status): 'gettransaction', txid = tx.coin_txid, include_watchonly = True, - verbose = False ) + verbose = False) rep = ('' if d.get('bip125-replaceable') == 'yes' else 'NOT ') + 'replaceable' t = d['timereceived'] if tx.cfg.quiet: msg('Transaction is in mempool') else: msg(f'TX status: in mempool, {rep}') - msg('Sent {} ({})'.format(time.strftime('%c',time.gmtime(t)), format_elapsed_hr(t))) + msg('Sent {} ({})'.format(time.strftime('%c', time.gmtime(t)), format_elapsed_hr(t))) else: msg('Warning: transaction is in mempool!') elif await is_in_wallet(): - die(0,f'Transaction has {r.confs} confirmation{suf(r.confs)}') + die(0, f'Transaction has {r.confs} confirmation{suf(r.confs)}') elif await is_in_utxos(): - die(4,'ERROR: transaction is in the blockchain (but not in the tracking wallet)!') + die(4, 'ERROR: transaction is in the blockchain (but not in the tracking wallet)!') elif await is_replaced(): msg('Transaction has been replaced') msg('Replacement transaction ' + ( f'has {r.replacing_confs} confirmation{suf(r.replacing_confs)}' if r.replacing_confs else - 'is in mempool' ) ) + 'is in mempool')) if not tx.cfg.quiet: msg('Replacing transactions:') d = [] for txid in r.replacing_txs: try: - d.append(await tx.rpc.call('getmempoolentry',txid)) + d.append(await tx.rpc.call('getmempoolentry', txid)) except: d.append({}) - for txid,mp_entry in zip(r.replacing_txs,d): - msg(f' {txid}' + (' in mempool' if 'height' in mp_entry else '') ) - die(0,'') + for txid, mp_entry in zip(r.replacing_txs, d): + msg(f' {txid}' + (' in mempool' if 'height' in mp_entry else '')) + die(0, '') diff --git a/mmgen/proto/btc/tx/unsigned.py b/mmgen/proto/btc/tx/unsigned.py index 6b33c486..4de2508d 100755 --- a/mmgen/proto/btc/tx/unsigned.py +++ b/mmgen/proto/btc/tx/unsigned.py @@ -13,14 +13,14 @@ proto.btc.tx.unsigned: Bitcoin unsigned transaction class """ from ....tx import unsigned as TxBase -from ....obj import CoinTxID,MMGenDict -from ....util import msg,msg_r,ymsg,suf,die +from ....obj import CoinTxID, MMGenDict +from ....util import msg, msg_r, ymsg, suf, die from .completed import Completed -class Unsigned(Completed,TxBase.Unsigned): +class Unsigned(Completed, TxBase.Unsigned): desc = 'unsigned transaction' - async def sign(self,tx_num_str,keys): # return signed object or False; don't exit or raise exception + async def sign(self, tx_num_str, keys): # return signed object or False; don't exit or raise exception from ....exception import TransactionChainMismatch try: @@ -37,14 +37,14 @@ class Unsigned(Completed,TxBase.Unsigned): self.cfg._util.qmsg(f'Passing {len(keys)} key{suf(keys)} to {self.rpc.daemon.exec_fn}') if self.has_segwit_inputs(): - from ....addrgen import KeyGenerator,AddrGenerator - kg = KeyGenerator( self.cfg, self.proto, 'std' ) - ag = AddrGenerator( self.cfg, self.proto, 'segwit' ) - keydict = MMGenDict([(d.addr,d.sec) for d in keys]) + from ....addrgen import KeyGenerator, AddrGenerator + kg = KeyGenerator(self.cfg, self.proto, 'std') + ag = AddrGenerator(self.cfg, self.proto, 'segwit') + keydict = MMGenDict([(d.addr, d.sec) for d in keys]) sig_data = [] for d in self.inputs: - e = {k:getattr(d,k) for k in ('txid','vout','scriptPubKey','amt')} + e = {k:getattr(d, k) for k in ('txid', 'vout', 'scriptPubKey', 'amt')} e['amount'] = e['amt'] del e['amt'] if d.mmtype == 'S': @@ -56,8 +56,8 @@ class Unsigned(Completed,TxBase.Unsigned): try: args = ( - ('signrawtransaction', self.serialized,sig_data,wifs,self.proto.sighash_type), - ('signrawtransactionwithkey',self.serialized,wifs,sig_data,self.proto.sighash_type) + ('signrawtransaction', self.serialized, sig_data, wifs, self.proto.sighash_type), + ('signrawtransactionwithkey', self.serialized, wifs, sig_data, self.proto.sighash_type) )['sign_with_key' in self.rpc.caps] ret = await self.rpc.call(*args) except Exception as e: @@ -68,11 +68,11 @@ class Unsigned(Completed,TxBase.Unsigned): self.update_serialized(ret['hex']) from ....tx import SignedTX new = await SignedTX(cfg=self.cfg, data=self.__dict__, automount=self.automount) - tx_decoded = await self.rpc.call( 'decoderawtransaction', ret['hex'] ) + tx_decoded = await self.rpc.call('decoderawtransaction', ret['hex']) new.compare_size_and_estimated_size(tx_decoded) new.coin_txid = CoinTxID(self.deserialized.txid) if not new.coin_txid == tx_decoded['txid']: - die( 'BadMMGenTxID', 'txid mismatch (after signing)' ) + die('BadMMGenTxID', 'txid mismatch (after signing)') msg('OK') return new except Exception as e: