From 57fc1e99994d63b1aa57fe4d9bb0f42a51939fc3 Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Wed, 2 Dec 2020 10:51:40 +0100 Subject: [PATCH 1/3] pyln: extend msat floating testcases This adds two more xfail'ing testcases to show that the current way of parsing Millisatoshi decimals is not yet optimal. Changelog-None --- contrib/pyln-client/tests/test_units.py | 216 +++++++++++++++++++++++- 1 file changed, 213 insertions(+), 3 deletions(-) diff --git a/contrib/pyln-client/tests/test_units.py b/contrib/pyln-client/tests/test_units.py index 0491338032b0..582f918d9d1e 100644 --- a/contrib/pyln-client/tests/test_units.py +++ b/contrib/pyln-client/tests/test_units.py @@ -53,11 +53,221 @@ def test_floats(): # test floating point arithmetic amount = Millisatoshi("1000msat") * 0.1 assert int(amount) == 100 + amount = Millisatoshi("100msat") * 0.1 + assert int(amount) == 10 + amount = Millisatoshi("10msat") * 0.1 + assert int(amount) == 1 + + +def test_zero(): + # zero amounts are of course valid + amount = Millisatoshi("0btc") + assert int(amount) == 0 + amount = Millisatoshi("0sat") + assert int(amount) == 0 + amount = Millisatoshi("0msat") + assert int(amount) == 0 + + # zero floating amount as well + amount = Millisatoshi("0.0btc") + assert int(amount) == 0 + amount = Millisatoshi("0.0sat") + assert int(amount) == 0 + amount = Millisatoshi("0.0msat") + assert int(amount) == 0 + + # also anything multiplied by zero + amount = Millisatoshi("1btc") * 0 + assert int(amount) == 0 + amount = Millisatoshi("1sat") * 0 + assert int(amount) == 0 + amount = Millisatoshi("1msat") * 0 + assert int(amount) == 0 + + # and multiplied by a floating zero + amount = Millisatoshi("1btc") * 0.0 + assert int(amount) == 0 + amount = Millisatoshi("1sat") * 0.0 + assert int(amount) == 0 + amount = Millisatoshi("1msat") * 0.0 + assert int(amount) == 0 + + +@pytest.mark.xfail +def test_round_zero(): + # everything below 1msat should round down to zero + amount = Millisatoshi("1msat") * 0.9 + assert int(amount) == 0 + amount = Millisatoshi("10msat") * 0.09 + assert int(amount) == 0 + amount = Millisatoshi("100msat") * 0.009 + assert int(amount) == 0 + amount = Millisatoshi("1000msat") * 0.0009 + assert int(amount) == 0 + + amount = Millisatoshi("1sat") * 0.0009 + assert int(amount) == 0 + amount = Millisatoshi("0.1sat") * 0.009 + assert int(amount) == 0 + amount = Millisatoshi("0.01sat") * 0.09 + assert int(amount) == 0 + amount = Millisatoshi("0.001sat") * 0.9 + assert int(amount) == 0 + + amount = Millisatoshi("10sat") * 0.00009 + assert int(amount) == 0 + amount = Millisatoshi("100sat") * 0.000009 + assert int(amount) == 0 + amount = Millisatoshi("1000sat") * 0.0000009 + assert int(amount) == 0 + amount = Millisatoshi("10000sat") * 0.00000009 + assert int(amount) == 0 + amount = Millisatoshi("10000sat") * 0.00000009 + assert int(amount) == 0 + + amount = Millisatoshi("1btc") * 0.000000000009 + assert int(amount) == 0 + amount = Millisatoshi("0.1btc") * 0.00000000009 + assert int(amount) == 0 + amount = Millisatoshi("0.01btc") * 0.0000000009 + assert int(amount) == 0 + amount = Millisatoshi("0.001btc") * 0.000000009 + assert int(amount) == 0 + amount = Millisatoshi("0.0001btc") * 0.00000009 + assert int(amount) == 0 + amount = Millisatoshi("0.00001btc") * 0.0000009 + assert int(amount) == 0 + amount = Millisatoshi("0.000001btc") * 0.000009 + assert int(amount) == 0 + amount = Millisatoshi("0.0000001btc") * 0.00009 + assert int(amount) == 0 + amount = Millisatoshi("0.00000001btc") * 0.0009 + assert int(amount) == 0 + amount = Millisatoshi("0.000000001btc") * 0.009 + assert int(amount) == 0 + amount = Millisatoshi("0.0000000001btc") * 0.09 + assert int(amount) == 0 + amount = Millisatoshi("0.00000000001btc") * 0.9 + assert int(amount) == 0 + + +@pytest.mark.xfail +def test_round_down(): + # sub msat significatns should be floored + amount = Millisatoshi("2msat") * 0.9 + assert int(amount) == 1 + amount = Millisatoshi("20msat") * 0.09 + assert int(amount) == 1 + amount = Millisatoshi("200msat") * 0.009 + assert int(amount) == 1 + amount = Millisatoshi("2000msat") * 0.0009 + assert int(amount) == 1 + amount = Millisatoshi("2sat") * 0.0009 + assert int(amount) == 1 + amount = Millisatoshi("0.2sat") * 0.009 + assert int(amount) == 1 + amount = Millisatoshi("0.02sat") * 0.09 + assert int(amount) == 1 + amount = Millisatoshi("0.002sat") * 0.9 + assert int(amount) == 1 + + amount = Millisatoshi("20sat") * 0.00009 + assert int(amount) == 1 + amount = Millisatoshi("200sat") * 0.000009 + assert int(amount) == 1 + amount = Millisatoshi("2000sat") * 0.0000009 + assert int(amount) == 1 + amount = Millisatoshi("20000sat") * 0.00000009 + assert int(amount) == 1 + amount = Millisatoshi("20000sat") * 0.00000009 + assert int(amount) == 1 + + amount = Millisatoshi("2btc") * 0.000000000009 + assert int(amount) == 1 + amount = Millisatoshi("0.2btc") * 0.00000000009 + assert int(amount) == 1 + amount = Millisatoshi("0.02btc") * 0.0000000009 + assert int(amount) == 1 + amount = Millisatoshi("0.002btc") * 0.000000009 + assert int(amount) == 1 + amount = Millisatoshi("0.0002btc") * 0.00000009 + assert int(amount) == 1 + amount = Millisatoshi("0.00002btc") * 0.0000009 + assert int(amount) == 1 + amount = Millisatoshi("0.000002btc") * 0.000009 + assert int(amount) == 1 + amount = Millisatoshi("0.0000002btc") * 0.00009 + assert int(amount) == 1 + amount = Millisatoshi("0.00000002btc") * 0.0009 + assert int(amount) == 1 + amount = Millisatoshi("0.000000002btc") * 0.009 + assert int(amount) == 1 + amount = Millisatoshi("0.0000000002btc") * 0.09 + assert int(amount) == 1 + amount = Millisatoshi("0.00000000002btc") * 0.9 + assert int(amount) == 1 + + +def test_nosubmsat(): # sub millisatoshi are not a concept yet with pytest.raises(ValueError, match='Millisatoshi must be a whole number'): - amount = Millisatoshi("0.000000000001btc") + Millisatoshi("0.1msat") + with pytest.raises(ValueError, match='Millisatoshi must be a whole number'): + Millisatoshi(".1msat") + with pytest.raises(ValueError, match='Millisatoshi must be a whole number'): + Millisatoshi("0.0001sat") with pytest.raises(ValueError, match='Millisatoshi must be a whole number'): - amount = Millisatoshi("0.0001sat") + Millisatoshi(".0001sat") with pytest.raises(ValueError, match='Millisatoshi must be a whole number'): - amount = Millisatoshi("0.1msat") + Millisatoshi("0.000000000001btc") + with pytest.raises(ValueError, match='Millisatoshi must be a whole number'): + Millisatoshi(".000000000001btc") + + +def test_nonegative(): + with pytest.raises(ValueError, match='Millisatoshi must be >= 0'): + Millisatoshi("-1btc") + with pytest.raises(ValueError, match='Millisatoshi must be >= 0'): + Millisatoshi("-1.0btc") + with pytest.raises(ValueError, match='Millisatoshi must be >= 0'): + Millisatoshi("-0.1btc") + with pytest.raises(ValueError, match='Millisatoshi must be >= 0'): + Millisatoshi("-.1btc") + with pytest.raises(ValueError, match='Millisatoshi must be >= 0'): + Millisatoshi("-1sat") + with pytest.raises(ValueError, match='Millisatoshi must be >= 0'): + Millisatoshi("-1.0sat") + with pytest.raises(ValueError, match='Millisatoshi must be >= 0'): + Millisatoshi("-0.1sat") + with pytest.raises(ValueError, match='Millisatoshi must be >= 0'): + Millisatoshi("-.1sat") + with pytest.raises(ValueError, match='Millisatoshi must be >= 0'): + Millisatoshi("-1msat") + with pytest.raises(ValueError, match='Millisatoshi must be >= 0'): + Millisatoshi("-1.0msat") + + with pytest.raises(ValueError, match='Millisatoshi must be >= 0'): + Millisatoshi("1msat") * -1 + with pytest.raises(ValueError, match='Millisatoshi must be >= 0'): + Millisatoshi("1msat") * -42 + with pytest.raises(ValueError, match='Millisatoshi must be >= 0'): + Millisatoshi("1sat") * -1 + with pytest.raises(ValueError, match='Millisatoshi must be >= 0'): + Millisatoshi("1btc") * -1 + + with pytest.raises(ValueError, match='Millisatoshi must be >= 0'): + Millisatoshi("1msat") / -1 + with pytest.raises(ValueError, match='Millisatoshi must be >= 0'): + Millisatoshi("1msat") / -0.5 + with pytest.raises(ValueError, match='Millisatoshi must be >= 0'): + Millisatoshi("1sat") / -1 + with pytest.raises(ValueError, match='Millisatoshi must be >= 0'): + Millisatoshi("1btc") / -1 + + with pytest.raises(ValueError, match='Millisatoshi must be >= 0'): + Millisatoshi("1msat") // -1 + with pytest.raises(ValueError, match='Millisatoshi must be >= 0'): + Millisatoshi("1sat") // -1 + with pytest.raises(ValueError, match='Millisatoshi must be >= 0'): + Millisatoshi("1btc") // -1 From 1fc6be458a32b37f5ffb70a5e557ff697ca68e53 Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Wed, 2 Dec 2020 12:30:46 +0100 Subject: [PATCH 2/3] pyln: fix msat float multiplication --- contrib/pyln-client/pyln/client/lightning.py | 2 +- contrib/pyln-client/tests/test_units.py | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index febc4b9449fa..e87a57c05a2d 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -177,7 +177,7 @@ def __sub__(self, other: 'Millisatoshi') -> 'Millisatoshi': return Millisatoshi(int(self) - int(other)) def __mul__(self, other: int) -> 'Millisatoshi': - return Millisatoshi(self.millisatoshis * other) + return Millisatoshi(int(self.millisatoshis * other)) def __truediv__(self, other: Union[int, float]) -> 'Millisatoshi': return Millisatoshi(int(self.millisatoshis / other)) diff --git a/contrib/pyln-client/tests/test_units.py b/contrib/pyln-client/tests/test_units.py index 582f918d9d1e..bc612a6c7303 100644 --- a/contrib/pyln-client/tests/test_units.py +++ b/contrib/pyln-client/tests/test_units.py @@ -93,7 +93,6 @@ def test_zero(): assert int(amount) == 0 -@pytest.mark.xfail def test_round_zero(): # everything below 1msat should round down to zero amount = Millisatoshi("1msat") * 0.9 @@ -151,7 +150,6 @@ def test_round_zero(): assert int(amount) == 0 -@pytest.mark.xfail def test_round_down(): # sub msat significatns should be floored amount = Millisatoshi("2msat") * 0.9 From 2486aab68e32bb896c0932c0e1096ec820c326b7 Mon Sep 17 00:00:00 2001 From: Michael Schmoock Date: Mon, 7 Dec 2020 11:19:27 +0100 Subject: [PATCH 3/3] plyn: use math.floor for msat mul and div --- contrib/pyln-client/pyln/client/lightning.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index e87a57c05a2d..753bd8f2cc90 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -177,13 +177,13 @@ def __sub__(self, other: 'Millisatoshi') -> 'Millisatoshi': return Millisatoshi(int(self) - int(other)) def __mul__(self, other: int) -> 'Millisatoshi': - return Millisatoshi(int(self.millisatoshis * other)) + return Millisatoshi(floor(self.millisatoshis * other)) def __truediv__(self, other: Union[int, float]) -> 'Millisatoshi': - return Millisatoshi(int(self.millisatoshis / other)) + return Millisatoshi(floor(self.millisatoshis / other)) def __floordiv__(self, other: Union[int, float]) -> 'Millisatoshi': - return Millisatoshi(int(self.millisatoshis // float(other))) + return Millisatoshi(floor(self.millisatoshis // float(other))) def __mod__(self, other: Union[float, int]) -> 'Millisatoshi': return Millisatoshi(int(self.millisatoshis % other))