diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d999019b..2ecf938b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -30,7 +30,7 @@ jobs: - name: Launch hardhat node working-directory: hardhat-node run: | - docker-compose up -d && sleep 20 + docker compose up -d && sleep 20 - name: Deploy manager run: | @@ -85,7 +85,7 @@ jobs: - name: Launch hardhat node working-directory: hardhat-node run: | - docker-compose up -d && sleep 20 + docker compose up -d && sleep 20 - name: Deploy manager contracts run: | diff --git a/scripts/calculate_version.sh b/scripts/calculate_version.sh index ad56af5a..eef5cb16 100644 --- a/scripts/calculate_version.sh +++ b/scripts/calculate_version.sh @@ -16,7 +16,7 @@ if [ -z $VERSION ]; then fi -if [[ $BRANCH == 'master' ]]; then +if [[ $BRANCH == 'stable' ]]; then echo $VERSION exit 0 elif [[ $BRANCH == 'develop' ]]; then diff --git a/setup.py b/setup.py index 7892f26e..36a203a5 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,7 @@ setup( name='skale.py', - version='6.2', + version='6.3', description='SKALE client tools', long_description_markdown_filename='README.md', author='SKALE Labs', @@ -43,10 +43,10 @@ install_requires=[ "asyncio==3.4.3", "pyyaml==6.0", - "sgx.py==0.9dev2", - "redis==4.4.4", + "sgx.py==0.9dev3", + "redis==5.0.3", "typing-extensions==4.9.0", - "web3==6.13.0" + "web3==6.20.2" ], python_requires='>=3.7,<4', diff --git a/skale/contracts/manager/schains.py b/skale/contracts/manager/schains.py index e12d18cd..547992ec 100644 --- a/skale/contracts/manager/schains.py +++ b/skale/contracts/manager/schains.py @@ -34,7 +34,7 @@ FIELDS = [ 'name', 'mainnetOwner', 'indexInOwnerList', 'partOfNode', 'lifetime', 'startDate', 'startBlock', 'deposit', 'index', 'generation', 'originator', 'chainId', 'multitransactionMode', - 'thresholdEncryption' + 'thresholdEncryption', 'allocationType' ] diff --git a/skale/dataclasses/schain_options.py b/skale/dataclasses/schain_options.py index 32d74357..d3b1ca8b 100644 --- a/skale/dataclasses/schain_options.py +++ b/skale/dataclasses/schain_options.py @@ -17,38 +17,65 @@ # You should have received a copy of the GNU Affero General Public License # along with SKALE.py. If not, see . +from __future__ import annotations from dataclasses import dataclass +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from skale.types.schain import SchainOption +from enum import Enum + + +class AllocationType(int, Enum): + DEFAULT = 0 + NO_FILESTORAGE = 1 + MAX_CONTRACT_STORAGE = 2 + MAX_CONSENSUS_DB = 3 + MAX_FILESTORAGE = 4 @dataclass class SchainOptions: multitransaction_mode: bool threshold_encryption: bool + allocation_type: AllocationType - def to_tuples(self) -> list: + def to_tuples(self) -> list[SchainOption]: return [ ('multitr', bool_to_bytes(self.multitransaction_mode)), - ('encrypt', bool_to_bytes(self.threshold_encryption)) + ('encrypt', bool_to_bytes(self.threshold_encryption)), + ('alloc', int_to_bytes(self.allocation_type.value)) ] -def parse_schain_options(raw_options: list) -> SchainOptions: +def parse_schain_options(raw_options: list[SchainOption]) -> SchainOptions: """ Parses raw sChain options from smart contracts (list of tuples). Returns default values if nothing is set on contracts. """ - if len(raw_options) == 0: - return get_default_schain_options() + + multitransaction_mode = False + threshold_encryption = False + allocation_type = AllocationType.DEFAULT + if len(raw_options) > 0: + multitransaction_mode = bytes_to_bool(raw_options[0][1]) + if len(raw_options) > 1: + threshold_encryption = bytes_to_bool(raw_options[1][1]) + if len(raw_options) > 2: + allocation_type = AllocationType(bytes_to_int(raw_options[2][1])) + return SchainOptions( - multitransaction_mode=bytes_to_bool(raw_options[0][1]), - threshold_encryption=bytes_to_bool(raw_options[1][1]) + multitransaction_mode=multitransaction_mode, + threshold_encryption=threshold_encryption, + allocation_type=allocation_type ) def get_default_schain_options() -> SchainOptions: return SchainOptions( multitransaction_mode=False, - threshold_encryption=False + threshold_encryption=False, + allocation_type=AllocationType.DEFAULT ) @@ -56,5 +83,13 @@ def bool_to_bytes(bool_value: bool) -> bytes: return bool_value.to_bytes(1, byteorder='big') +def int_to_bytes(int_value: int) -> bytes: + return int.to_bytes(int_value, length=1, byteorder='big') + + +def bytes_to_int(bytes_value: bytes) -> int: + return int.from_bytes(bytes_value, byteorder='big') + + def bytes_to_bool(bytes_value: bytes) -> bool: return bool(int.from_bytes(bytes_value, 'big')) diff --git a/tests/manager/schains_internal_test.py b/tests/manager/schains_internal_test.py index 1abc952d..32f32f8b 100644 --- a/tests/manager/schains_internal_test.py +++ b/tests/manager/schains_internal_test.py @@ -10,7 +10,7 @@ def test_get_raw(skale): schain_arr = skale.schains_internal.get_raw(DEFAULT_SCHAIN_ID) - assert len(FIELDS) == len(schain_arr) + 3 # +1 for chainId + options + assert len(FIELDS) == len(schain_arr) + 4 # +1 for chainId + options def test_get_raw_not_exist(skale): diff --git a/tests/manager/schains_test.py b/tests/manager/schains_test.py index 4b2dd78a..20511bee 100644 --- a/tests/manager/schains_test.py +++ b/tests/manager/schains_test.py @@ -1,16 +1,27 @@ -""" SKALE chain test """ +"""SKALE chain test""" from hexbytes import HexBytes +import pytest + from skale.contracts.manager.schains import FIELDS, SchainStructure -from skale.dataclasses.schain_options import SchainOptions -from skale.utils.contracts_provision.fake_multisig_contract import FAKE_MULTISIG_DATA_PATH -from skale.utils.contracts_provision.main import generate_random_schain_data, create_schain +from skale.dataclasses.schain_options import AllocationType, SchainOptions +from skale.utils.contracts_provision.fake_multisig_contract import ( + FAKE_MULTISIG_DATA_PATH, +) +from skale.utils.contracts_provision.main import ( + generate_random_schain_data, + create_schain, +) from skale.utils.helper import get_abi from skale.wallets.web3_wallet import generate_wallet -from tests.constants import (DEFAULT_NODE_NAME, DEFAULT_SCHAIN_ID, - DEFAULT_SCHAIN_NAME, LIFETIME_SECONDS) +from tests.constants import ( + DEFAULT_NODE_NAME, + DEFAULT_SCHAIN_ID, + DEFAULT_SCHAIN_NAME, + LIFETIME_SECONDS, +) def test_get(skale): @@ -27,7 +38,7 @@ def test_get_object(skale): def test_get_by_name(skale): schain = skale.schains.get(DEFAULT_SCHAIN_ID) - schain_name = schain['name'] + schain_name = schain["name"] schain_by_name = skale.schains.get_by_name(schain_name) assert list(schain_by_name.keys()) == FIELDS @@ -47,9 +58,7 @@ def test_get_schains_for_owner(skale, schain, empty_account): def test_get_schains_for_node(skale, schain): node_id = skale.nodes.node_name_to_index(DEFAULT_NODE_NAME) schains_for_node = skale.schains.get_schains_for_node(node_id) - schain_ids_for_node = skale.schains_internal.get_schain_ids_for_node( - node_id - ) + schain_ids_for_node = skale.schains_internal.get_schain_ids_for_node(node_id) assert isinstance(schains_for_node, list) assert len(schains_for_node) > 0 @@ -57,7 +66,7 @@ def test_get_schains_for_node(skale, schain): test_schain = schains_for_node[0] schain_node_ids = skale.schains_internal.get_node_ids_for_schain( - test_schain['name'] + test_schain["name"] ) assert node_id in schain_node_ids @@ -77,45 +86,43 @@ def test_get_all_schains_ids(skale, schain): def test_get_schain_price(skale): schain_price = skale.schains.get_schain_price(1, LIFETIME_SECONDS) assert schain_price > 0 - assert type(schain_price) is int + assert isinstance(schain_price, int) def test_add_schain_by_foundation(skale, nodes): - skale.schains.grant_role( - skale.schains.schain_creator_role(), - skale.wallet.address - ) + skale.schains.grant_role(skale.schains.schain_creator_role(), skale.wallet.address) _, lifetime_seconds, name = generate_random_schain_data(skale) type_of_nodes = 1 # test2 schain try: - skale.schains.add_schain_by_foundation( - lifetime_seconds, type_of_nodes, 0, name - ) + skale.schains.add_schain_by_foundation(lifetime_seconds, type_of_nodes, 0, name) schains_ids_after = skale.schains_internal.get_all_schains_ids() - schains_names = [ - skale.schains.get(sid)['name'] - for sid in schains_ids_after - ] + schains_names = [skale.schains.get(sid)["name"] for sid in schains_ids_after] assert name in schains_names new_schain = skale.schains.get_by_name(name) - assert new_schain['mainnetOwner'] == skale.wallet.address + assert new_schain["mainnetOwner"] == skale.wallet.address + + schain = skale.schains.get_by_name(name, obj=True) + assert schain.options.multitransaction_mode is False + assert schain.options.threshold_encryption is False + assert schain.options.allocation_type is AllocationType.DEFAULT finally: skale.manager.delete_schain(name, wait_for=True) schains_ids_after = skale.schains_internal.get_all_schains_ids() - schains_names = [ - skale.schains.get(sid)['name'] - for sid in schains_ids_after - ] + schains_names = [skale.schains.get(sid)["name"] for sid in schains_ids_after] assert name not in schains_names -def test_add_schain_by_foundation_with_options(skale, nodes): - skale.schains.grant_role( - skale.schains.schain_creator_role(), - skale.wallet.address - ) +@pytest.mark.parametrize( + "mts,threshold,alloc", + [ + (True, False, AllocationType.MAX_CONSENSUS_DB), + (False, True, AllocationType.MAX_FILESTORAGE), + ], +) +def test_add_schain_by_foundation_with_options(mts, threshold, alloc, skale, nodes): + skale.schains.grant_role(skale.schains.schain_creator_role(), skale.wallet.address) _, lifetime_seconds, name = generate_random_schain_data(skale) type_of_nodes = 1 # test2 schain try: @@ -125,24 +132,23 @@ def test_add_schain_by_foundation_with_options(skale, nodes): 0, name, options=SchainOptions( - multitransaction_mode=True, - threshold_encryption=False + multitransaction_mode=mts, + threshold_encryption=threshold, + allocation_type=alloc, ), - wait_for=True + wait_for=True, ) schain = skale.schains.get_by_name(name, obj=True) - assert schain.options.multitransaction_mode is True - assert schain.options.threshold_encryption is False + assert schain.options.multitransaction_mode is mts + assert schain.options.threshold_encryption is threshold + assert schain.options.allocation_type is alloc finally: skale.manager.delete_schain(name) def test_add_schain_by_foundation_custom_owner(skale, nodes): - skale.schains.grant_role( - skale.schains.schain_creator_role(), - skale.wallet.address - ) + skale.schains.grant_role(skale.schains.schain_creator_role(), skale.wallet.address) _, lifetime_seconds, name = generate_random_schain_data(skale) type_of_nodes = 1 # test2 schain main_wallet = skale.wallet @@ -154,12 +160,12 @@ def test_add_schain_by_foundation_custom_owner(skale, nodes): 0, name, schain_owner=custom_wallet.address, - wait_for=True + wait_for=True, ) new_schain = skale.schains.get_by_name(name) - assert new_schain['mainnetOwner'] != skale.wallet.address - assert new_schain['mainnetOwner'] == custom_wallet.address + assert new_schain["mainnetOwner"] != skale.wallet.address + assert new_schain["mainnetOwner"] == custom_wallet.address skale.wallet = custom_wallet finally: @@ -168,24 +174,18 @@ def test_add_schain_by_foundation_custom_owner(skale, nodes): schains_ids_after = skale.schains_internal.get_all_schains_ids() - schains_names = [ - skale.schains.get(sid)['name'] - for sid in schains_ids_after - ] + schains_names = [skale.schains.get(sid)["name"] for sid in schains_ids_after] assert name not in schains_names def test_add_schain_by_foundation_custom_originator(skale, nodes): - skale.schains.grant_role( - skale.schains.schain_creator_role(), - skale.wallet.address - ) + skale.schains.grant_role(skale.schains.schain_creator_role(), skale.wallet.address) _, lifetime_seconds, name = generate_random_schain_data(skale) type_of_nodes = 1 # test2 schain custom_originator = generate_wallet(skale.web3) fake_multisig_data = get_abi(FAKE_MULTISIG_DATA_PATH) - payable_contract_address = fake_multisig_data['address'] + payable_contract_address = fake_multisig_data["address"] try: skale.schains.add_schain_by_foundation( @@ -194,12 +194,12 @@ def test_add_schain_by_foundation_custom_originator(skale, nodes): 0, name, schain_owner=payable_contract_address, - schain_originator=custom_originator.address + schain_originator=custom_originator.address, ) new_schain = skale.schains.get_by_name(name) - assert new_schain['originator'] != skale.wallet.address - assert new_schain['originator'] == custom_originator.address + assert new_schain["originator"] != skale.wallet.address + assert new_schain["originator"] == custom_originator.address finally: if name: @@ -207,10 +207,7 @@ def test_add_schain_by_foundation_custom_originator(skale, nodes): schains_ids_after = skale.schains_internal.get_all_schains_ids() - schains_names = [ - skale.schains.get(sid)['name'] - for sid in schains_ids_after - ] + schains_names = [skale.schains.get(sid)["name"] for sid in schains_ids_after] assert name not in schains_names @@ -221,8 +218,7 @@ def test_get_active_schains_for_node(skale, nodes, schain): node_id = skale.nodes.node_name_to_index(DEFAULT_NODE_NAME) active_schains = skale.schains.get_active_schains_for_node(node_id) all_schains = skale.schains.get_schains_for_node(node_id) - all_active_schains = [ - schain for schain in all_schains if schain['active']] + all_active_schains = [schain for schain in all_schains if schain["active"]] for active_schain in all_active_schains: assert active_schain in active_schains finally: @@ -231,27 +227,28 @@ def test_get_active_schains_for_node(skale, nodes, schain): def test_name_to_group_id(skale): - name = 'TEST' + name = "TEST" gid = skale.schains.name_to_group_id(name) - assert gid == HexBytes('0x852daa74cc3c31fe64542bb9b8764cfb91cc30f9acf9389071ffb44a9eefde46') # noqa + assert gid == HexBytes( + "0x852daa74cc3c31fe64542bb9b8764cfb91cc30f9acf9389071ffb44a9eefde46" + ) # noqa def test_get_options(skale, nodes): schain_options = SchainOptions( - multitransaction_mode=True, - threshold_encryption=False + multitransaction_mode=True, threshold_encryption=False, + allocation_type=AllocationType.DEFAULT ) name = None try: - name = create_schain(skale, random_name=True, - schain_options=schain_options) + name = create_schain(skale, random_name=True, schain_options=schain_options) id_ = skale.schains.name_to_id(name) options = skale.schains.get_options(id_) assert options == schain_options options = skale.schains.get_options_by_name(name) assert options == schain_options raw_options = skale.schains._SChains__raw_get_options(id_) - assert raw_options == [('multitr', b'\x01'), ('encrypt', b'\x00')] + assert raw_options == [("multitr", b"\x01"), ("encrypt", b"\x00"), ('alloc', b'\x00')] finally: if name: