Skip to content

Commit

Permalink
Merge pull request #469 from skalenetwork/feature/SKALE-4898-custom-c…
Browse files Browse the repository at this point in the history
…onfigs

SKALE-4898 Add custom config options for sChain
  • Loading branch information
dmytrotkk authored Jan 26, 2022
2 parents a971f52 + 6fcf178 commit c972529
Show file tree
Hide file tree
Showing 11 changed files with 211 additions and 33 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
ETH_PRIVATE_KEY: ${{ secrets.ETH_PRIVATE_KEY }}
ENDPOINT: ${{ secrets.ENDPOINT }}
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
MANAGER_TAG: "1.8.2-develop.57"
MANAGER_TAG: "1.9.0-develop.1"
steps:
- uses: actions/checkout@v2
with:
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@

setup(
name='skale.py',
version='5.6',
version='5.7',
description='SKALE client tools',
long_description_markdown_filename='README.md',
author='SKALE Labs',
Expand Down
30 changes: 26 additions & 4 deletions skale/contracts/manager/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
from skale.contracts.base_contract import BaseContract, transaction_method
from skale.utils import helper
from skale.transactions.result import TxRes
from skale.dataclasses.schain_options import (
SchainOptions, get_default_schain_options
)

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -62,15 +65,34 @@ def create_default_schain(self, name):
wait_for=True)

@transaction_method
def create_schain(self, lifetime, type_of_nodes, deposit, name):
def create_schain(
self,
lifetime: int,
type_of_nodes: int,
deposit: str,
name: str,
schain_originator: str = None,
options: SchainOptions = None
):
logger.info(
f'create_schain: type_of_nodes: {type_of_nodes}, name: {name}')
skale_nonce = helper.generate_nonce()

if schain_originator is None:
schain_originator = self.skale.wallet.address
if not options:
options = get_default_schain_options()

tx_data = encode_abi(
['uint', 'uint8', 'uint16', 'string'],
[lifetime, type_of_nodes, skale_nonce, name]
['(uint,uint8,uint16,string,address,(string,bytes)[])'],
[(lifetime, type_of_nodes, skale_nonce, name, schain_originator, options.to_tuples())]
)

return self.skale.token.contract.functions.send(
self.address,
deposit,
tx_data
)
return self.skale.token.contract.functions.send(self.address, deposit, tx_data)

@transaction_method
def get_bounty(self, node_id):
Expand Down
4 changes: 4 additions & 0 deletions skale/contracts/manager/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,7 @@ def node_manager_role(self):

def compliance_role(self):
return self.contract.functions.COMPLIANCE_ROLE().call()

@transaction_method
def init_exit(self, node_id: int) -> TxRes:
return self.contract.functions.initExit(node_id)
59 changes: 42 additions & 17 deletions skale/contracts/manager/schains.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,22 @@
""" Schains.sol functions """

import functools
from dataclasses import dataclass
from dataclasses import dataclass, asdict

from Crypto.Hash import keccak

from skale.contracts.base_contract import BaseContract, transaction_method
from skale.transactions.result import TxRes
from skale.utils.helper import format_fields
from skale.dataclasses.schain_options import (
SchainOptions, get_default_schain_options, parse_schain_options
)


FIELDS = [
'name', 'mainnetOwner', 'indexInOwnerList', 'partOfNode', 'lifetime', 'startDate', 'startBlock',
'deposit', 'index', 'generation', 'originator', 'chainId'
'deposit', 'index', 'generation', 'originator', 'chainId', 'multitransactionMode',
'thresholdEncryption'
]


Expand All @@ -48,6 +52,7 @@ class SchainStructure:
generation: int
originator: str
chain_id: int
options: SchainOptions


class SChains(BaseContract):
Expand All @@ -72,18 +77,17 @@ def get(self, id_, obj=False):
hash_obj = keccak.new(data=res[0].encode("utf8"), digest_bits=256)
hash_str = "0x" + hash_obj.hexdigest()[:13]
res.append(hash_str)
options = self.get_options(id_)
if obj: # TODO: temporary solution for backwards compatibility
return SchainStructure(*res)
return SchainStructure(*res, options=options)
else:
res += asdict(options).values()
return res

@format_fields(FIELDS)
def get_by_name(self, name):
def get_by_name(self, name, obj=False):
id_ = self.name_to_id(name)
res = self.schains_internal.get_raw(id_)
hash_obj = keccak.new(data=res[0].encode("utf8"), digest_bits=256)
hash_str = "0x" + hash_obj.hexdigest()[:13]
res.append(hash_str)
return res
return self.get(id_, obj=obj)

def get_schains_for_owner(self, account):
schains = []
Expand Down Expand Up @@ -132,21 +136,30 @@ def get_schain_price(self, index_of_type, lifetime):

@transaction_method
def add_schain_by_foundation(
self,
lifetime: int,
type_of_nodes: int,
nonce: int,
name: str,
schain_owner=None,
schain_originator=None
self,
lifetime: int,
type_of_nodes: int,
nonce: int,
name: str,
options: SchainOptions = None,
schain_owner=None,
schain_originator=None
) -> TxRes:
if schain_owner is None:
schain_owner = self.skale.wallet.address
if schain_originator is None:
schain_originator = self.skale.wallet.address
if not options:
options = get_default_schain_options()

return self.contract.functions.addSchainByFoundation(
lifetime, type_of_nodes, nonce, name, schain_owner, schain_originator
lifetime,
type_of_nodes,
nonce,
name,
schain_owner,
schain_originator,
options.to_tuples()
)

@transaction_method
Expand All @@ -155,3 +168,15 @@ def grant_role(self, role: bytes, owner: str) -> TxRes:

def schain_creator_role(self):
return self.contract.functions.SCHAIN_CREATOR_ROLE().call()

def __raw_get_options(self, schain_id: str) -> list:
return self.contract.functions.getOptions(schain_id).call()

def get_options(self, schain_id: str) -> SchainOptions:
return parse_schain_options(
raw_options=self.__raw_get_options(schain_id)
)

def get_options_by_name(self, name: str) -> SchainOptions:
id_ = self.name_to_id(name)
return self.get_options(id_)
60 changes: 60 additions & 0 deletions skale/dataclasses/schain_options.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# -*- coding: utf-8 -*-
#
# This file is part of SKALE.py
#
# Copyright (C) 2021 SKALE Labs
#
# SKALE.py 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.
#
# SKALE.py 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 SKALE.py. If not, see <https://www.gnu.org/licenses/>.

from dataclasses import dataclass


@dataclass
class SchainOptions:
multitransaction_mode: bool
threshold_encryption: bool

def to_tuples(self) -> list:
return [
('multitr', bool_to_bytes(self.multitransaction_mode)),
('encrypt', bool_to_bytes(self.threshold_encryption))
]


def parse_schain_options(raw_options: list) -> 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()
return SchainOptions(
multitransaction_mode=bytes_to_bool(raw_options[0][1]),
threshold_encryption=bytes_to_bool(raw_options[1][1])
)


def get_default_schain_options() -> SchainOptions:
return SchainOptions(
multitransaction_mode=False,
threshold_encryption=False
)


def bool_to_bytes(bool_value: bool) -> bytes:
return bool_value.to_bytes(1, byteorder='big')


def bytes_to_bool(bytes_value: bytes) -> bool:
return bool(int.from_bytes(bytes_value, 'big'))
10 changes: 9 additions & 1 deletion skale/utils/contracts_provision/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ def cleanup_nodes_schains(skale):
if schain_name is not None:
skale.manager.delete_schain_by_root(schain_name, wait_for=True)
for node_id in skale.nodes.get_active_node_ids():
skale.nodes.init_exit(node_id, wait_for=True)
skale.manager.node_exit(node_id, wait_for=True)


Expand Down Expand Up @@ -255,7 +256,13 @@ def create_nodes(skale, names=()):
)


def create_schain(skale, schain_name=DEFAULT_SCHAIN_NAME, schain_type=None, random_name=False):
def create_schain(
skale,
schain_name=DEFAULT_SCHAIN_NAME,
schain_type=None,
random_name=False,
schain_options=None
):
print('Creating schain')
# create 1 s-chain
type_of_nodes, lifetime_seconds, name = generate_random_schain_data(skale)
Expand All @@ -271,6 +278,7 @@ def create_schain(skale, schain_name=DEFAULT_SCHAIN_NAME, schain_type=None, rand
schain_type,
0,
schain_name,
options=schain_options,
wait_for=True,
value=TEST_SRW_FUND_VALUE
)
Expand Down
17 changes: 13 additions & 4 deletions tests/manager/manager_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,18 @@ def test_create_delete_schain(skale):
schains_ids = skale.schains_internal.get_all_schains_ids()

type_of_nodes, lifetime_seconds, name = generate_random_schain_data(skale)
price_in_wei = skale.schains.get_schain_price(type_of_nodes,
lifetime_seconds)
tx_res = skale.manager.create_schain(lifetime_seconds, type_of_nodes,
price_in_wei, name, wait_for=True)
price_in_wei = skale.schains.get_schain_price(
type_of_nodes,
lifetime_seconds
)
tx_res = skale.manager.create_schain(
lifetime_seconds,
type_of_nodes,
price_in_wei,
name,
wait_for=True
)

assert tx_res.receipt['status'] == 1

schains_ids_number_after = skale.schains_internal.get_schains_number()
Expand Down Expand Up @@ -221,6 +229,7 @@ def test_empty_node_exit(skale):
ip, public_ip, port, name = generate_random_node_data()
skale.manager.create_node(ip, port, name, public_ip, wait_for=True)
node_idx = skale.nodes.node_name_to_index(name)
skale.nodes.init_exit(node_idx, wait_for=True)
tx_res = skale.manager.node_exit(node_idx, wait_for=True)
assert tx_res.receipt['status'] == 1
assert skale.nodes.get_node_status(node_idx) == 2
Expand Down
2 changes: 1 addition & 1 deletion tests/manager/schains_internal_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

def test_get_raw(skale):
schain_arr = skale.schains_internal.get_raw(DEFAULT_SCHAIN_ID)
assert len(FIELDS) == len(schain_arr) + 1 # +1 for chainId
assert len(FIELDS) == len(schain_arr) + 3 # +1 for chainId + options


def test_get_raw_not_exist(skale):
Expand Down
Loading

0 comments on commit c972529

Please sign in to comment.