From 6f8d5211df3665f510f639c0ce5654ac9750ab81 Mon Sep 17 00:00:00 2001 From: Artem Saprykin Date: Sun, 26 May 2019 15:28:17 +0300 Subject: [PATCH 1/6] Transfer tokens from unfrozen to operational balance --- README.md | 15 ++++++++ cli/node_account/cli.py | 27 ++++++++++++++ cli/node_account/interfaces.py | 6 ++++ cli/node_account/service.py | 16 +++++++++ tests/conftest.py | 22 ++++++++++++ ...fer_tokens_from_unfrozen_to_operational.py | 35 +++++++++++++++++++ 6 files changed, 121 insertions(+) create mode 100644 tests/node_account/test_transfer_tokens_from_unfrozen_to_operational.py diff --git a/README.md b/README.md index df3cec1..d95f0a6 100644 --- a/README.md +++ b/README.md @@ -268,6 +268,21 @@ $ remme node-account transfer-tokens-from-frozen-to-unfrozen } ``` +Transfer available tokens from unfrozen reputational balance to operational balance (executable only on the machine which runs the node) — `remme node-account transfer-tokens-from-unfrozen-to-operational`. + +| Arguments | Type | Required | Description | +| :---------: | :-----: | :------: | ---------------------------------------------- | +| amount | Integer | Yes | Amount to transfer. | + +```bash +$ remme node-account transfer-tokens-from-unfrozen-to-operational +{ + "result": { + "batch_id": "045c2b7c43a7ca7c3dc60e92714c03265572a726d1fae631c39a404eaf97770e3f6a7a8c35c86f6361afb2e4f12b4a17d71a66a19158b62f30531ab32b62f06f" + } +} +``` + ### Block Get a list of blocks — ``remme block get-list``: diff --git a/cli/node_account/cli.py b/cli/node_account/cli.py index 56bee4a..ac35ab3 100644 --- a/cli/node_account/cli.py +++ b/cli/node_account/cli.py @@ -132,3 +132,30 @@ def transfer_tokens_from_frozen_to_unfrozen(): sys.exit(FAILED_EXIT_FROM_COMMAND_CODE) print_result(result=result) + + +@node_account_commands.command('transfer-tokens-from-unfrozen-to-operational') +@click.option('--amount', type=int, required=True, help=AMOUNT_ARGUMENT_HELP_MESSAGE) +def transfer_tokens_from_unfrozen_to_operational(amount): + """ + Transfer available tokens from unfrozen reputational balance to operational balance. + """ + try: + node_private_key = NodePrivateKey().get() + + except (NotSupportedOsToGetNodePrivateKeyError, FileNotFoundError) as error: + print_errors(errors=str(error)) + sys.exit(FAILED_EXIT_FROM_COMMAND_CODE) + + remme = Remme( + account_config={'private_key_hex': node_private_key, 'account_type': AccountType.NODE}, + network_config={'node_address': 'localhost' + ':8080'}, + ) + + result, errors = NodeAccount(service=remme).transfer_tokens_from_unfrozen_to_operational(amount=amount) + + if errors is not None: + print_errors(errors=errors) + sys.exit(FAILED_EXIT_FROM_COMMAND_CODE) + + print_result(result=result) diff --git a/cli/node_account/interfaces.py b/cli/node_account/interfaces.py index c2d6bea..316dd5b 100644 --- a/cli/node_account/interfaces.py +++ b/cli/node_account/interfaces.py @@ -28,3 +28,9 @@ def transfer_tokens_from_frozen_to_unfrozen(self): Transfer available tokens from frozen to unfrozen reputation's balances. """ pass + + def transfer_tokens_from_unfrozen_to_operational(self, amount): + """ + Transfer available tokens from unfrozen to operational balance. + """ + pass diff --git a/cli/node_account/service.py b/cli/node_account/service.py index 391f6db..04bc91e 100644 --- a/cli/node_account/service.py +++ b/cli/node_account/service.py @@ -75,3 +75,19 @@ def transfer_tokens_from_frozen_to_unfrozen(self): return { 'batch_identifier': transfer_transaction.batch_id, }, None + + def transfer_tokens_from_unfrozen_to_operational(self, amount): + """ + Transfer available tokens from unfrozen reputational balance to operational balance. + """ + try: + transfer_transaction = loop.run_until_complete( + self.service.token.transfer_from_unfrozen_to_operational(amount=amount), + ) + + except Exception as error: + return None, str(error) + + return { + 'batch_identifier': transfer_transaction.batch_id, + }, None diff --git a/tests/conftest.py b/tests/conftest.py index ff042a9..c028bd1 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -300,6 +300,20 @@ def batch_id(self): '08f5308af03fd4aa18ff1d868f043b12dd7b0a792e141f000a2505acd4b7a956' +class UnfrozenToOperationalTransaction: + """ + Impose unfrozen to opetational balance transaction's data transfer object. + """ + + @property + def batch_id(self): + """ + Get batch identifier of the transaction. + """ + return '37809770b004dcbc7dae116fd9f17428255ddddee3304c9b3d14609d2792e78f' \ + '08f5308af03fd4aa18ff1d868f043b12dd7b0a792e141f000a2505acd4b7a956' + + @pytest.fixture() def sent_transaction(): """ @@ -378,3 +392,11 @@ def transaction(): Get the transaction fixture. """ return Transaction() + + +@pytest.fixture() +def unfrozen_to_operational_transaction(): + """ + Get the transaction fixture. + """ + return UnfrozenToOperationalTransaction() diff --git a/tests/node_account/test_transfer_tokens_from_unfrozen_to_operational.py b/tests/node_account/test_transfer_tokens_from_unfrozen_to_operational.py new file mode 100644 index 0000000..dc00b9d --- /dev/null +++ b/tests/node_account/test_transfer_tokens_from_unfrozen_to_operational.py @@ -0,0 +1,35 @@ +""" +Provide tests for command line interface's node account transfer tokens from unfrozen to operational balance. +""" +import json + +from click.testing import CliRunner + +from cli.constants import PASSED_EXIT_FROM_COMMAND_CODE +from cli.entrypoint import cli + + +def test_transfer_tokens_from_unfrozen_to_operational(mocker, unfrozen_to_operational_transaction): + """ + Case: transfer available tokens from unfrozen to operational. + Expect: transaction's batch identifier is returned. + """ + mock_get_node_private_key = mocker.patch('cli.config.NodePrivateKey.get') + mock_get_node_private_key.return_value = '42dada12f863528bd456785d8c544154db6ec9455be2c123d91b687df3697314' + + mock_node_account_transfer_tokens_from_unfrozen_to_operational = \ + mocker.patch('cli.node_account.service.loop.run_until_complete') + mock_node_account_transfer_tokens_from_unfrozen_to_operational.return_value = unfrozen_to_operational_transaction + + runner = CliRunner() + result = runner.invoke(cli, [ + 'node-account', + 'transfer-tokens-from-unfrozen-to-operational', + '--amount', + 1000 + ]) + + transaction_batch_identifier = json.loads(result.output).get('result').get('batch_identifier') + + assert PASSED_EXIT_FROM_COMMAND_CODE == result.exit_code + assert unfrozen_to_operational_transaction.batch_id == transaction_batch_identifier From 4c24e3d47da78c2077cba7e39769f66c9b54f4f5 Mon Sep 17 00:00:00 2001 From: Artem Saprykin Date: Sun, 26 May 2019 15:43:49 +0300 Subject: [PATCH 2/6] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d95f0a6..529cac8 100644 --- a/README.md +++ b/README.md @@ -268,7 +268,7 @@ $ remme node-account transfer-tokens-from-frozen-to-unfrozen } ``` -Transfer available tokens from unfrozen reputational balance to operational balance (executable only on the machine which runs the node) — `remme node-account transfer-tokens-from-unfrozen-to-operational`. +Transfer available tokens from unfrozen reputational balance to operational balance (executable only on the machine which runs the node) — `remme node-account transfer-tokens-from-unfrozen-to-operational --amount=1000`. | Arguments | Type | Required | Description | | :---------: | :-----: | :------: | ---------------------------------------------- | From 8e36b2a5e24c7232085f97bc6699774737082aed Mon Sep 17 00:00:00 2001 From: Artem Saprykin Date: Sun, 26 May 2019 15:46:02 +0300 Subject: [PATCH 3/6] flake8 code update --- .../test_transfer_tokens_from_unfrozen_to_operational.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/node_account/test_transfer_tokens_from_unfrozen_to_operational.py b/tests/node_account/test_transfer_tokens_from_unfrozen_to_operational.py index dc00b9d..6e1510b 100644 --- a/tests/node_account/test_transfer_tokens_from_unfrozen_to_operational.py +++ b/tests/node_account/test_transfer_tokens_from_unfrozen_to_operational.py @@ -26,7 +26,7 @@ def test_transfer_tokens_from_unfrozen_to_operational(mocker, unfrozen_to_operat 'node-account', 'transfer-tokens-from-unfrozen-to-operational', '--amount', - 1000 + 1000, ]) transaction_batch_identifier = json.loads(result.output).get('result').get('batch_identifier') From 9aa1eb5b703b2f9a55fb6baca282d189a5b025c8 Mon Sep 17 00:00:00 2001 From: "DESKTOP-CBVF60D\\Artem" Date: Mon, 27 May 2019 12:47:44 +0300 Subject: [PATCH 4/6] review summary --- README.md | 10 ++++----- cli/node_account/cli.py | 2 +- cli/node_account/interfaces.py | 2 +- cli/node_account/service.py | 2 +- tests/conftest.py | 22 ------------------- ...fer_tokens_from_unfrozen_to_operational.py | 8 +++---- 6 files changed, 12 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 529cac8..ae1ecb7 100644 --- a/README.md +++ b/README.md @@ -268,14 +268,14 @@ $ remme node-account transfer-tokens-from-frozen-to-unfrozen } ``` -Transfer available tokens from unfrozen reputational balance to operational balance (executable only on the machine which runs the node) — `remme node-account transfer-tokens-from-unfrozen-to-operational --amount=1000`. +Transfer tokens from unfrozen reputational balance to operational balance (executable only on the machine which runs the node) — `remme node-account transfer-tokens-from-unfrozen-to-operational`. -| Arguments | Type | Required | Description | -| :---------: | :-----: | :------: | ---------------------------------------------- | -| amount | Integer | Yes | Amount to transfer. | +| Arguments | Type | Required | Description | +| :------: | :-----: | :------: | -------------------- | +| amount | Integer | Yes | Amount to transfer. | ```bash -$ remme node-account transfer-tokens-from-unfrozen-to-operational +$ remme node-account transfer-tokens-from-unfrozen-to-operational --amount=1000 { "result": { "batch_id": "045c2b7c43a7ca7c3dc60e92714c03265572a726d1fae631c39a404eaf97770e3f6a7a8c35c86f6361afb2e4f12b4a17d71a66a19158b62f30531ab32b62f06f" diff --git a/cli/node_account/cli.py b/cli/node_account/cli.py index ac35ab3..7da4518 100644 --- a/cli/node_account/cli.py +++ b/cli/node_account/cli.py @@ -138,7 +138,7 @@ def transfer_tokens_from_frozen_to_unfrozen(): @click.option('--amount', type=int, required=True, help=AMOUNT_ARGUMENT_HELP_MESSAGE) def transfer_tokens_from_unfrozen_to_operational(amount): """ - Transfer available tokens from unfrozen reputational balance to operational balance. + Transfer tokens from unfrozen reputational balance to operational balance. """ try: node_private_key = NodePrivateKey().get() diff --git a/cli/node_account/interfaces.py b/cli/node_account/interfaces.py index 316dd5b..6229ef7 100644 --- a/cli/node_account/interfaces.py +++ b/cli/node_account/interfaces.py @@ -31,6 +31,6 @@ def transfer_tokens_from_frozen_to_unfrozen(self): def transfer_tokens_from_unfrozen_to_operational(self, amount): """ - Transfer available tokens from unfrozen to operational balance. + Transfer tokens from unfrozen to operational balance. """ pass diff --git a/cli/node_account/service.py b/cli/node_account/service.py index 04bc91e..0b191b1 100644 --- a/cli/node_account/service.py +++ b/cli/node_account/service.py @@ -78,7 +78,7 @@ def transfer_tokens_from_frozen_to_unfrozen(self): def transfer_tokens_from_unfrozen_to_operational(self, amount): """ - Transfer available tokens from unfrozen reputational balance to operational balance. + Transfer tokens from unfrozen reputational balance to operational balance. """ try: transfer_transaction = loop.run_until_complete( diff --git a/tests/conftest.py b/tests/conftest.py index c028bd1..ff042a9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -300,20 +300,6 @@ def batch_id(self): '08f5308af03fd4aa18ff1d868f043b12dd7b0a792e141f000a2505acd4b7a956' -class UnfrozenToOperationalTransaction: - """ - Impose unfrozen to opetational balance transaction's data transfer object. - """ - - @property - def batch_id(self): - """ - Get batch identifier of the transaction. - """ - return '37809770b004dcbc7dae116fd9f17428255ddddee3304c9b3d14609d2792e78f' \ - '08f5308af03fd4aa18ff1d868f043b12dd7b0a792e141f000a2505acd4b7a956' - - @pytest.fixture() def sent_transaction(): """ @@ -392,11 +378,3 @@ def transaction(): Get the transaction fixture. """ return Transaction() - - -@pytest.fixture() -def unfrozen_to_operational_transaction(): - """ - Get the transaction fixture. - """ - return UnfrozenToOperationalTransaction() diff --git a/tests/node_account/test_transfer_tokens_from_unfrozen_to_operational.py b/tests/node_account/test_transfer_tokens_from_unfrozen_to_operational.py index 6e1510b..0c80993 100644 --- a/tests/node_account/test_transfer_tokens_from_unfrozen_to_operational.py +++ b/tests/node_account/test_transfer_tokens_from_unfrozen_to_operational.py @@ -9,9 +9,9 @@ from cli.entrypoint import cli -def test_transfer_tokens_from_unfrozen_to_operational(mocker, unfrozen_to_operational_transaction): +def test_transfer_tokens_from_unfrozen_to_operational(mocker, transaction): """ - Case: transfer available tokens from unfrozen to operational. + Case: transfer tokens from unfrozen to operational. Expect: transaction's batch identifier is returned. """ mock_get_node_private_key = mocker.patch('cli.config.NodePrivateKey.get') @@ -19,7 +19,7 @@ def test_transfer_tokens_from_unfrozen_to_operational(mocker, unfrozen_to_operat mock_node_account_transfer_tokens_from_unfrozen_to_operational = \ mocker.patch('cli.node_account.service.loop.run_until_complete') - mock_node_account_transfer_tokens_from_unfrozen_to_operational.return_value = unfrozen_to_operational_transaction + mock_node_account_transfer_tokens_from_unfrozen_to_operational.return_value = transaction runner = CliRunner() result = runner.invoke(cli, [ @@ -32,4 +32,4 @@ def test_transfer_tokens_from_unfrozen_to_operational(mocker, unfrozen_to_operat transaction_batch_identifier = json.loads(result.output).get('result').get('batch_identifier') assert PASSED_EXIT_FROM_COMMAND_CODE == result.exit_code - assert unfrozen_to_operational_transaction.batch_id == transaction_batch_identifier + assert transaction.batch_id == transaction_batch_identifier From 4742adb7ae725d64838fc1244ae576600035cf18 Mon Sep 17 00:00:00 2001 From: Artem Saprykin Date: Mon, 27 May 2019 14:08:57 +0300 Subject: [PATCH 5/6] a transfer from unfrozen to operational form added --- cli/node_account/cli.py | 13 +++- cli/node_account/forms.py | 20 +++++- ...fer_tokens_from_unfrozen_to_operational.py | 69 ++++++++++++++++++- 3 files changed, 98 insertions(+), 4 deletions(-) diff --git a/cli/node_account/cli.py b/cli/node_account/cli.py index 7da4518..a970ccb 100644 --- a/cli/node_account/cli.py +++ b/cli/node_account/cli.py @@ -14,7 +14,10 @@ ) from cli.errors import NotSupportedOsToGetNodePrivateKeyError from cli.generic.forms.forms import TransferTokensForm -from cli.node_account.forms import GetNodeAccountInformationForm +from cli.node_account.forms import ( + GetNodeAccountInformationForm, + TransferTokensFromUnfrozenToOperationalForm, +) from cli.node_account.help import ( ACCOUNT_ADDRESS_TO_ARGUMENT_HELP_MESSAGE, AMOUNT_ARGUMENT_HELP_MESSAGE, @@ -140,6 +143,14 @@ def transfer_tokens_from_unfrozen_to_operational(amount): """ Transfer tokens from unfrozen reputational balance to operational balance. """ + arguments, errors = TransferTokensFromUnfrozenToOperationalForm().load({ + 'amount': amount, + }) + + if errors: + print_errors(errors=errors) + sys.exit(FAILED_EXIT_FROM_COMMAND_CODE) + try: node_private_key = NodePrivateKey().get() diff --git a/cli/node_account/forms.py b/cli/node_account/forms.py index 4643660..6d38000 100644 --- a/cli/node_account/forms.py +++ b/cli/node_account/forms.py @@ -1,7 +1,11 @@ """ Provide forms for command line interface's node account commands. """ -from marshmallow import Schema +from marshmallow import ( + Schema, + fields, + validate, +) from cli.generic.forms.fields import ( AccountAddressField, @@ -16,3 +20,17 @@ class GetNodeAccountInformationForm(Schema): address = AccountAddressField(required=True) node_url = NodeUrlField(required=True) + + +class TransferTokensFromUnfrozenToOperationalForm(Schema): + """ + A transfer of tokens from unfrozen reputational balance to operational balance form. + """ + + amount = fields.Integer( + strict=True, + required=True, + validate=[ + validate.Range(min=1, error='Amount must be greater than 0.'), + ], + ) diff --git a/tests/node_account/test_transfer_tokens_from_unfrozen_to_operational.py b/tests/node_account/test_transfer_tokens_from_unfrozen_to_operational.py index 0c80993..9d5231b 100644 --- a/tests/node_account/test_transfer_tokens_from_unfrozen_to_operational.py +++ b/tests/node_account/test_transfer_tokens_from_unfrozen_to_operational.py @@ -3,15 +3,21 @@ """ import json +import pytest from click.testing import CliRunner -from cli.constants import PASSED_EXIT_FROM_COMMAND_CODE +from cli.constants import ( + FAILED_EXIT_FROM_COMMAND_CODE, + INCORRECT_ENTERED_COMMAND_CODE, + PASSED_EXIT_FROM_COMMAND_CODE, +) from cli.entrypoint import cli +from cli.utils import dict_to_pretty_json def test_transfer_tokens_from_unfrozen_to_operational(mocker, transaction): """ - Case: transfer tokens from unfrozen to operational. + Case: transfer tokens from unfrozen reputational balance to operational balance. Expect: transaction's batch identifier is returned. """ mock_get_node_private_key = mocker.patch('cli.config.NodePrivateKey.get') @@ -33,3 +39,62 @@ def test_transfer_tokens_from_unfrozen_to_operational(mocker, transaction): assert PASSED_EXIT_FROM_COMMAND_CODE == result.exit_code assert transaction.batch_id == transaction_batch_identifier + + +def test_transfer_tokens_from_unfrozen_to_operational_invalid_amount(mocker, transaction): + """ + Case: transfer tokens from unfrozen reputational balance to operational balance with invalid amount. + Expect: amount is not a valid integer error message. + """ + invalid_amount = 'je682' + + mock_get_node_private_key = mocker.patch('cli.config.NodePrivateKey.get') + mock_get_node_private_key.return_value = '42dada12f863528bd456785d8c544154db6ec9455be2c123d91b687df3697314' + + mock_node_account_transfer_tokens_from_unfrozen_to_operational = \ + mocker.patch('cli.node_account.service.loop.run_until_complete') + mock_node_account_transfer_tokens_from_unfrozen_to_operational.return_value = transaction + + runner = CliRunner() + result = runner.invoke(cli, [ + 'node-account', + 'transfer-tokens-from-unfrozen-to-operational', + '--amount', + invalid_amount, + ]) + + assert INCORRECT_ENTERED_COMMAND_CODE == result.exit_code + assert f'{invalid_amount} is not a valid integer' in result.output + + +@pytest.mark.parametrize('insufficient_amount', [-1, 0]) +def test_transfer_tokens_from_unfrozen_to_operational_insufficient_amount(mocker, transaction, insufficient_amount): + """ + Case: transfer tokens from unfrozen reputational balance to operational balance with insufficient amount. + Expect: amount must be greater than 0 error message. + """ + mock_get_node_private_key = mocker.patch('cli.config.NodePrivateKey.get') + mock_get_node_private_key.return_value = '42dada12f863528bd456785d8c544154db6ec9455be2c123d91b687df3697314' + + mock_node_account_transfer_tokens_from_unfrozen_to_operational = \ + mocker.patch('cli.node_account.service.loop.run_until_complete') + mock_node_account_transfer_tokens_from_unfrozen_to_operational.return_value = transaction + + runner = CliRunner() + result = runner.invoke(cli, [ + 'node-account', + 'transfer-tokens-from-unfrozen-to-operational', + '--amount', + insufficient_amount, + ]) + + expected_error = { + 'errors': { + 'amount': [ + f'Amount must be greater than 0.', + ], + }, + } + + assert FAILED_EXIT_FROM_COMMAND_CODE == result.exit_code + assert dict_to_pretty_json(expected_error) in result.output From 7dfa36eb5435c4c34d0a903493ed9e6c09f88785 Mon Sep 17 00:00:00 2001 From: Artem Saprykin Date: Mon, 27 May 2019 14:13:23 +0300 Subject: [PATCH 6/6] batch_identifier field --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ae1ecb7..a6d3365 100644 --- a/README.md +++ b/README.md @@ -263,7 +263,7 @@ Transfer available tokens from frozen to unfrozen reputation's balances (executa $ remme node-account transfer-tokens-from-frozen-to-unfrozen { "result": { - "batch_id": "045c2b7c43a7ca7c3dc60e92714c03265572a726d1fae631c39a404eaf97770e3f6a7a8c35c86f6361afb2e4f12b4a17d71a66a19158b62f30531ab32b62f06f" + "batch_identifier": "045c2b7c43a7ca7c3dc60e92714c03265572a726d1fae631c39a404eaf97770e3f6a7a8c35c86f6361afb2e4f12b4a17d71a66a19158b62f30531ab32b62f06f" } } ```