Skip to content

Commit

Permalink
(fixup) pylightning: remove 'Satoshi' class.
Browse files Browse the repository at this point in the history
All the world is now Millisatoshis.  You can initialize Millisatoshis with
'sat' or 'btc'-appended strings though, and we have converters to those
versions too.

Makes things a bit simpler.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
  • Loading branch information
rustyrussell committed Feb 21, 2019
1 parent f61a92a commit 9589a6f
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 124 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Expand Up @@ -27,7 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- JSON API: `listchannels` now takes a `source` option to filter by node id. - JSON API: `listchannels` now takes a `source` option to filter by node id.
- JSON API: New command `paystatus` gives detailed information on `pay` commands. - JSON API: New command `paystatus` gives detailed information on `pay` commands.
- JSON API: `getroute` `riskfactor` argument is simplified; `pay` now defaults to setting it to 10. - JSON API: `getroute` `riskfactor` argument is simplified; `pay` now defaults to setting it to 10.
- pylightning: New class 'Satoshi' and 'Millisatoshi' can be used for JSON API, and new '_sat' and '_msat' fields are turned into these classes on reading. - pylightning: New class 'Millisatoshi' can be used for JSON API, and new '_msat' fields are turned into this on reading.


### Changed ### Changed


Expand Down
2 changes: 1 addition & 1 deletion contrib/pylightning/lightning/__init__.py
@@ -1,2 +1,2 @@
from .lightning import LightningRpc, RpcError, Satoshi, Millisatoshi from .lightning import LightningRpc, RpcError, Millisatoshi
from .plugin import Plugin, monkey_patch from .plugin import Plugin, monkey_patch
163 changes: 43 additions & 120 deletions contrib/pylightning/lightning/lightning.py
@@ -1,3 +1,4 @@
from decimal import Decimal
import json import json
import logging import logging
import socket import socket
Expand All @@ -22,16 +23,23 @@ class Millisatoshi:
""" """
def __init__(self, v): def __init__(self, v):
""" """
Takes either a string ending in 'msat' or an integer. Takes either a string ending in 'msat', 'sat', 'btc' or an integer.
""" """
if isinstance(v, str): if isinstance(v, str):
if not v.endswith("msat"): if v.endswith("msat"):
raise ValueError("Millisatoshi must end in msat") self.millisatoshis = int(v[0:-4])
self.millisatoshis = int(v[0:-4]) elif v.endswith("sat"):
elif int(v) != v: self.millisatoshis = Decimal(v[0:-3]) * 1000
raise TypeError("Millisatoshi must be string-msat or int") elif v.endswith("btc"):
else: self.millisatoshis = Decimal(v[0:-3]) * 1000 * 10**8
if self.millisatoshis != int(self.millisatoshis):
raise ValueError("Millisatoshi must be a whole number")
elif isinstance(v, Millisatoshi):
self.millisatoshis = v.millisatoshis
elif int(v) == v:
self.millisatoshis = v self.millisatoshis = v
else:
raise TypeError("Millisatoshi must be string with msat/sat/btc suffix or int")


if self.millisatoshis < 0: if self.millisatoshis < 0:
raise ValueError("Millisatoshi must be >= 0") raise ValueError("Millisatoshi must be >= 0")
Expand All @@ -43,12 +51,34 @@ def __repr__(self):
return str(self.millisatoshis) + "msat" return str(self.millisatoshis) + "msat"


def to_satoshi(self): def to_satoshi(self):
if self.satoshi % 1000 != 0: """
raise ValueError("Not a whole number of satoshis") Return a Decimal representing the number of satoshis
return Satoshi(self.millisatoshis / 1000) """
return Decimal(self.millisatoshis) / 1000


def to_satoshi_round_down(self): def to_btc(self):
return Satoshi(self.millisatoshis // 1000) """
Return a Decimal representing the number of bitcoin
"""
return Decimal(self.millisatoshis) / 1000 / 10**8

def to_satoshi_str(self):
"""
Return a string of form 1234sat or 1234.567sat.
"""
if self.millisatoshis % 1000:
return '{:.3f}sat'.format(self.to_satoshi())
else:
return '{:.0f}sat'.format(self.to_satoshi())

def to_btc_str(self):
"""
Return a string of form 12.34567890btc or 12.34567890123btc.
"""
if self.millisatoshis % 1000:
return '{:.8f}btc'.format(self.to_btc())
else:
return '{:.11f}btc'.format(self.to_btc())


def to_json(self): def to_json(self):
return self.__repr__() return self.__repr__()
Expand All @@ -57,38 +87,24 @@ def __int__(self):
return self.millisatoshis return self.millisatoshis


def __lt__(self, other): def __lt__(self, other):
if isinstance(other, Satoshi):
other = other.to_millisatoshi()
return self.millisatoshis < other.millisatoshis return self.millisatoshis < other.millisatoshis


def __le__(self, other): def __le__(self, other):
if isinstance(other, Satoshi):
other = other.to_millisatoshi()
return self.millisatoshis <= other.millisatoshis return self.millisatoshis <= other.millisatoshis


def __eq__(self, other): def __eq__(self, other):
if isinstance(other, Satoshi):
other = other.to_millisatoshi()
return self.millisatoshis == other.millisatoshis return self.millisatoshis == other.millisatoshis


def __gt__(self, other): def __gt__(self, other):
if isinstance(other, Satoshi):
other = other.to_millisatoshi()
return self.millisatoshis > other.millisatoshis return self.millisatoshis > other.millisatoshis


def __ge__(self, other): def __ge__(self, other):
if isinstance(other, Satoshi):
other = other.to_millisatoshi()
return self.millisatoshis >= other.millisatoshis return self.millisatoshis >= other.millisatoshis


def __add__(self, other): def __add__(self, other):
if isinstance(other, Satoshi):
other = other.to_millisatoshi()
return Millisatoshi(int(self) + int(other)) return Millisatoshi(int(self) + int(other))


def __sub__(self, other): def __sub__(self, other):
if isinstance(other, Satoshi):
other = other.to_millisatoshi()
return Millisatoshi(int(self) - int(other)) return Millisatoshi(int(self) - int(other))


def __mul__(self, other): def __mul__(self, other):
Expand All @@ -104,92 +120,6 @@ def __mod__(self, other):
return Millisatoshi(int(self) % other) return Millisatoshi(int(self) % other)




class Satoshi:
"""
A subtype to represent satoshi units.
Many JSON API fields are expressed in satoshi: these automatically get
turned into Satoshi types. Converts to and from int.
"""
def __init__(self, v):
"""
Takes either a string ending in 'sat' or an integer.
"""
if isinstance(v, str):
if not v.endswith("sat"):
raise ValueError("Satoshi must end in sat")
self.satoshis = int(v[0:-3])
elif int(v) != v:
raise TypeError("Satoshi must be string-msat or int")
else:
self.satoshis = v

if self.satoshis < 0:
raise ValueError("Satoshi must be >= 0")

def __repr__(self):
"""
Appends the 'sat' as expected for this type.
"""
return str(self.satoshis) + "sat"

def to_json(self):
return self.__repr__()

def __int__(self):
return self.satoshis

def to_millisatoshi(self):
return Millisatoshi(self.satoshis * 1000)

def __lt__(self, other):
if isinstance(other, Millisatoshi):
return self.to_millisatoshi() < other
return self.satoshis < other.satoshis

def __le__(self, other):
if isinstance(other, Millisatoshi):
return self.to_millisatoshi() <= other
return self.satoshis <= other.satoshis

def __eq__(self, other):
if isinstance(other, Millisatoshi):
return self.to_millisatoshi() == other
return self.satoshis == other.satoshis

def __gt__(self, other):
if isinstance(other, Millisatoshi):
return self.to_millisatoshi() > other
return self.satoshis > other.satoshis

def __ge__(self, other):
if isinstance(other, Millisatoshi):
return self.to_millisatoshi() >= other
return self.satoshis >= other.satoshis

def __add__(self, other):
if isinstance(other, Millisatoshi):
other = other.to_satoshi()
return Satoshi(int(self) + int(other))

def __sub__(self, other):
if isinstance(other, Millisatoshi):
other = other.to_satoshi()
return Satoshi(int(self) - int(other))

def __mul__(self, other):
return Satoshi(int(self) * other)

def __truediv__(self, other):
return Satoshi(int(self) / other)

def __floordiv__(self, other):
return Satoshi(int(self) // other)

def __mod__(self, other):
return Satoshi(int(self) % other)


class UnixDomainSocketRpc(object): class UnixDomainSocketRpc(object):
def __init__(self, socket_path, executor=None, logger=logging, encoder=json.JSONEncoder, decoder=json.JSONDecoder): def __init__(self, socket_path, executor=None, logger=logging, encoder=json.JSONEncoder, decoder=json.JSONDecoder):
self.socket_path = socket_path self.socket_path = socket_path
Expand Down Expand Up @@ -320,8 +250,7 @@ def lightning_json_hook(json_object):
@staticmethod @staticmethod
def replace_amounts(obj): def replace_amounts(obj):
""" """
Recursively replace _msat and _sat fields with appropriate values with Recursively replace _msat fields with appropriate values with Millisatoshi.
Satoshi and Millisatoshi classes.
""" """
if isinstance(obj, dict): if isinstance(obj, dict):
for k, v in obj.items(): for k, v in obj.items():
Expand All @@ -331,12 +260,6 @@ def replace_amounts(obj):
# Special case for array of msat values # Special case for array of msat values
elif isinstance(v, list) and all(isinstance(e, str) and e.endswith('msat') for e in v): elif isinstance(v, list) and all(isinstance(e, str) and e.endswith('msat') for e in v):
obj[k] = [Millisatoshi(e) for e in v] obj[k] = [Millisatoshi(e) for e in v]
elif k.endswith('sat'):
if isinstance(v, str) and v.endswith('sat'):
obj[k] = Satoshi(v)
# Special case for array of sat values
elif isinstance(v, list) and all(isinstance(e, str) and e.endswith('sat') for e in v):
obj[k] = [Satoshi(e) for e in v]
else: else:
obj[k] = LightningRpc.replace_amounts(v) obj[k] = LightningRpc.replace_amounts(v)
elif isinstance(obj, list): elif isinstance(obj, list):
Expand Down
4 changes: 2 additions & 2 deletions tests/test_pay.py
@@ -1,5 +1,5 @@
from fixtures import * # noqa: F401,F403 from fixtures import * # noqa: F401,F403
from lightning import RpcError, Satoshi, Millisatoshi from lightning import RpcError, Millisatoshi
from utils import DEVELOPER, wait_for, only_one, sync_blockheight, SLOW_MACHINE from utils import DEVELOPER, wait_for, only_one, sync_blockheight, SLOW_MACHINE




Expand Down Expand Up @@ -61,7 +61,7 @@ def test_pay(node_factory):


def test_pay_amounts(node_factory): def test_pay_amounts(node_factory):
l1, l2 = node_factory.line_graph(2) l1, l2 = node_factory.line_graph(2)
inv = l2.rpc.invoice(Satoshi(123), 'test_pay_amounts', 'description')['bolt11'] inv = l2.rpc.invoice(Millisatoshi("123sat"), 'test_pay_amounts', 'description')['bolt11']


invoice = only_one(l2.rpc.listinvoices('test_pay_amounts')['invoices']) invoice = only_one(l2.rpc.listinvoices('test_pay_amounts')['invoices'])


Expand Down

0 comments on commit 9589a6f

Please sign in to comment.