tx.py: rename vars,funcs; refactor,move funcs. obj.py: ETHAmt bugfix

This commit is contained in:
The MMGen Project 2018-05-28 19:29:30 +00:00
commit 11e02f3ab9
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
3 changed files with 81 additions and 62 deletions

View file

@ -114,7 +114,7 @@ fee = tx.get_usr_fee_interactive(tx_fee=opt.tx_fee,desc='User-selected')
tx.update_output_amt(op_idx,tx.sum_inputs()-tx.sum_outputs(exclude=op_idx)-fee)
d = tx.get_fee()
d = tx.get_fee_from_tx()
assert d == fee and d <= g.proto.max_tx_fee
if not opt.yes:

View file

@ -392,8 +392,8 @@ class ETHAmt(BTCAmt):
if from_unit:
assert from_unit in ('wei','szabo'),"'{}': unrecognized ETH denomination".format(from_unit)
assert type(num) in (int,long),'value is not an integer or long integer'
return super(cls,cls).__new__(cls,num * getattr(cls,from_unit))
return super(cls,cls).__new__(cls,num)
return super(cls,cls).__new__(cls,num * getattr(cls,from_unit),on_fail=on_fail)
return super(cls,cls).__new__(cls,num,on_fail=on_fail)
except Exception as e:
m = "{!r}: value cannot be converted to {} ({})"
return cls.init_fail(m.format(num,cls.__name__,e[0]),on_fail)

View file

@ -225,7 +225,9 @@ class MMGenTX(MMGenObject):
txid_ext = 'txid'
desc = 'transaction'
chg_fs = 'Transaction produces {} {} in change'
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'
rel_fee_desc = 'satoshis per byte'
class MMGenTxInput(MMGenListItem):
for k in txio_attrs: locals()[k] = txio_attrs[k] # in lieu of inheritance
@ -371,6 +373,9 @@ class MMGenTX(MMGenObject):
def edit_comment(self):
return self.add_comment(self)
def get_fee_from_tx(self):
return self.sum_inputs() - self.sum_outputs()
def has_segwit_inputs(self):
return any(i.mmid and i.mmid.mmtype in ('S','B') for i in self.inputs)
@ -459,18 +464,28 @@ class MMGenTX(MMGenObject):
return int(ret * float(opt.vsize_adj)) if hasattr(opt,'vsize_adj') and opt.vsize_adj else ret
def get_fee(self):
return self.sum_inputs() - self.sum_outputs()
def btc2spb(self,coin_fee):
return int(coin_fee/g.proto.coin_amt.min_coin_unit/self.estimate_size())
# coin-specific fee routines
def get_relay_fee(self):
kb_fee = g.proto.coin_amt(g.rpch.getnetworkinfo()['relayfee'])
ret = kb_fee * self.estimate_size() / 1024
vmsg('Relay fee: {} {c}/kB, for transaction: {} {c}'.format(kb_fee,ret,c=g.coin))
return ret
# convert absolute BTC fee to satoshis-per-byte
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
try:
ret = g.rpch.estimatesmartfee(opt.tx_confs,on_fail='raise')
rel_fee = ret['feerate'] if 'feerate' in ret else -2
fe_type = 'estimatesmartfee'
except:
rel_fee = g.rpch.estimatefee(opt.tx_confs)
fe_type = 'estimatefee'
return rel_fee,fe_type
def convert_fee_spec(self,tx_fee,tx_size,on_fail='throw'):
if g.proto.coin_amt(tx_fee,on_fail='silent'):
return g.proto.coin_amt(tx_fee)
@ -485,42 +500,76 @@ class MMGenTX(MMGenObject):
elif on_fail == 'throw':
assert False, "'{}': invalid tx-fee argument".format(tx_fee)
def get_usr_fee(self,tx_fee,desc='Missing description'):
coin_fee = self.convert_fee_spec(tx_fee,self.estimate_size(),on_fail='return')
if coin_fee == None:
# given network fee estimate in BTC/kB and tx size, calculate absolute fee in coin units
def calculate_fee(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:
msg('{} fee for {} confirmations: {} {}/kB'.format(fe_type.upper(),opt.tx_confs,rel_fee,g.coin))
msg('TX size (estimated): {}'.format(tx_size))
return ret
def convert_and_check_fee(self,tx_fee,desc='Missing description'):
abs_fee = self.convert_fee_spec(tx_fee,self.estimate_size(),on_fail='return')
if abs_fee == None:
# we shouldn't be calling this if tx size is unknown
m = "'{}': cannot convert satoshis-per-byte to {} because transaction size is unknown"
assert False, m.format(tx_fee,g.coin)
elif coin_fee == False:
elif abs_fee == False:
m = "'{}': invalid TX fee (not a {} amount or satoshis-per-byte specification)"
msg(m.format(tx_fee,g.coin))
return False
elif coin_fee > g.proto.max_tx_fee:
elif abs_fee > g.proto.max_tx_fee:
m = '{} {c}: {} fee too large (maximum fee: {} {c})'
msg(m.format(coin_fee,desc,g.proto.max_tx_fee,c=g.coin))
msg(m.format(abs_fee,desc,g.proto.max_tx_fee,c=g.coin))
return False
elif coin_fee < self.get_relay_fee():
elif abs_fee < self.get_relay_fee():
m = '{} {c}: {} fee too small (below relay fee of {} {c})'
msg(m.format(str(coin_fee),desc,str(self.get_relay_fee()),c=g.coin))
msg(m.format(str(abs_fee),desc,str(self.get_relay_fee()),c=g.coin))
return False
else:
return coin_fee
return abs_fee
# non-coin-specific fee routines
def get_usr_fee_interactive(self,tx_fee=None,desc='Starting'):
coin_fee = None
abs_fee = None
while True:
if tx_fee:
coin_fee = self.get_usr_fee(tx_fee,desc)
if coin_fee:
abs_fee = self.convert_and_check_fee(tx_fee,desc)
if abs_fee:
m = ('',' (after {}x adjustment)'.format(opt.tx_fee_adj))[opt.tx_fee_adj != 1]
p = '{} TX fee{}: {} {} ({} satoshis per byte)'.format(desc,m,
coin_fee.hl(),g.coin,pink(str(self.btc2spb(coin_fee))))
if opt.yes or keypress_confirm(p+'. OK?',default_yes=True):
p = u'{} TX fee{}: {} {} ({} {})\n'.format(
desc,
m,
abs_fee.hl(),
g.coin,
pink(str(self.fee_abs2rel(abs_fee))),
self.rel_fee_desc)
if opt.yes or keypress_confirm(p+'OK?',default_yes=True):
if opt.yes: msg(p)
return coin_fee
return abs_fee
tx_fee = my_raw_input('Enter transaction fee: ')
desc = 'User-selected'
def get_fee_from_user(self,have_estimate_fail=[]):
if opt.tx_fee:
desc = 'User-selected'
start_fee = opt.tx_fee
else:
desc = 'Network-estimated'
rel_fee,fe_type = self.get_rel_fee_from_network()
if rel_fee < 0:
if not have_estimate_fail:
msg(self.fee_fail_fs.format(c=opt.tx_confs,t=fe_type))
have_estimate_fail.append(True)
start_fee = None
else:
start_fee = self.calculate_fee(rel_fee,fe_type)
return self.get_usr_fee_interactive(start_fee,desc=desc)
def delete_attrs(self,desc,attr):
for e in getattr(self,desc):
if hasattr(e,attr): delattr(e,attr)
@ -806,9 +855,9 @@ class MMGenTX(MMGenObject):
m = 'Transaction has MMGen Segwit outputs, but this blockchain does not support Segwit'
die(2,m+' at the current height')
if self.get_fee() > g.proto.max_tx_fee:
if self.get_fee_from_tx() > g.proto.max_tx_fee:
die(2,'Transaction fee ({}) greater than {} max_tx_fee ({} {})!'.format(
self.get_fee(),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.upper()))
self.get_status()
@ -863,7 +912,7 @@ class MMGenTX(MMGenObject):
self.txid,
('-'+g.coin,'')[g.coin=='BTC'],
self.send_amt,
('',',{}'.format(self.btc2spb(self.get_fee())))[self.is_rbf()],
('',',{}'.format(self.fee_abs2rel(self.get_fee_from_tx())))[self.is_rbf()],
('',',tl={}'.format(tl))[bool(tl)],
self.ext,
x=u'' if g.debug_utf8 else '')
@ -988,7 +1037,7 @@ class MMGenTX(MMGenObject):
t_in,t_out = self.sum_inputs(),self.sum_outputs()
fee = t_in-t_out
out += fs.format(t_in.hl(),t_out.hl(),fee.hl(),pink(str(self.btc2spb(fee))),c=g.coin)
out += fs.format(t_in.hl(),t_out.hl(),fee.hl(),pink(str(self.fee_abs2rel(fee))),c=g.coin)
if opt.verbose:
ts = len(self.hex)/2 if self.hex else 'unknown'
@ -1088,36 +1137,6 @@ class MMGenTX(MMGenObject):
if not self.chain and not self.inputs[0].addr.is_for_chain('testnet'):
self.chain = 'mainnet'
def get_fee_from_user(self,have_estimate_fail=[]):
if opt.tx_fee:
desc = 'User-selected'
start_fee = opt.tx_fee
else:
desc = 'Network-estimated'
try:
ret = g.rpch.estimatesmartfee(opt.tx_confs,on_fail='raise')
except:
fe_type = 'estimatefee'
fee_per_kb = g.rpch.estimatefee(opt.tx_confs)
else:
fe_type = 'estimatesmartfee'
fee_per_kb = ret['feerate'] if 'feerate' in ret else -2
if fee_per_kb < 0:
if not have_estimate_fail:
msg('Network fee estimation for {} confirmations failed ({})'.format(opt.tx_confs,fe_type))
have_estimate_fail.append(True)
start_fee = None
else:
start_fee = g.proto.coin_amt(fee_per_kb) * opt.tx_fee_adj * self.estimate_size() / 1024
if opt.verbose:
msg('{} fee for {} confirmations: {} {}/kB'.format(
fe_type.upper(),opt.tx_confs,fee_per_kb,g.coin))
msg('TX size (estimated): {}'.format(self.estimate_size()))
return self.get_usr_fee_interactive(start_fee,desc=desc)
def process_cmd_args(self,cmd_args,ad_f,ad_w):
for a in cmd_args:
if ',' in a:
@ -1323,11 +1342,11 @@ class MMGenBumpTX(MMGenTX):
def set_min_fee(self):
self.min_fee = self.sum_inputs() - self.sum_outputs() + self.get_relay_fee()
def get_usr_fee(self,tx_fee,desc):
ret = super(type(self),self).get_usr_fee(tx_fee,desc)
def convert_and_check_fee(self,tx_fee,desc):
ret = super(type(self),self).convert_and_check_fee(tx_fee,desc)
if ret < self.min_fee:
msg('{} {c}: {} fee too small. Minimum fee: {} {c} ({} satoshis per byte)'.format(
ret,desc,self.min_fee,self.btc2spb(self.min_fee),c=g.coin))
ret,desc,self.min_fee,self.fee_abs2rel(self.min_fee),c=g.coin))
return False
output_amt = self.outputs[self.bump_output_idx].amt
if ret >= output_amt: