move CoinAmt and related classes to amt.py
This commit is contained in:
parent
4bb1b1533a
commit
d669229da0
12 changed files with 199 additions and 162 deletions
|
|
@ -25,8 +25,9 @@ from . import rlp
|
|||
|
||||
from mmgen.globalvars import g
|
||||
from mmgen.common import *
|
||||
from mmgen.obj import MMGenObject,CoinAddr,TokenAddr,CoinTxID,ETHAmt,AsyncInit
|
||||
from mmgen.obj import MMGenObject,CoinAddr,TokenAddr,CoinTxID,AsyncInit
|
||||
from mmgen.util import msg
|
||||
from .obj import ETHAmt
|
||||
|
||||
try:
|
||||
assert not g.use_internal_keccak_module
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@ altcoins.eth.obj: Ethereum data type classes for the MMGen suite
|
|||
"""
|
||||
|
||||
from decimal import Decimal
|
||||
from mmgen.obj import CoinAmt,Int
|
||||
from mmgen.obj import Int
|
||||
from mmgen.amt import CoinAmt
|
||||
|
||||
# Kwei (babbage) 3, Mwei (lovelace) 6, Gwei (shannon) 9, µETH (szabo) 12, mETH (finney) 15, ETH 18
|
||||
class ETHAmt(CoinAmt):
|
||||
|
|
|
|||
|
|
@ -21,10 +21,11 @@ altcoins.eth.tw: Ethereum tracking wallet and related classes for the MMGen suit
|
|||
"""
|
||||
|
||||
from mmgen.common import *
|
||||
from mmgen.obj import ETHAmt,TwLabel,is_coin_addr,is_mmgen_id,ListItemAttr,ImmutableAttr
|
||||
from mmgen.obj import TwLabel,is_coin_addr,is_mmgen_id,ListItemAttr,ImmutableAttr
|
||||
from mmgen.tw import TrackingWallet,TwAddrList,TwUnspentOutputs,TwGetBalance
|
||||
from mmgen.addr import AddrData,TwAddrData
|
||||
from .contract import Token,TokenResolve
|
||||
from .obj import ETHAmt
|
||||
|
||||
class EthereumTrackingWallet(TrackingWallet):
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ from mmgen.obj import *
|
|||
from mmgen.tx import MMGenTX
|
||||
from mmgen.tw import TrackingWallet
|
||||
from .contract import Token
|
||||
from .obj import ETHAmt,ETHNonce
|
||||
|
||||
class EthereumMMGenTX:
|
||||
|
||||
|
|
|
|||
181
mmgen/amt.py
Executable file
181
mmgen/amt.py
Executable file
|
|
@ -0,0 +1,181 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C)2013-2022 The MMGen Project <mmgen@tuta.io>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
amt.py: MMGen CoinAmt and related classes
|
||||
"""
|
||||
|
||||
from decimal import Decimal
|
||||
from .obj import Hilite,InitErrors
|
||||
|
||||
class UnknownCoinAmt(Decimal): pass
|
||||
|
||||
class DecimalNegateResult(Decimal): pass
|
||||
|
||||
class CoinAmt(Decimal,Hilite,InitErrors): # abstract class
|
||||
"""
|
||||
Instantiating with 'from_decimal' rounds value down to 'max_prec' precision.
|
||||
For addition and subtraction, operand types must match.
|
||||
For multiplication and division, operand types may differ.
|
||||
Negative amounts, floor division and modulus operation are unimplemented.
|
||||
"""
|
||||
color = 'yellow'
|
||||
forbidden_types = (float,int)
|
||||
|
||||
max_prec = 0 # number of decimal places for this coin
|
||||
max_amt = None # coin supply if known, otherwise None
|
||||
units = () # defined unit names, e.g. ('satoshi',...)
|
||||
amt_fs = '0.0' # format string for the fmt() method
|
||||
|
||||
def __new__(cls,num,from_unit=None,from_decimal=False):
|
||||
if type(num) == cls:
|
||||
return num
|
||||
try:
|
||||
if from_unit:
|
||||
assert from_unit in cls.units, f'{from_unit!r}: unrecognized denomination for {cls.__name__}'
|
||||
assert type(num) == int,'value is not an integer'
|
||||
me = Decimal.__new__(cls,num * getattr(cls,from_unit))
|
||||
elif from_decimal:
|
||||
assert type(num) == Decimal, f'number must be of type Decimal, not {type(num).__name__})'
|
||||
me = Decimal.__new__(cls,num.quantize(Decimal('10') ** -cls.max_prec))
|
||||
else:
|
||||
for t in cls.forbidden_types:
|
||||
assert type(num) is not t, f'number is of forbidden type {t.__name__}'
|
||||
me = Decimal.__new__(cls,str(num))
|
||||
assert me.normalize().as_tuple()[-1] >= -cls.max_prec,'too many decimal places in coin amount'
|
||||
if cls.max_amt:
|
||||
assert me <= cls.max_amt, f'{me}: coin amount too large (>{cls.max_amt})'
|
||||
assert me >= 0,'coin amount cannot be negative'
|
||||
return me
|
||||
except Exception as e:
|
||||
return cls.init_fail(e,num)
|
||||
|
||||
def to_unit(self,unit,show_decimal=False):
|
||||
ret = Decimal(self) // getattr(self,unit)
|
||||
if show_decimal and ret < 1:
|
||||
return f'{ret:.8f}'.rstrip('0')
|
||||
return int(ret)
|
||||
|
||||
@classmethod
|
||||
def fmtc(cls):
|
||||
cls.method_not_implemented()
|
||||
|
||||
def fmt(self,fs=None,color=False,suf='',prec=1000):
|
||||
if fs == None:
|
||||
fs = self.amt_fs
|
||||
s = self.__str__()
|
||||
if '.' in fs:
|
||||
p1,p2 = list(map(int,fs.split('.',1)))
|
||||
ss = s.split('.',1)
|
||||
if len(ss) == 2:
|
||||
a,b = ss
|
||||
ret = a.rjust(p1) + '.' + ((b+suf).ljust(p2+len(suf)))[:prec]
|
||||
else:
|
||||
ret = s.rjust(p1) + suf + (' ' * (p2+1))[:prec+1-len(suf)]
|
||||
else:
|
||||
ret = s.ljust(int(fs))
|
||||
return self.colorize(ret,color=color)
|
||||
|
||||
def hl(self,color=True):
|
||||
return self.colorize(self.__str__(),color=color)
|
||||
|
||||
def hl2(self,color=True,encl=''): # display with coin symbol
|
||||
return (
|
||||
encl[:-1]
|
||||
+ self.colorize(self.__str__(),color=color)
|
||||
+ ' ' + type(self).__name__[:-3]
|
||||
+ encl[1:]
|
||||
)
|
||||
|
||||
def __str__(self): # format simply, with no exponential notation
|
||||
return str(int(self)) if int(self) == self else self.normalize().__format__('f')
|
||||
|
||||
def __repr__(self):
|
||||
return "{}('{}')".format(type(self).__name__,self.__str__())
|
||||
|
||||
def __add__(self,other,*args,**kwargs):
|
||||
"""
|
||||
we must allow other to be int(0) to use the sum() builtin
|
||||
"""
|
||||
if type(other) not in ( type(self), DecimalNegateResult ) and other != 0:
|
||||
raise ValueError(
|
||||
f'operand {other} of incorrect type ({type(other).__name__} != {type(self).__name__})')
|
||||
return type(self)(Decimal.__add__(self,other,*args,**kwargs))
|
||||
|
||||
__radd__ = __add__
|
||||
|
||||
def __sub__(self,other,*args,**kwargs):
|
||||
if type(other) is not type(self):
|
||||
raise ValueError(
|
||||
f'operand {other} of incorrect type ({type(other).__name__} != {type(self).__name__})')
|
||||
return type(self)(Decimal.__sub__(self,other,*args,**kwargs))
|
||||
|
||||
def copy_negate(self,*args,**kwargs):
|
||||
"""
|
||||
We implement this so that __add__() can check type, because:
|
||||
class Decimal:
|
||||
def __sub__(self, other, ...):
|
||||
...
|
||||
return self.__add__(other.copy_negate(), ...)
|
||||
"""
|
||||
return DecimalNegateResult(Decimal.copy_negate(self,*args,**kwargs))
|
||||
|
||||
def __mul__(self,other,*args,**kwargs):
|
||||
return type(self)('{:0.{p}f}'.format(
|
||||
Decimal.__mul__(self,Decimal(other),*args,**kwargs),
|
||||
p = self.max_prec
|
||||
))
|
||||
|
||||
__rmul__ = __mul__
|
||||
|
||||
def __truediv__(self,other,*args,**kwargs):
|
||||
return type(self)('{:0.{p}f}'.format(
|
||||
Decimal.__truediv__(self,Decimal(other),*args,**kwargs),
|
||||
p = self.max_prec
|
||||
))
|
||||
|
||||
def __neg__(self,*args,**kwargs):
|
||||
self.method_not_implemented()
|
||||
|
||||
def __floordiv__(self,*args,**kwargs):
|
||||
self.method_not_implemented()
|
||||
|
||||
def __mod__(self,*args,**kwargs):
|
||||
self.method_not_implemented()
|
||||
|
||||
class BTCAmt(CoinAmt):
|
||||
max_prec = 8
|
||||
max_amt = 21000000
|
||||
satoshi = Decimal('0.00000001')
|
||||
units = ('satoshi',)
|
||||
amt_fs = '4.8'
|
||||
|
||||
class BCHAmt(BTCAmt):
|
||||
pass
|
||||
|
||||
class B2XAmt(BTCAmt):
|
||||
pass
|
||||
|
||||
class LTCAmt(BTCAmt):
|
||||
max_amt = 84000000
|
||||
|
||||
class XMRAmt(CoinAmt):
|
||||
max_prec = 12
|
||||
atomic = Decimal('0.000000000001')
|
||||
units = ('atomic',)
|
||||
amt_fs = '4.12'
|
||||
154
mmgen/obj.py
154
mmgen/obj.py
|
|
@ -440,160 +440,6 @@ class MMGenRange(tuple,InitErrors,MMGenObject):
|
|||
def items(self):
|
||||
return list(self.iterate())
|
||||
|
||||
class UnknownCoinAmt(Decimal): pass
|
||||
|
||||
class DecimalNegateResult(Decimal): pass
|
||||
|
||||
class CoinAmt(Decimal,Hilite,InitErrors): # abstract class
|
||||
"""
|
||||
Instantiating with 'from_decimal' rounds value down to 'max_prec' precision.
|
||||
For addition and subtraction, operand types must match.
|
||||
For multiplication and division, operand types may differ.
|
||||
Negative amounts, floor division and modulus operation are unimplemented.
|
||||
"""
|
||||
color = 'yellow'
|
||||
forbidden_types = (float,int)
|
||||
|
||||
max_prec = 0 # number of decimal places for this coin
|
||||
max_amt = None # coin supply if known, otherwise None
|
||||
units = () # defined unit names, e.g. ('satoshi',...)
|
||||
amt_fs = '0.0' # format string for the fmt() method
|
||||
|
||||
def __new__(cls,num,from_unit=None,from_decimal=False):
|
||||
if type(num) == cls:
|
||||
return num
|
||||
try:
|
||||
if from_unit:
|
||||
assert from_unit in cls.units, f'{from_unit!r}: unrecognized denomination for {cls.__name__}'
|
||||
assert type(num) == int,'value is not an integer'
|
||||
me = Decimal.__new__(cls,num * getattr(cls,from_unit))
|
||||
elif from_decimal:
|
||||
assert type(num) == Decimal, f'number must be of type Decimal, not {type(num).__name__})'
|
||||
me = Decimal.__new__(cls,num.quantize(Decimal('10') ** -cls.max_prec))
|
||||
else:
|
||||
for t in cls.forbidden_types:
|
||||
assert type(num) is not t, f'number is of forbidden type {t.__name__}'
|
||||
me = Decimal.__new__(cls,str(num))
|
||||
assert me.normalize().as_tuple()[-1] >= -cls.max_prec,'too many decimal places in coin amount'
|
||||
if cls.max_amt:
|
||||
assert me <= cls.max_amt, f'{me}: coin amount too large (>{cls.max_amt})'
|
||||
assert me >= 0,'coin amount cannot be negative'
|
||||
return me
|
||||
except Exception as e:
|
||||
return cls.init_fail(e,num)
|
||||
|
||||
def to_unit(self,unit,show_decimal=False):
|
||||
ret = Decimal(self) // getattr(self,unit)
|
||||
if show_decimal and ret < 1:
|
||||
return f'{ret:.8f}'.rstrip('0')
|
||||
return int(ret)
|
||||
|
||||
@classmethod
|
||||
def fmtc(cls):
|
||||
cls.method_not_implemented()
|
||||
|
||||
def fmt(self,fs=None,color=False,suf='',prec=1000):
|
||||
if fs == None:
|
||||
fs = self.amt_fs
|
||||
s = self.__str__()
|
||||
if '.' in fs:
|
||||
p1,p2 = list(map(int,fs.split('.',1)))
|
||||
ss = s.split('.',1)
|
||||
if len(ss) == 2:
|
||||
a,b = ss
|
||||
ret = a.rjust(p1) + '.' + ((b+suf).ljust(p2+len(suf)))[:prec]
|
||||
else:
|
||||
ret = s.rjust(p1) + suf + (' ' * (p2+1))[:prec+1-len(suf)]
|
||||
else:
|
||||
ret = s.ljust(int(fs))
|
||||
return self.colorize(ret,color=color)
|
||||
|
||||
def hl(self,color=True):
|
||||
return self.colorize(self.__str__(),color=color)
|
||||
|
||||
def hl2(self,color=True,encl=''): # display with coin symbol
|
||||
return (
|
||||
encl[:-1]
|
||||
+ self.colorize(self.__str__(),color=color)
|
||||
+ ' ' + type(self).__name__[:-3]
|
||||
+ encl[1:]
|
||||
)
|
||||
|
||||
def __str__(self): # format simply, with no exponential notation
|
||||
return str(int(self)) if int(self) == self else self.normalize().__format__('f')
|
||||
|
||||
def __repr__(self):
|
||||
return "{}('{}')".format(type(self).__name__,self.__str__())
|
||||
|
||||
def __add__(self,other,*args,**kwargs):
|
||||
"""
|
||||
we must allow other to be int(0) to use the sum() builtin
|
||||
"""
|
||||
if type(other) not in ( type(self), DecimalNegateResult ) and other != 0:
|
||||
raise ValueError(
|
||||
f'operand {other} of incorrect type ({type(other).__name__} != {type(self).__name__})')
|
||||
return type(self)(Decimal.__add__(self,other,*args,**kwargs))
|
||||
|
||||
__radd__ = __add__
|
||||
|
||||
def __sub__(self,other,*args,**kwargs):
|
||||
if type(other) is not type(self):
|
||||
raise ValueError(
|
||||
f'operand {other} of incorrect type ({type(other).__name__} != {type(self).__name__})')
|
||||
return type(self)(Decimal.__sub__(self,other,*args,**kwargs))
|
||||
|
||||
def copy_negate(self,*args,**kwargs):
|
||||
"""
|
||||
We implement this so that __add__() can check type, because:
|
||||
class Decimal:
|
||||
def __sub__(self, other, ...):
|
||||
...
|
||||
return self.__add__(other.copy_negate(), ...)
|
||||
"""
|
||||
return DecimalNegateResult(Decimal.copy_negate(self,*args,**kwargs))
|
||||
|
||||
def __mul__(self,other,*args,**kwargs):
|
||||
return type(self)('{:0.{p}f}'.format(
|
||||
Decimal.__mul__(self,Decimal(other),*args,**kwargs),
|
||||
p = self.max_prec
|
||||
))
|
||||
|
||||
__rmul__ = __mul__
|
||||
|
||||
def __truediv__(self,other,*args,**kwargs):
|
||||
return type(self)('{:0.{p}f}'.format(
|
||||
Decimal.__truediv__(self,Decimal(other),*args,**kwargs),
|
||||
p = self.max_prec
|
||||
))
|
||||
|
||||
def __neg__(self,*args,**kwargs):
|
||||
self.method_not_implemented()
|
||||
|
||||
def __floordiv__(self,*args,**kwargs):
|
||||
self.method_not_implemented()
|
||||
|
||||
def __mod__(self,*args,**kwargs):
|
||||
self.method_not_implemented()
|
||||
|
||||
class BTCAmt(CoinAmt):
|
||||
max_prec = 8
|
||||
max_amt = 21000000
|
||||
satoshi = Decimal('0.00000001')
|
||||
units = ('satoshi',)
|
||||
amt_fs = '4.8'
|
||||
|
||||
class BCHAmt(BTCAmt): pass
|
||||
class B2XAmt(BTCAmt): pass
|
||||
class LTCAmt(BTCAmt): max_amt = 84000000
|
||||
|
||||
class XMRAmt(CoinAmt):
|
||||
max_prec = 12
|
||||
atomic = Decimal('0.000000000001')
|
||||
units = ('atomic',)
|
||||
amt_fs = '4.12'
|
||||
|
||||
from .altcoins.eth.obj import ETHAmt,ETHNonce
|
||||
|
||||
class CoinAddr(str,Hilite,InitErrors,MMGenObject):
|
||||
color = 'cyan'
|
||||
hex_width = 40
|
||||
|
|
|
|||
|
|
@ -25,8 +25,10 @@ from collections import namedtuple
|
|||
|
||||
from .util import msg,ymsg,Msg,ydie
|
||||
from .devtools import *
|
||||
from .obj import BTCAmt,LTCAmt,BCHAmt,B2XAmt,XMRAmt,ETHAmt,CoinAddr,MMGenAddrType,PrivKey
|
||||
from .obj import CoinAddr,MMGenAddrType,PrivKey
|
||||
from .globalvars import g
|
||||
from .amt import BTCAmt,LTCAmt,BCHAmt,B2XAmt,XMRAmt
|
||||
from .altcoins.eth.obj import ETHAmt
|
||||
import mmgen.bech32 as bech32
|
||||
|
||||
parsed_wif = namedtuple('parsed_wif',['sec','pubkey_type','compressed'])
|
||||
|
|
|
|||
|
|
@ -21,8 +21,9 @@ txfile.py: Transaction file operations for the MMGen suite
|
|||
"""
|
||||
|
||||
from .common import *
|
||||
from .obj import HexStr,MMGenTxID,UnknownCoinAmt,CoinTxID,MMGenTxLabel
|
||||
from .obj import HexStr,MMGenTxID,CoinTxID,MMGenTxLabel
|
||||
from .tx import MMGenTxOutput,MMGenTxOutputList,MMGenTxInput,MMGenTxInputList
|
||||
from .amt import UnknownCoinAmt
|
||||
from .exception import MaxFileSizeExceeded
|
||||
|
||||
class MMGenTxFile:
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ from mmgen.common import *
|
|||
from mmgen.obj import *
|
||||
from mmgen.altcoins.eth.obj import *
|
||||
from mmgen.seedsplit import *
|
||||
from mmgen.amt import *
|
||||
|
||||
opts_data = {
|
||||
'sets': [('super_silent', True, 'silent', True)],
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ from mmgen.globalvars import g
|
|||
from mmgen.opts import opt
|
||||
from mmgen.util import die
|
||||
from mmgen.exception import *
|
||||
from mmgen.obj import ETHAmt
|
||||
from mmgen.altcoins.eth.obj import ETHAmt
|
||||
from mmgen.protocol import CoinProtocol
|
||||
from ..include.common import *
|
||||
from .common import *
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@ from subprocess import run,PIPE
|
|||
|
||||
from mmgen.globalvars import g
|
||||
from mmgen.opts import opt
|
||||
from mmgen.obj import MMGenRange,XMRAmt
|
||||
from mmgen.obj import MMGenRange
|
||||
from mmgen.amt import XMRAmt
|
||||
from mmgen.addr import KeyAddrList,AddrIdxList
|
||||
from ..include.common import *
|
||||
from .common import *
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ class unit_tests:
|
|||
|
||||
def coinamt(self,name,ut):
|
||||
|
||||
from mmgen.obj import BTCAmt,LTCAmt,XMRAmt,ETHAmt
|
||||
from mmgen.amt import BTCAmt,LTCAmt,XMRAmt
|
||||
from mmgen.altcoins.eth.obj import ETHAmt
|
||||
|
||||
for cls,aa,bb in (
|
||||
( BTCAmt, '1.2345', '11234567.897' ),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue