Skip to content
Permalink
Browse files

(fixup) pylightning: remove 'Satoshi' class.

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 9589a6f2bf935b616e8c3af14099fde040e601f0
Showing with 47 additions and 124 deletions.
  1. +1 −1 CHANGELOG.md
  2. +1 −1 contrib/pylightning/lightning/__init__.py
  3. +43 −120 contrib/pylightning/lightning/lightning.py
  4. +2 −2 tests/test_pay.py
@@ -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: New command `paystatus` gives detailed information on `pay` commands.
- 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
@@ -1,2 +1,2 @@
from .lightning import LightningRpc, RpcError, Satoshi, Millisatoshi
from .lightning import LightningRpc, RpcError, Millisatoshi
from .plugin import Plugin, monkey_patch
@@ -1,3 +1,4 @@
from decimal import Decimal
import json
import logging
import socket
@@ -22,16 +23,23 @@ class Millisatoshi:
"""
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 not v.endswith("msat"):
raise ValueError("Millisatoshi must end in msat")
self.millisatoshis = int(v[0:-4])
elif int(v) != v:
raise TypeError("Millisatoshi must be string-msat or int")
else:
if v.endswith("msat"):
self.millisatoshis = int(v[0:-4])
elif v.endswith("sat"):
self.millisatoshis = Decimal(v[0:-3]) * 1000
elif v.endswith("btc"):
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
else:
raise TypeError("Millisatoshi must be string with msat/sat/btc suffix or int")

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

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

def to_satoshi_round_down(self):
return Satoshi(self.millisatoshis // 1000)
def to_btc(self):
"""
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):
return self.__repr__()
@@ -57,38 +87,24 @@ def __int__(self):
return self.millisatoshis

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

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

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

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

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

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

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

def __mul__(self, other):
@@ -104,92 +120,6 @@ def __mod__(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):
def __init__(self, socket_path, executor=None, logger=logging, encoder=json.JSONEncoder, decoder=json.JSONDecoder):
self.socket_path = socket_path
@@ -320,8 +250,7 @@ def lightning_json_hook(json_object):
@staticmethod
def replace_amounts(obj):
"""
Recursively replace _msat and _sat fields with appropriate values with
Satoshi and Millisatoshi classes.
Recursively replace _msat fields with appropriate values with Millisatoshi.
"""
if isinstance(obj, dict):
for k, v in obj.items():
@@ -331,12 +260,6 @@ def replace_amounts(obj):
# Special case for array of msat values
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]
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:
obj[k] = LightningRpc.replace_amounts(v)
elif isinstance(obj, list):
@@ -1,5 +1,5 @@
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


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

def test_pay_amounts(node_factory):
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'])

0 comments on commit 9589a6f

Please sign in to comment.
You can’t perform that action at this time.