diff --git a/mmgen/addr.py b/mmgen/addr.py index 51a3b6db..88a8551c 100755 --- a/mmgen/addr.py +++ b/mmgen/addr.py @@ -572,7 +572,7 @@ Removed {{}} duplicate WIF key{{}} from keylist (also in {pnm} key-address file else: bc,mt = g.proto.base_coin,self.al_id.mmtype l_coin = [] if bc == 'BTC' else [g.coin] if bc == 'ETH' else [bc] - l_type = [] if mt in ('L','E') else [mt.name.upper()] + l_type = [] if mt == 'E' or (mt == 'L' and not g.proto.is_testnet()) else [mt.name.upper()] l_tn = [] if not g.proto.is_testnet() else ['TESTNET'] lbl_p2 = ':'.join(l_coin+l_type+l_tn) lbl = self.al_id.sid + ('',' ')[bool(lbl_p2)] + lbl_p2 diff --git a/mmgen/altcoins/eth/tw.py b/mmgen/altcoins/eth/tw.py index f7bacc6e..3fe77c56 100755 --- a/mmgen/altcoins/eth/tw.py +++ b/mmgen/altcoins/eth/tw.py @@ -29,7 +29,7 @@ from mmgen.addr import AddrData # No file locking - 2 processes accessing the wallet at the same time will corrupt it class EthereumTrackingWallet(TrackingWallet): - data_dir = os.path.join(g.altcoin_data_dir,'eth') + data_dir = os.path.join(g.altcoin_data_dir,'eth',g.proto.data_subdir) tw_file = os.path.join(data_dir,'tracking-wallet.json') def __init__(self): diff --git a/mmgen/main_txsign.py b/mmgen/main_txsign.py index f4a4cb7b..df0f02fe 100755 --- a/mmgen/main_txsign.py +++ b/mmgen/main_txsign.py @@ -60,13 +60,14 @@ opts_data = lambda: { -v, --verbose Produce more verbose output -V, --vsize-adj= f Adjust transaction's estimated vsize by factor 'f' -y, --yes Answer 'yes' to prompts, suppress non-essential output -""".format( +""", + 'options_fmt_args': lambda: dict( g=g,pnm=g.proj_name,pnl=g.proj_name.lower(),dn=g.proto.daemon_name, kgs=' '.join(['{}:{}'.format(n,k) for n,k in enumerate(g.key_generators,1)]), kg=g.key_generator, cu=g.coin ), - 'notes': '\n' + help_notes('txsign') + 'notes': lambda: '\n' + help_notes('txsign') } infiles = opts.init(opts_data,add_opts=['b16']) diff --git a/mmgen/protocol.py b/mmgen/protocol.py index 7dde6745..633e8e23 100755 --- a/mmgen/protocol.py +++ b/mmgen/protocol.py @@ -298,11 +298,13 @@ class EthereumProtocol(DummyWIF,BitcoinProtocolAddrgen): base_coin = 'ETH' pubkey_type = 'std' # required by DummyWIF + data_subdir = '' daemon_name = 'parity' rpc_port = 8545 mmcaps = ('key','addr','rpc') coin_amt = ETHAmt + @classmethod def verify_addr(cls,addr,hex_width,return_dict=False): from mmgen.util import is_hex_str_lc @@ -317,7 +319,8 @@ class EthereumProtocol(DummyWIF,BitcoinProtocolAddrgen): assert not p2sh,'Ethereum has no P2SH address format' return pubkey_hash -class EthereumTestnetProtocol(EthereumProtocol): pass +class EthereumTestnetProtocol(EthereumProtocol): + data_subdir = 'testnet' class EthereumClassicProtocol(EthereumProtocol): name = 'ethereum_classic' mmcaps = ('key','addr') diff --git a/mmgen/rpc.py b/mmgen/rpc.py index b3533e60..d2267022 100755 --- a/mmgen/rpc.py +++ b/mmgen/rpc.py @@ -235,6 +235,7 @@ class EthereumRPCConnection(CoinDaemonRPCConnection): 'parity_minGasPrice', 'parity_mode', 'parity_netPeers', + 'parity_nextNonce', 'parity_nodeKind', 'parity_nodeName', 'parity_pendingTransactions', diff --git a/mmgen/tx.py b/mmgen/tx.py index c793b253..12f6f366 100755 --- a/mmgen/tx.py +++ b/mmgen/tx.py @@ -479,7 +479,7 @@ class MMGenTX(MMGenObject): def fee_abs2rel(self,abs_fee): return int(abs_fee/g.proto.coin_amt.min_coin_unit/self.estimate_size()) - def get_rel_fee_from_network(self): # rel_fee is BTC/kB + def get_rel_fee_from_network(self): # rel_fee is in BTC/kB try: ret = g.rpch.estimatesmartfee(opt.tx_confs,on_fail='raise') rel_fee = ret['feerate'] if 'feerate' in ret else -2 @@ -496,24 +496,8 @@ class MMGenTX(MMGenObject): return g.proto.coin_amt(int(amt)*tx_size*getattr(g.proto.coin_amt,units[unit])) \ if tx_size else None - # given tx size and absolute fee or fee spec, return absolute fee - # relative fee is N+ - def process_fee_spec(self,tx_fee,tx_size,on_fail='throw'): - import re - units = dict((u[0],u) for u in g.proto.coin_amt.units) - pat = r'([1-9][0-9]*)({})'.format('|'.join(units.keys())) - if g.proto.coin_amt(tx_fee,on_fail='silent'): - return g.proto.coin_amt(tx_fee) - elif re.match(pat,tx_fee): - return self.convert_fee_spec(tx_size,units,*re.match(pat,tx_fee).groups()) - else: - if on_fail == 'return': - return False - elif on_fail == 'throw': - assert False, "'{}': invalid tx-fee argument".format(tx_fee) - # given network fee estimate in BTC/kB, return absolute fee using estimated tx size - def calculate_fee(self,rel_fee,fe_type=None): + def fee_est2abs(self,rel_fee,fe_type=None): tx_size = self.estimate_size() ret = g.proto.coin_amt(rel_fee) * opt.tx_fee_adj * tx_size / 1024 if opt.verbose: @@ -543,6 +527,23 @@ class MMGenTX(MMGenObject): return abs_fee # non-coin-specific fee routines + + # given tx size and absolute fee or fee spec, return absolute fee + # relative fee is N+ + def process_fee_spec(self,tx_fee,tx_size,on_fail='throw'): + import re + units = dict((u[0],u) for u in g.proto.coin_amt.units) + pat = r'([1-9][0-9]*)({})'.format('|'.join(units.keys())) + if g.proto.coin_amt(tx_fee,on_fail='silent'): + return g.proto.coin_amt(tx_fee) + elif re.match(pat,tx_fee): + return self.convert_fee_spec(tx_size,units,*re.match(pat,tx_fee).groups()) + else: + if on_fail == 'return': + return False + elif on_fail == 'throw': + assert False, "'{}': invalid tx-fee argument".format(tx_fee) + def get_usr_fee_interactive(self,tx_fee=None,desc='Starting'): abs_fee = None while True: @@ -578,7 +579,7 @@ class MMGenTX(MMGenObject): have_estimate_fail.append(True) start_fee = None else: - start_fee = self.calculate_fee(rel_fee,fe_type) + start_fee = self.fee_est2abs(rel_fee,fe_type) return self.get_usr_fee_interactive(start_fee,desc=desc) @@ -852,6 +853,13 @@ class MMGenTX(MMGenObject): msg(' {}{}'.format(t,('',' in mempool')[s])) die(0,'') + def confirm_send(self): + m1 = ("Once this transaction is sent, there's no taking it back!",'')[bool(opt.quiet)] + m2 = 'broadcast this transaction to the {} network'.format(g.chain.upper()) + m3 = ('YES, I REALLY WANT TO DO THIS','YES')[bool(opt.quiet or opt.yes)] + confirm_or_exit(m1,m2,m3) + msg('Sending transaction') + def send(self,prompt_user=True,exit_on_fail=False): if not self.marked_signed(): @@ -869,17 +877,12 @@ class MMGenTX(MMGenObject): if self.get_fee_from_tx() > g.proto.max_tx_fee: die(2,'Transaction fee ({}) greater than {} max_tx_fee ({} {})!'.format( - self.get_fee_from_tx(),g.proto.name.capitalize(),g.proto.max_tx_fee,g.coin.upper())) + self.get_fee_from_tx(),g.proto.name.capitalize(),g.proto.max_tx_fee,g.coin)) self.get_status() - if prompt_user: - m1 = ("Once this transaction is sent, there's no taking it back!",'')[bool(opt.quiet)] - m2 = 'broadcast this transaction to the {} network'.format(g.chain.upper()) - m3 = ('YES, I REALLY WANT TO DO THIS','YES')[bool(opt.quiet or opt.yes)] - confirm_or_exit(m1,m2,m3) + if prompt_user: self.confirm_send() - msg('Sending transaction') ret = None if bogus_send else g.rpch.sendrawtransaction(self.hex,on_fail='return') from mmgen.rpc import rpc_error,rpc_errmsg @@ -1162,6 +1165,7 @@ class MMGenTX(MMGenObject): except Exception as e: die(2,'Invalid {} in transaction file: {}'.format(desc,e[0])) + # test doesn't work for Ethereum if not self.chain and not self.inputs[0].addr.is_for_chain('testnet'): self.chain = 'mainnet' diff --git a/test/test.py b/test/test.py index 02954485..8d45a3df 100755 --- a/test/test.py +++ b/test/test.py @@ -2525,8 +2525,9 @@ class MMGenTestSuite(object): for mmtype in g.proto.mmtypes: desc = MMGenAddrType.mmtypes[mmtype]['name'] fn = os.path.join(self.regtest_user_dir(user), - u'{}{}{}[{}]{x}.addrs'.format(sid,altcoin_pfx,id_strs[desc],addr_range, - x=u'-α' if g.debug_utf8 else '')) + u'{}{}{}[{}]{x}.testnet.addrs'.format( + sid,altcoin_pfx,id_strs[desc],addr_range, + x=u'-α' if g.debug_utf8 else '')) if mmtype == g.proto.mmtypes[0] and user == 'bob': psave = g.proto g.proto = CoinProtocol(g.coin,True) @@ -2634,7 +2635,8 @@ class MMGenTestSuite(object): def get_addr_from_regtest_addrlist(self,user,sid,mmtype,idx,addr_range='1-5'): id_str = { 'L':'', 'S':'-S', 'C':'-C' }[mmtype] - ext = u'{}{}{}[{}]{x}.addrs'.format(sid,altcoin_pfx,id_str,addr_range,x=u'-α' if g.debug_utf8 else '') + ext = u'{}{}{}[{}]{x}.testnet.addrs'.format( + sid,altcoin_pfx,id_str,addr_range,x=u'-α' if g.debug_utf8 else '') fn = get_file_with_ext(ext,self.regtest_user_dir(user),no_dot=True) silence() psave = g.proto