tx.new: support relative fees < 1 unit; add test

This commit is contained in:
The MMGen Project 2025-03-15 18:24:53 +00:00
commit 1cab2f9d6d
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
5 changed files with 41 additions and 14 deletions

View file

@ -73,8 +73,8 @@ class New(Base, TxNew):
return fee_per_kb, fe_type
# given tx size, rel fee and units, return absolute fee
def fee_rel2abs(self, tx_size, units, amt_in_units, unit):
return self.proto.coin_amt(amt_in_units * tx_size, from_unit=units[unit])
def fee_rel2abs(self, tx_size, amt_in_units, unit):
return self.proto.coin_amt(int(amt_in_units * tx_size), from_unit=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):

View file

@ -34,9 +34,8 @@ class Base(TxBase.Base):
return self.outputs
def pretty_fmt_fee(self, fee):
if fee < 1:
ret = f'{fee:.8f}'.rstrip('0')
return ret + '0' if ret.endswith('.') else ret
if fee < 10:
return f'{fee:.3f}'.rstrip('0').rstrip('.')
return str(int(fee))
# given absolute fee in ETH, return gas price for display in selected unit

View file

@ -126,8 +126,8 @@ class New(Base, TxBase.New):
assert self.usr_fee <= self.proto.max_tx_fee
# given rel fee and units, return absolute fee using self.gas
def fee_rel2abs(self, tx_size, units, amt_in_units, unit):
return self.proto.coin_amt(amt_in_units, from_unit=units[unit]) * self.gas.toWei()
def fee_rel2abs(self, tx_size, amt_in_units, unit):
return self.proto.coin_amt(int(amt_in_units * self.gas.toWei()), from_unit=unit)
# given fee estimate (gas price) in wei, return absolute fee, adjusting by self.cfg.fee_adjust
def fee_est2abs(self, rel_fee, *, fe_type=None):

View file

@ -70,6 +70,13 @@ def mmaddr2coinaddr(cfg, mmaddr, ad_w, ad_f, proto):
return CoinAddr(proto, coin_addr)
def parse_fee_spec(proto, fee_arg):
import re
units = {u[0]:u for u in proto.coin_amt.units}
pat = re.compile(r'((?:[1-9][0-9]*)|(?:[0-9]+\.[0-9]+))({})'.format('|'.join(units)))
if m := pat.match(fee_arg):
return namedtuple('parsed_fee_spec', ['amt', 'unit'])(m[1], units[m[2]])
class New(Base):
fee_is_approximate = False
@ -117,12 +124,8 @@ class New(Base):
if fee := get_obj(self.proto.coin_amt, num=fee_arg, silent=True):
return fee
import re
units = {u[0]:u for u in self.proto.coin_amt.units}
pat = re.compile(r'([1-9][0-9]*)({})'.format('|'.join(units)))
if pat.match(fee_arg):
amt, unit = pat.match(fee_arg).groups()
return self.fee_rel2abs(tx_size, units, int(amt), unit)
if res := parse_fee_spec(self.proto, fee_arg):
return self.fee_rel2abs(tx_size, float(res.amt), res.unit)
return False

View file

@ -7,6 +7,7 @@ test.modtest_d.ut_amt: CoinAmt unit tests for the MMGen suite
from decimal import Decimal
from mmgen.protocol import init_proto
from mmgen.tx.new import parse_fee_spec
from mmgen.cfg import Config
from ..include.common import cfg, vmsg
@ -26,9 +27,18 @@ def test_to_unit(data):
assert res == int(chk), f'{res} != {int(chk)}'
return True
def test_fee_spec(data):
protos = get_protos(data)
for proto, spec, amt, unit in data:
vmsg(f' {proto.upper():6} {spec:<5} => {amt:<4} {unit}')
res = parse_fee_spec(protos[proto], spec)
assert res.amt == amt, f' {res.amt} != {amt}'
assert res.unit == unit, f' {res.unit} != {unit}'
return True
class unit_tests:
altcoin_deps = ('to_unit_alt',)
altcoin_deps = ('fee_spec_alt', 'to_unit_alt')
def to_unit(self, name, ut, desc='CoinAmt.to_unit() (BTC)'):
return test_to_unit((
@ -54,3 +64,18 @@ class unit_tests:
('xmr', '1', 'atomic', '1000000000000'),
('xmr', '0.000000000001', 'atomic', '1'),
('xmr', '1.234567890123', 'atomic', '1234567890123')))
def fee_spec(self, name, ut, desc='fee spec parsing (BTC)'):
return test_fee_spec((
('btc', '32s', '32', 'satoshi'),
('btc', '1s', '1', 'satoshi')))
def fee_spec_alt(self, name, ut, desc='fee spec parsing (LTC, BCH, ETH, XMR)'):
return test_fee_spec((
('ltc', '3.07s', '3.07', 'satoshi'),
('bch', '3.07s', '3.07', 'satoshi'),
('eth', '3.07G', '3.07', 'Gwei'),
('eth', '37M', '37', 'Mwei'),
('eth', '3701w', '3701', 'wei'),
('eth', '3.07M', '3.07', 'Mwei'),
('xmr', '3.07a', '3.07', 'atomic')))