From db91d28d3589bee3e7df51d6e36c8df23ebe1933 Mon Sep 17 00:00:00 2001 From: badrogger Date: Sun, 8 Dec 2019 20:37:05 +0200 Subject: [PATCH] SKALE-1841 Add wrappers for basic commands in skale.py --- skale/contracts/__init__.py | 2 +- skale/contracts/base_contract.py | 22 ++++++++++++++++ skale/contracts/constants.py | 5 +++- skale/contracts/manager.py | 17 +++++++++++- skale/contracts/token.py | 4 ++- skale/utils/web3_utils.py | 4 +++ tests/contracts/manager_test.py | 44 +++++++++++++++++++++++++++++++- 7 files changed, 93 insertions(+), 5 deletions(-) diff --git a/skale/contracts/__init__.py b/skale/contracts/__init__.py index aa5d2f87..e064dd7a 100644 --- a/skale/contracts/__init__.py +++ b/skale/contracts/__init__.py @@ -1,6 +1,6 @@ # flake8: noqa -from skale.contracts.base_contract import BaseContract +from skale.contracts.base_contract import BaseContract, transaction_method from skale.contracts.manager import Manager from skale.contracts.contract_manager import ContractManager diff --git a/skale/contracts/base_contract.py b/skale/contracts/base_contract.py index ca47e45c..3b04b391 100644 --- a/skale/contracts/base_contract.py +++ b/skale/contracts/base_contract.py @@ -18,8 +18,30 @@ # along with SKALE.py. If not, see . """ SKALE base contract class """ +from functools import wraps + from web3 import Web3 +from skale.utils.web3_utils import TransactionFailedError, wait_receipt + + +def transaction_method(transaction): + @wraps(transaction_method) + def wrapper(self, *args, wait_for=False, **kwargs): + res = transaction(self, *args, **kwargs) + if wait_for: + receipt = wait_receipt(self.skale.web3, res['tx']) + if receipt.get('status') == 1: + return receipt + else: + raise TransactionFailedError( + 'Transaction {transaction_method.__name__} failed with ' + 'receipt {receipt}' + ) + else: + return res + return wrapper + class BaseContract: def __init__(self, skale, name, address, abi): diff --git a/skale/contracts/constants.py b/skale/contracts/constants.py index d7eeeede..acb59b4e 100644 --- a/skale/contracts/constants.py +++ b/skale/contracts/constants.py @@ -17,12 +17,13 @@ # You should have received a copy of the GNU Affero General Public License # along with SKALE.py. If not, see . -from skale.contracts import BaseContract +from skale.contracts import BaseContract, transaction_method from skale.transactions.tools import post_transaction from skale.utils.constants import GAS class Constants(BaseContract): + @transaction_method def set_periods(self, new_reward_period, new_delta_period): op = self.contract.functions.setPeriods(new_reward_period, new_delta_period) tx = post_transaction(self.skale.wallet, op, GAS['set_periods']) @@ -34,6 +35,7 @@ def get_reward_period(self): def get_delta_period(self): return self.contract.functions.deltaPeriod().call() + @transaction_method def set_check_time(self, new_check_time): op = self.contract.functions.setCheckTime(new_check_time) tx = post_transaction(self.skale.wallet, op, GAS['set_check_time']) @@ -42,6 +44,7 @@ def set_check_time(self, new_check_time): def get_check_time(self): return self.contract.functions.checkTime().call() + @transaction_method def set_latency(self, new_allowable_latency): op = self.contract.functions.setLatency(new_allowable_latency) tx = post_transaction(self.skale.wallet, op, GAS['set_latency']) diff --git a/skale/contracts/manager.py b/skale/contracts/manager.py index c81aa1ed..e9da1d8e 100644 --- a/skale/contracts/manager.py +++ b/skale/contracts/manager.py @@ -23,7 +23,7 @@ import socket -from skale.contracts import BaseContract +from skale.contracts import BaseContract, transaction_method from skale.transactions.tools import post_transaction from skale.utils import helper from skale.utils.constants import GAS, NODE_DEPOSIT, OP_TYPES @@ -32,6 +32,7 @@ class Manager(BaseContract): + @transaction_method def create_node(self, ip, port, name, public_ip=None): logger.info( f'create_node: {ip}:{port}, public ip: {public_ip} name: {name}') @@ -77,6 +78,14 @@ def create_node_data_to_bytes(self, ip, public_ip, port, name, pk, nonce): return data_bytes + def create_default_schain(self, name): + lifetime = 3600 + nodes_type = 4 + price_in_wei = self.skale.schains.get_schain_price(nodes_type, lifetime) + return self.create_schain(lifetime, nodes_type, price_in_wei, name, + wait_for=True) + + @transaction_method def create_schain(self, lifetime, type_of_nodes, deposit, name): logger.info( f'create_schain: type_of_nodes: {type_of_nodes}, name: {name}') @@ -108,33 +117,39 @@ def create_schain_data_to_bytes(self, lifetime, type_of_nodes, name, ) return data_bytes + @transaction_method def get_bounty(self, node_id): op = self.contract.functions.getBounty(node_id) tx = post_transaction(self.skale.wallet, op, GAS['get_bounty']) return {'tx': tx} + @transaction_method def send_verdict(self, validator, node_id, downtime, latency): op = self.contract.functions.sendVerdict(validator, node_id, downtime, latency) tx = post_transaction(self.skale.wallet, op, GAS['send_verdict']) return {'tx': tx} + @transaction_method def send_verdicts(self, validator, nodes_ids, downtimes, latencies): op = self.contract.functions.sendVerdicts(validator, nodes_ids, downtimes, latencies) tx = post_transaction(self.skale.wallet, op, GAS['send_verdicts']) return {'tx': tx} + @transaction_method def deregister(self, node_id): op = self.contract.functions.deleteNode(node_id) tx = post_transaction(self.skale.wallet, op, GAS['delete_node']) return {'tx': tx} + @transaction_method def delete_schain(self, schain_name): op = self.contract.functions.deleteSchain(schain_name) tx = post_transaction(self.skale.wallet, op, GAS['delete_schain']) return {'tx': tx} + @transaction_method def delete_node_by_root(self, node_id): op = self.contract.functions.deleteNodeByRoot(node_id) tx = post_transaction(self.skale.wallet, op, GAS['delete_node_by_root']) diff --git a/skale/contracts/token.py b/skale/contracts/token.py index c626dd89..d5b21e39 100644 --- a/skale/contracts/token.py +++ b/skale/contracts/token.py @@ -18,12 +18,13 @@ # along with SKALE.py. If not, see . """ SKALE token operations """ -from skale.contracts import BaseContract +from skale.contracts import BaseContract, transaction_method from skale.transactions.tools import post_transaction from skale.utils.constants import GAS class Token(BaseContract): + @transaction_method def transfer(self, address, value): op = self.contract.functions.send(address, value, b'') tx = post_transaction(self.skale.wallet, op, GAS['token_transfer']) @@ -32,6 +33,7 @@ def transfer(self, address, value): def get_balance(self, address): return self.contract.functions.balanceOf(address).call() + @transaction_method def add_authorized(self, address, wallet): # pragma: no cover op = self.contract.functions.addAuthorized(address) tx = post_transaction(self.skale.wallet, op, GAS['token_transfer']) diff --git a/skale/utils/web3_utils.py b/skale/utils/web3_utils.py index 646fb32d..15193e81 100644 --- a/skale/utils/web3_utils.py +++ b/skale/utils/web3_utils.py @@ -29,6 +29,10 @@ logger = logging.getLogger(__name__) +class TransactionFailedError(Exception): + pass + + def get_provider(endpoint): scheme = urlparse(endpoint).scheme if scheme == 'ws' or scheme == 'wss': diff --git a/tests/contracts/manager_test.py b/tests/contracts/manager_test.py index 74e4c273..cd8d88e4 100644 --- a/tests/contracts/manager_test.py +++ b/tests/contracts/manager_test.py @@ -1,12 +1,14 @@ """ SKALE manager test """ import mock +import pytest import web3 from hexbytes import HexBytes import skale.utils.helper as helper from skale.utils.constants import GAS -from skale.utils.web3_utils import wait_receipt, private_key_to_public +from skale.utils.web3_utils import (wait_receipt, private_key_to_public, + TransactionFailedError) from tests.constants import DEFAULT_NODE_NAME, SECOND_NODE_NAME from tests.utils import generate_random_node_data, generate_random_schain_data @@ -232,3 +234,43 @@ def test_create_delete_schain(skale): for sid in schains_ids_after ] assert name not in schains_names + + +def test_create_delete_default_schain(skale): + schains_ids = skale.schains_data.get_all_schains_ids() + name = 'default-schain' + res = skale.manager.create_default_schain(name) + assert res['status'] == 1 + + schains_ids_number_after = skale.schains_data.get_schains_number() + assert schains_ids_number_after == len(schains_ids) + 1 + schains_ids_after = skale.schains_data.get_all_schains_ids() + + schains_names = [ + skale.schains_data.get(sid)['name'] + for sid in schains_ids_after + ] + assert name in schains_names + + res = skale.manager.delete_schain(name, wait_for=True) + assert res['status'] == 1 + + schains_ids_number_after = skale.schains_data.get_schains_number() + assert schains_ids_number_after == len(schains_ids) + schains_ids_after = skale.schains_data.get_all_schains_ids() + + schains_names = [ + skale.schains_data.get(sid)['name'] + for sid in schains_ids_after + ] + assert name not in schains_names + + +def test_create_node_status_0(skale): + ip, public_ip, port, name = generate_random_node_data() + with mock.patch.object(web3.eth.Eth, 'sendRawTransaction') as send_tx_mock: + send_tx_mock.return_value = b'hexstring' + with mock.patch('skale.contracts.base_contract.wait_receipt', + return_value={'status': 0}): + with pytest.raises(TransactionFailedError): + skale.manager.create_node(ip, port, name, wait_for=True)