diff --git a/silkaj/cli.py b/silkaj/cli.py index 5ec6fbb75c5c66b6da3185382a81587e57c2d6a9..f2e38eca1131861f5f3592691f90277d2a54f6c1 100644 --- a/silkaj/cli.py +++ b/silkaj/cli.py @@ -33,7 +33,7 @@ from silkaj.constants import ( ) from silkaj.g1_monetary_license import license_command from silkaj.membership import send_membership -from silkaj.money import cmd_amount +from silkaj.money.balance import cmd_amount from silkaj.tx import send_transaction from silkaj.tx_history import transaction_history from silkaj.wot import id_pubkey_correspondence, received_sent_certifications diff --git a/silkaj/money/__init__.py b/silkaj/money/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..58426bbc2bbfc951dc181bdd19b5b2569c074af0 --- /dev/null +++ b/silkaj/money/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2016-2022 Maël Azimi <m.a@moul.re> +# +# Silkaj is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Silkaj 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with Silkaj. If not, see <https://www.gnu.org/licenses/>. diff --git a/silkaj/money.py b/silkaj/money/balance.py similarity index 56% rename from silkaj/money.py rename to silkaj/money/balance.py index 0237de0852404f88318a53e7b8c48e6b28446104..a24fd26f74bf502f338a8107bd91bb3b908783e4 100644 --- a/silkaj/money.py +++ b/silkaj/money/balance.py @@ -13,20 +13,17 @@ # You should have received a copy of the GNU Affero General Public License # along with Silkaj. If not, see <https://www.gnu.org/licenses/>. -import functools import sys -from typing import List, Tuple, Union +from typing import List from click import Context, argument, command, echo, pass_context -from duniterpy.api.bma import blockchain, tx -from duniterpy.documents.transaction import InputSource, OutputSource from silkaj import tui from silkaj import wot_tools as wt from silkaj.auth import auth_method, has_auth_method from silkaj.blockchain.tools import get_head_block from silkaj.crypto_tools import is_pubkey_and_check -from silkaj.network_tools import client_instance +from silkaj.money import tools as m_tools from silkaj.tools import get_currency_symbol @@ -59,7 +56,7 @@ def cmd_amount(ctx: Context, pubkeys: str) -> None: total = [0, 0] for pubkey in pubkeys_list: - inputs_balance = get_amount_from_pubkey(pubkey) + inputs_balance = m_tools.get_amount_from_pubkey(pubkey) show_amount_from_pubkey(pubkey, inputs_balance) total[0] += inputs_balance[0] total[1] += inputs_balance[1] @@ -68,7 +65,7 @@ def cmd_amount(ctx: Context, pubkeys: str) -> None: else: key = auth_method() pubkey = key.pubkey - show_amount_from_pubkey(pubkey, get_amount_from_pubkey(pubkey)) + show_amount_from_pubkey(pubkey, m_tools.get_amount_from_pubkey(pubkey)) def show_amount_from_pubkey(label: str, inputs_balance: List[int]) -> None: @@ -79,7 +76,7 @@ def show_amount_from_pubkey(label: str, inputs_balance: List[int]) -> None: totalAmountInput = inputs_balance[0] balance = inputs_balance[1] currency_symbol = get_currency_symbol() - ud_value = get_ud_value() + ud_value = m_tools.get_ud_value() average = get_average() member = None @@ -121,81 +118,3 @@ def show_amount_from_pubkey(label: str, inputs_balance: List[int]) -> None: def get_average() -> int: head = get_head_block() return head["monetaryMass"] / head["membersCount"] - - -def get_amount_from_pubkey(pubkey: str) -> List[int]: - listinput, amount = get_sources(pubkey) - - totalAmountInput = 0 - for _input in listinput: - totalAmountInput += amount_in_current_base(_input) - return [totalAmountInput, amount] - - -def get_sources(pubkey: str) -> Tuple[List[InputSource], int]: - client = client_instance() - # Sources written into the blockchain - sources = client(tx.sources, pubkey) - - listinput = [] - amount = 0 - for source in sources["sources"]: - if source["conditions"] == f"SIG({pubkey})": - listinput.append( - InputSource( - amount=source["amount"], - base=source["base"], - source=source["type"], - origin_id=source["identifier"], - index=source["noffset"], - ) - ) - amount += amount_in_current_base(listinput[-1]) - - # pending source - history = (client(tx.pending, pubkey))["history"] - pendings = history["sending"] + history["receiving"] + history["pending"] - - # add pending output - pending_sources = [] - for pending in pendings: - for i, output in enumerate(pending["outputs"]): - # duniterpy#80 - outputsplited = output.split(":") - if outputsplited[2] == f"SIG({pubkey})": - inputgenerated = InputSource( - amount=int(outputsplited[0]), - base=int(outputsplited[1]), - source="T", - origin_id=pending["hash"], - index=i, - ) - if inputgenerated not in listinput: - # add pendings before blockchain sources for change txs - listinput.insert(0, inputgenerated) - - for _input in pending["inputs"]: - pending_sources.append(InputSource.from_inline(_input)) - - # remove input already used - for _input in pending_sources: - if _input in listinput: - listinput.remove(_input) - - return listinput, amount - - -@functools.lru_cache(maxsize=1) -def get_ud_value() -> int: - client = client_instance() - blockswithud = client(blockchain.ud) - NBlastUDblock = blockswithud["result"]["blocks"][-1] - lastUDblock = client(blockchain.block, NBlastUDblock) - return lastUDblock["dividend"] * 10 ** lastUDblock["unitbase"] - - -def amount_in_current_base(source: Union[InputSource, OutputSource]) -> int: - """ - Get amount in current base from input or output source - """ - return source.amount * 10**source.base diff --git a/silkaj/money/tools.py b/silkaj/money/tools.py new file mode 100644 index 0000000000000000000000000000000000000000..01b982b55e5156cf0d6e066e57d035d0e5b56f2f --- /dev/null +++ b/silkaj/money/tools.py @@ -0,0 +1,100 @@ +# Copyright 2016-2022 Maël Azimi <m.a@moul.re> +# +# Silkaj is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Silkaj 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with Silkaj. If not, see <https://www.gnu.org/licenses/>. + +import functools +from typing import List, Tuple, Union + +from duniterpy.api.bma import blockchain, tx +from duniterpy.documents.transaction import InputSource, OutputSource + +from silkaj.network_tools import client_instance + + +def get_amount_from_pubkey(pubkey: str) -> List[int]: + listinput, amount = get_sources(pubkey) + + totalAmountInput = 0 + for _input in listinput: + totalAmountInput += amount_in_current_base(_input) + return [totalAmountInput, amount] + + +def get_sources(pubkey: str) -> Tuple[List[InputSource], int]: + client = client_instance() + # Sources written into the blockchain + sources = client(tx.sources, pubkey) + + listinput = [] + amount = 0 + for source in sources["sources"]: + if source["conditions"] == f"SIG({pubkey})": + listinput.append( + InputSource( + amount=source["amount"], + base=source["base"], + source=source["type"], + origin_id=source["identifier"], + index=source["noffset"], + ) + ) + amount += amount_in_current_base(listinput[-1]) + + # pending source + history = (client(tx.pending, pubkey))["history"] + pendings = history["sending"] + history["receiving"] + history["pending"] + + # add pending output + pending_sources = [] + for pending in pendings: + for i, output in enumerate(pending["outputs"]): + # duniterpy#80 + outputsplited = output.split(":") + if outputsplited[2] == f"SIG({pubkey})": + inputgenerated = InputSource( + amount=int(outputsplited[0]), + base=int(outputsplited[1]), + source="T", + origin_id=pending["hash"], + index=i, + ) + if inputgenerated not in listinput: + # add pendings before blockchain sources for change txs + listinput.insert(0, inputgenerated) + + for _input in pending["inputs"]: + pending_sources.append(InputSource.from_inline(_input)) + + # remove input already used + for _input in pending_sources: + if _input in listinput: + listinput.remove(_input) + + return listinput, amount + + +@functools.lru_cache(maxsize=1) +def get_ud_value() -> int: + client = client_instance() + blockswithud = client(blockchain.ud) + NBlastUDblock = blockswithud["result"]["blocks"][-1] + lastUDblock = client(blockchain.block, NBlastUDblock) + return lastUDblock["dividend"] * 10 ** lastUDblock["unitbase"] + + +def amount_in_current_base(source: Union[InputSource, OutputSource]) -> int: + """ + Get amount in current base from input or output source + """ + return source.amount * 10**source.base diff --git a/silkaj/tx.py b/silkaj/tx.py index 9c791461016c99d31e17756e32bdfaa318f3a4ea..ff7689c2096d4fadb4e84d160479d0d1bf35be94 100644 --- a/silkaj/tx.py +++ b/silkaj/tx.py @@ -33,7 +33,6 @@ from duniterpy.key import SigningKey from silkaj import auth, cli_tools from silkaj import crypto_tools as ct -from silkaj import money from silkaj import network_tools as nt from silkaj import tools, tui from silkaj.blockchain import tools as bc_tools @@ -42,6 +41,7 @@ from silkaj.constants import ( MINIMAL_ABSOLUTE_TX_AMOUNT, MINIMAL_RELATIVE_TX_AMOUNT, ) +from silkaj.money import tools as m_tools MAX_COMMENT_LENGTH = 255 @@ -146,7 +146,7 @@ def send_transaction( key = auth.auth_method() issuer_pubkey = key.pubkey - pubkey_amount = money.get_amount_from_pubkey(issuer_pubkey) + pubkey_amount = m_tools.get_amount_from_pubkey(issuer_pubkey) if allsources: if pubkey_amount[0] <= 0: tools.message_exit( @@ -235,7 +235,7 @@ def parse_file_containing_amounts_recipients( if reference == "ABSOLUTE": reference_mult = CENT_MULT_TO_UNIT else: - reference_mult = money.get_ud_value() + reference_mult = m_tools.get_ud_value() tx_amounts = compute_amounts(amounts, reference_mult) return tx_amounts, recipients @@ -252,7 +252,7 @@ def transaction_amount( if amounts: amounts_list = compute_amounts(amounts, CENT_MULT_TO_UNIT) elif UDs_amounts: - UD_value = money.get_ud_value() + UD_value = m_tools.get_ud_value() amounts_list = compute_amounts(UDs_amounts, UD_value) if len(amounts_list) != len(outputAddresses) and len(amounts_list) != 1: tools.message_exit( @@ -330,7 +330,7 @@ def gen_confirmation_table( """ currency_symbol = tools.get_currency_symbol() - ud_value = money.get_ud_value() + ud_value = m_tools.get_ud_value() total_tx_amount = sum(tx_amounts) tx = [] # type: List[List[str]] # display account situation @@ -370,7 +370,7 @@ def gen_confirmation_table( def get_list_input_for_transaction( pubkey: str, TXamount: int, outputs_number: int ) -> Tuple[List[InputSource], int, bool]: - listinput = money.get_sources(pubkey)[0] + listinput = m_tools.get_sources(pubkey)[0] maxInputsNumber = max_inputs_number(outputs_number, NBR_ISSUERS) # generate final list source listinputfinal = [] @@ -378,8 +378,8 @@ def get_list_input_for_transaction( intermediatetransaction = False for nbr_inputs, _input in enumerate(listinput, start=1): listinputfinal.append(_input) - totalAmountInput += money.amount_in_current_base(_input) - TXamount -= money.amount_in_current_base(_input) + totalAmountInput += m_tools.amount_in_current_base(_input) + TXamount -= m_tools.amount_in_current_base(_input) # if too much sources, it's an intermediate transaction. amount_not_reached_and_max_doc_size_reached = ( TXamount > 0 and MAX_INPUTS_PER_TX <= nbr_inputs diff --git a/silkaj/tx_history.py b/silkaj/tx_history.py index 278ac5c46360d33b3ac357eb3548adf4baef9eeb..2a00e23297348fb5b9b1573972ec436dfd370f31 100644 --- a/silkaj/tx_history.py +++ b/silkaj/tx_history.py @@ -27,7 +27,11 @@ from pendulum import from_timestamp, now from silkaj import wot_tools as wt from silkaj.constants import ALL, ALL_DIGITAL from silkaj.crypto_tools import check_pubkey_format, validate_checksum -from silkaj.money import amount_in_current_base, get_amount_from_pubkey, get_ud_value +from silkaj.money.tools import ( + amount_in_current_base, + get_amount_from_pubkey, + get_ud_value, +) from silkaj.network_tools import client_instance from silkaj.tools import get_currency_symbol from silkaj.tui import Table, gen_pubkey_checksum diff --git a/tests/money/__init__.py b/tests/money/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..58426bbc2bbfc951dc181bdd19b5b2569c074af0 --- /dev/null +++ b/tests/money/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2016-2022 Maël Azimi <m.a@moul.re> +# +# Silkaj is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Silkaj 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with Silkaj. If not, see <https://www.gnu.org/licenses/>. diff --git a/tests/test_money.py b/tests/money/test_tools.py similarity index 99% rename from tests/test_money.py rename to tests/money/test_tools.py index 42170b17ee171bf4f4fb4377f26a55dddcce1089..69b2eec4b304cf2b5180514e46a782168a0a6d00 100644 --- a/tests/test_money.py +++ b/tests/money/test_tools.py @@ -18,7 +18,7 @@ from click.testing import CliRunner from silkaj.cli import cli from silkaj.constants import FAILURE_EXIT_STATUS -from silkaj.money import get_sources +from silkaj.money.tools import get_sources from silkaj.tui import gen_pubkey_checksum diff --git a/tests/patched/money.py b/tests/patched/money.py index f309885814b68e1dc9ef79e80a73e7ada82b55d5..88b5bfc6db6f85d91d3640ba191424090c1689e7 100644 --- a/tests/patched/money.py +++ b/tests/patched/money.py @@ -18,7 +18,7 @@ from duniterpy.documents.transaction import InputSource from patched.test_constants import mock_ud_value -from silkaj.money import amount_in_current_base +from silkaj.money.tools import amount_in_current_base from silkaj.tx import MAX_INPUTS_PER_TX diff --git a/tests/test_tx.py b/tests/test_tx.py index 295b93f57c2d207cd4c8f8c7da51eae66b615af6..bc76805ff504cd68c5c0a63119d32b8dd4674e0e 100644 --- a/tests/test_tx.py +++ b/tests/test_tx.py @@ -22,7 +22,7 @@ from click.testing import CliRunner from patched.auth import patched_auth_method from patched.money import patched_get_sources, patched_get_ud_value from patched.test_constants import mock_ud_value -from silkaj import auth, money, tx +from silkaj import auth, tx from silkaj.cli import cli from silkaj.constants import ( FAILURE_EXIT_STATUS, @@ -30,6 +30,7 @@ from silkaj.constants import ( MINIMAL_RELATIVE_TX_AMOUNT, PUBKEY_MIN_LENGTH, ) +from silkaj.money import tools as m_tools # create test auths @@ -47,7 +48,7 @@ def test_transaction_amount(monkeypatch): """test passed amounts passed tx command float ≠100 does not give the exact value""" - monkeypatch.setattr(money, "get_ud_value", patched_get_ud_value) + monkeypatch.setattr(m_tools, "get_ud_value", patched_get_ud_value) trials = ( # tests for --amount (unit) ([141.89], None, ["A"], [14189]), @@ -108,7 +109,7 @@ def test_transaction_amount_errors( amounts, UDs_amounts, outputAddresses, expected, capsys, monkeypatch ): # patched functions - monkeypatch.setattr(money, "get_ud_value", patched_get_ud_value) + monkeypatch.setattr(m_tools, "get_ud_value", patched_get_ud_value) # check program exit on error with pytest.raises(SystemExit) as pytest_exit: @@ -213,7 +214,7 @@ def test_tx_passed_all_sources_empty( # patch functions monkeypatch.setattr(auth, "auth_method", auth_method) - monkeypatch.setattr(money, "get_sources", patched_get_sources) + monkeypatch.setattr(m_tools, "get_sources", patched_get_sources) patched_gen_confirmation_table = Mock() monkeypatch.setattr(tx, "gen_confirmation_table", patched_gen_confirmation_table) diff --git a/tests/test_tx_file.py b/tests/test_tx_file.py index d003228b5fbaf865413a41a66ecda724aaf92570..64ead71a350351f8426c258f5032b01caa63cfb6 100644 --- a/tests/test_tx_file.py +++ b/tests/test_tx_file.py @@ -17,7 +17,7 @@ import pytest from click.testing import CliRunner from silkaj.constants import CENT_MULT_TO_UNIT -from silkaj.money import get_ud_value +from silkaj.money.tools import get_ud_value from silkaj.tx import parse_file_containing_amounts_recipients FILE_PATH = "recipients.txt" diff --git a/tests/test_unit_tx.py b/tests/test_unit_tx.py index 30bafcd0b392e1de408de7c48e292c5f93d9569f..6dc148609bc903a78753fd5d5a71d2c8f2958b49 100644 --- a/tests/test_unit_tx.py +++ b/tests/test_unit_tx.py @@ -32,10 +32,11 @@ from patched.money import Counter, patched_get_sources, patched_get_ud_value from patched.test_constants import mock_ud_value from patched.tools import patched_get_currency_symbol from patched.wot import patched_is_member -from silkaj import auth, money, network_tools, tools, tx, wot_tools +from silkaj import auth, network_tools, tools, tx, wot_tools from silkaj.blockchain import tools as bc_tools from silkaj.cli import cli from silkaj.constants import CENT_MULT_TO_UNIT, G1_SYMBOL +from silkaj.money import tools as m_tools from silkaj.tui import display_amount, display_pubkey # Values @@ -111,7 +112,7 @@ def test_gen_confirmation_table( ): # patched functions monkeypatch.setattr(wot_tools, "is_member", patched_is_member) - monkeypatch.setattr(money, "get_ud_value", patched_get_ud_value) + monkeypatch.setattr(m_tools, "get_ud_value", patched_get_ud_value) monkeypatch.setattr(tools, "get_currency_symbol", patched_get_currency_symbol) # creating expected list @@ -421,7 +422,7 @@ def test_get_list_input_for_transaction( """ # patched functions - monkeypatch.setattr(money, "get_sources", patched_get_sources) + monkeypatch.setattr(m_tools, "get_sources", patched_get_sources) # reset Counter.counter Counter.counter = 0 # testing error exit @@ -826,7 +827,7 @@ def test_handle_intermediaries_transactions( ): # patched functions patched_generate_and_send_transaction = Mock(return_value=None) - monkeypatch.setattr(money, "get_sources", patched_get_sources) + monkeypatch.setattr(m_tools, "get_sources", patched_get_sources) monkeypatch.setattr( tx, "generate_and_send_transaction", patched_generate_and_send_transaction ) @@ -986,8 +987,8 @@ def test_send_transaction( "handle_intermediaries_transactions", patched_handle_intermediaries_transactions, ) - monkeypatch.setattr(money, "get_sources", patched_get_sources) - monkeypatch.setattr(money, "get_ud_value", patched_get_ud_value) + monkeypatch.setattr(m_tools, "get_sources", patched_get_sources) + monkeypatch.setattr(m_tools, "get_ud_value", patched_get_ud_value) # reset Counter.counter Counter.counter = 0