├── .github └── workflows │ ├── iconsdk-publish-test-pypi.yml │ └── iconsdk-workflow.yml ├── .gitignore ├── CHANGELOG.md ├── README.md ├── iconsdk ├── __init__.py ├── builder │ ├── __init__.py │ ├── call_builder.py │ └── transaction_builder.py ├── exception.py ├── icon_service.py ├── libs │ ├── __init__.py │ ├── in_memory_zip.py │ ├── serializer.py │ └── signer.py ├── monitor.py ├── providers │ ├── __init__.py │ ├── http_provider.py │ └── provider.py ├── signed_transaction.py ├── utils │ ├── __init__.py │ ├── convert_type.py │ ├── converter.py │ ├── hexadecimal.py │ ├── templates.py │ ├── type.py │ ├── typing │ │ ├── __init__.py │ │ └── conversion.py │ └── validation.py ├── version.py └── wallet │ ├── __init__.py │ └── wallet.py ├── quickstart ├── README.md └── examples │ ├── __init__.py │ ├── deploy_token_example.py │ ├── icx_transaction_example.py │ ├── sample_data │ ├── sample_token.zip │ └── standard_token.zip │ ├── sync_block_example.py │ ├── test │ ├── __init__.py │ └── constant.py │ ├── token_transaction_example.py │ ├── util │ └── repeater.py │ └── wallet_example.py ├── requirements.txt ├── setup.cfg ├── setup.py └── tests ├── __init__.py ├── api_call ├── __init__.py └── test_call.py ├── api_debug ├── __init__.py ├── test_estimate_step.py └── test_get_trace.py ├── api_full_response ├── __init__.py ├── example_response.py ├── test_full_response_base.py ├── test_get_balance.py ├── test_get_block_by_hash.py ├── test_get_block_by_height.py ├── test_get_last_block.py ├── test_get_score_api.py ├── test_get_total_supply.py ├── test_get_transaction_by_hash.py ├── test_get_transaction_result.py ├── test_send_call.py ├── test_send_deploy.py ├── test_send_deposit.py ├── test_send_message.py └── test_send_transfer.py ├── api_get ├── __init__.py ├── test_btp2.py ├── test_get_balance.py ├── test_get_block_by_hash.py ├── test_get_block_by_height.py ├── test_get_last_block.py ├── test_get_score_api.py ├── test_get_total_supply.py ├── test_get_transaction_by_hash.py ├── test_get_transaction_result.py └── test_wait_transaction_result.py ├── api_send ├── __init__.py ├── sample_token │ ├── __init__.py │ ├── package.json │ └── sample_token.py ├── sample_token2 │ ├── __init__.py │ ├── package.json │ └── sample_token.py ├── test_send_deposit.py ├── test_send_message.py ├── test_send_super.py ├── test_send_transfer.py ├── test_send_tx_wait.py └── test_signed_transaction.py ├── builder ├── __init__.py ├── test_call_builder.py └── test_transaction_builder.py ├── converter ├── __init__.py ├── example_blocks_0_1a.py ├── example_blocks_0_3.py ├── example_transactions.py ├── example_tx_results.py └── test_converter.py ├── example_config.py ├── example_tx_requests.py ├── keystore_file ├── not_a_keystore_file.txt ├── test_keystore.txt ├── test_keystore_for_transfer.txt └── test_keystore_for_transfer2.txt ├── libs ├── __init__.py ├── sample_token │ ├── __init__.py │ ├── package.json │ ├── sample_token.py │ └── tests │ │ ├── __init__.py │ │ └── test_integrate_sample_token.py ├── test_in_memory_zip.py ├── test_serializer.py └── test_signer.py ├── providers ├── __init__.py └── test_http_provider.py ├── utils ├── __init__.py ├── test_convert_type.py ├── test_hexadecimal.py └── test_validation.py └── wallet ├── __init__.py ├── test_wallet.py ├── test_wallet_create.py ├── test_wallet_load_by_private_key.py ├── test_wallet_load_from_keystore_file.py └── test_wallet_store.py /.github/workflows/iconsdk-publish-test-pypi.yml: -------------------------------------------------------------------------------- 1 | name: publish to test pypi 2 | on: 3 | workflow_dispatch: 4 | inputs: 5 | test_deploy: 6 | description: 'test deploy to https://test.pypi.org' 7 | required: false 8 | default: 'false' 9 | version: 10 | description: 'test version to publish' 11 | required: false 12 | 13 | jobs: 14 | unittest: 15 | runs-on: ubuntu-latest 16 | strategy: 17 | matrix: 18 | python-version: ["3.8", "3.9", "3.10", "3.11"] 19 | steps: 20 | - uses: actions/checkout@v3 21 | - name: Set up Python ${{ matrix.python-version }} 22 | uses: actions/setup-python@v4 23 | with: 24 | python-version: ${{ matrix.python-version }} 25 | cache: pip 26 | - name: Check event inputs 27 | run: | 28 | echo "::notice:: test_deploy=${{ github.event.inputs.test_deploy }}" 29 | echo "::notice:: version=${{ github.event.inputs.version }}" 30 | - name: Install dependency 31 | run: | 32 | python -m pip install --upgrade pip 33 | pip install -e .[tests] 34 | - name: Run Test 35 | run: | 36 | python -m pytest -ra 37 | 38 | test-deploy: 39 | needs: unittest 40 | if: github.event.inputs.test_deploy == 'true' 41 | runs-on: ubuntu-latest 42 | steps: 43 | - uses: actions/checkout@v3 44 | - name: Set up Python ${{ matrix.python-version }} 45 | uses: actions/setup-python@v4 46 | with: 47 | python-version: "3.8" 48 | cache: pip 49 | - name: Install dependency 50 | run: | 51 | python -m pip install --upgrade pip 52 | pip install wheel 53 | echo "::notice:: pip list" 54 | pip list 55 | - name: Setup test version 56 | if: github.event.inputs.version != '' 57 | run: | 58 | TEST_VERSION=${{ github.event.inputs.version }} 59 | echo "::notice:: update version from $(cat VERSION) to ${TEST_VERSION}" 60 | echo ${TEST_VERSION} > VERSION 61 | - name: Cache dist 62 | id: dist_cache 63 | uses: actions/cache@v3 64 | with: 65 | path: ./dist 66 | key: ${{ runner.os }}-dist-${{ github.sha }} 67 | - name: Build package 68 | if: steps.dist_cache.outputs.cache-hit != 'true' 69 | run: | 70 | python setup.py sdist bdist_wheel 71 | echo "::notice:: ls -al dist" 72 | ls -al dist 73 | - name: Publish package to TestPyPI 74 | uses: pypa/gh-action-pypi-publish@release/v1 75 | with: 76 | user: __token__ 77 | password: ${{ secrets.TEST_PYPI_API_TOKEN }} 78 | repository_url: https://test.pypi.org/legacy/ 79 | -------------------------------------------------------------------------------- /.github/workflows/iconsdk-workflow.yml: -------------------------------------------------------------------------------- 1 | name: unittest and publish to pypi 2 | on: 3 | push: 4 | branches: [master] 5 | pull_request: 6 | release: 7 | types: [published] 8 | 9 | jobs: 10 | unittest: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | python-version: ["3.8", "3.9", "3.10", "3.11"] 15 | steps: 16 | - uses: actions/checkout@v3 17 | - name: Set up Python ${{ matrix.python-version }} 18 | uses: actions/setup-python@v4 19 | with: 20 | python-version: ${{ matrix.python-version }} 21 | cache: pip 22 | - name: Install dependency 23 | run: | 24 | python -m pip install --upgrade pip 25 | pip install -e .[tests] 26 | - name: Run Test 27 | run: | 28 | python -m pytest -ra 29 | 30 | deploy: 31 | needs: unittest 32 | if: github.event_name == 'release' 33 | runs-on: ubuntu-latest 34 | environment: release 35 | permissions: 36 | id-token: write 37 | steps: 38 | - uses: actions/checkout@v3 39 | - name: Set up Python ${{ matrix.python-version }} 40 | uses: actions/setup-python@v4 41 | with: 42 | python-version: "3.8" 43 | cache: pip 44 | - name: Install dependency 45 | run: | 46 | python -m pip install --upgrade pip 47 | pip install wheel 48 | echo "::notice:: pip list" 49 | pip list 50 | - name: Build package 51 | id: build_package 52 | run: | 53 | python setup.py sdist bdist_wheel 54 | echo "::notice:: ls -al dist" 55 | ls -al dist 56 | - name: Publish package 57 | uses: pypa/gh-action-pypi-publish@release/v1 58 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *.py[cod] 3 | *$py.class 4 | .idea/ 5 | 6 | .Python 7 | 8 | # Environments 9 | .env 10 | .venv 11 | env/ 12 | venv/ 13 | ENV/ 14 | env.bak/ 15 | venv.bak/ 16 | 17 | build/ 18 | dist/ 19 | tmp/ 20 | iconsdk.egg-info/ 21 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [1.2.0] - Sep, 17, 2019 4 | ### Added 5 | - Apply return value of full response to support T-bears 6 | 7 | ### Changed 8 | - Substitute `coincurve` for `secp256k1` 9 | - Support for setting logger function 10 | 11 | ### Removed 12 | N/A 13 | 14 | 15 | ## [1.1.0] - June, 25, 2019 16 | ### Added 17 | - DepositTransaction and DepositTransactionBuilder for deposit and withdraw 18 | 19 | ### Changed 20 | - `From` field of call became optional 21 | 22 | ### Removed 23 | - Set and get channel method on provider 24 | 25 | ## [1.0.9] - May, 22, 2019 26 | ### Added 27 | - Add the method of `estimate_step` 28 | 29 | ### Changed 30 | N/A 31 | 32 | ### Removed 33 | N/A 34 | 35 | ## [1.0.8] - Apr, 23, 2019 36 | ### Added 37 | N/A 38 | 39 | ### Changed 40 | - Fix: Convert the negative value into hex str correctly by using `hex` function 41 | - Fix: Convert hex str to bytes correctly by using `fromhex` method 42 | 43 | ### Removed 44 | N/A 45 | 46 | ## [1.0.7] - Dec, 7, 2018 47 | ### Added 48 | N/A 49 | 50 | ### Changed 51 | - Fix: The data type of the Message type transaction's data is changed from string to hex string prefixed with '0x' 52 | 53 | ### Removed 54 | N/A 55 | 56 | 57 | ## [1.0.6] - Nov, 22, 2018 58 | ### Added 59 | N/A 60 | 61 | ### Changed 62 | - Fix: convert_params_value_to_hex_str() converts first item of dict only 63 | 64 | ### Removed 65 | N/A 66 | 67 | 68 | ## [1.0.5] - Nov, 15, 2018 69 | ### Added 70 | - Add the method of `get_max_step_limit` 71 | - Make the call and transaction have the method of `from_dict` to convert the dict to call or transaction object 72 | - Make the call and transaction have the method of `to_dict` to convert call or transaction object to the dict 73 | 74 | ### Changed 75 | - Upgrade requests to version 2.20.0 or later 76 | - Amend `gen_deploy_data_content` not to load tests directory 77 | 78 | ### Removed 79 | N/A 80 | 81 | 82 | ## [1.0.4] - Sep, 17, 2018 83 | ### Added 84 | - Add quickstart module 85 | - Add samples and README.md for quickstart 86 | - Add Converter to convert return data from hex str into an int 87 | - Add repeater for calling the decorated function 88 | - To check returning transaction result after consensus 89 | 90 | ### Changed 91 | - Amend the way to load a key file having 'bom' 92 | 93 | ### Removed 94 | N/A 95 | 96 | 97 | ## [1.0.3] - August, 31, 2018 98 | ### Added 99 | - Add FileNotFountError while loading the key file 100 | 101 | ### Changed 102 | - Set the function `create_keyfile_json`'s param 'kdf' into 'scrypt' 103 | - To set the params 'kdf' into 'scrypt' while creating key file json 104 | 105 | ### Removed 106 | N/A 107 | 108 | 109 | ## [1.0.1] - August, 30, 2018 110 | ### Added 111 | N/A 112 | 113 | ### Changed 114 | - Revise the package names and update the version to 1.0.1. 115 | - `IconService` to `iconsdk` 116 | - `Icon_service` to `icon_service` 117 | 118 | ### Removed 119 | N/A 120 | 121 | 122 | ## [1.0.0] - July, 31, 2018 123 | ### Added 124 | - Initialize version. 125 | 126 | ### Changed 127 | N/A 128 | 129 | ### Removed 130 | N/A 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /iconsdk/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | from logging import getLogger 16 | 17 | logger = getLogger("ICON-SDK-PYTHON") 18 | -------------------------------------------------------------------------------- /iconsdk/builder/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | -------------------------------------------------------------------------------- /iconsdk/builder/call_builder.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | from ..exception import DataTypeException 17 | from ..utils.typing.conversion import object_to_str 18 | 19 | 20 | class Call: 21 | """ 22 | Class `Call` for calling a SCORE API. 23 | Once an instance generated, it is read-only.""" 24 | 25 | def __init__(self, from_: str, to: str, method: str, params: dict, height: int): 26 | self.__from = from_ 27 | self.__to = to 28 | self.__method = method 29 | self.__params = params 30 | self.__height = height 31 | 32 | @property 33 | def from_(self) -> str: 34 | return self.__from 35 | 36 | @property 37 | def to(self) -> str: 38 | return self.__to 39 | 40 | @property 41 | def method(self) -> str: 42 | return self.__method 43 | 44 | @property 45 | def params(self) -> dict: 46 | return object_to_str(self.__params) if self.__params else None 47 | 48 | @property 49 | def height(self) -> int: 50 | return object_to_str(self.__height) if self.__height else None 51 | 52 | def to_dict(self) -> dict: 53 | return {"from_": self.from_, "to": self.to, "method": self.method, "params": self.params, "height": self.height} 54 | 55 | 56 | class CallBuilder: 57 | """ 58 | Builder for a `Call` object. 59 | Once setting it, a value of any property can't be changed forever. 60 | """ 61 | 62 | def __init__(self, from_: str = None, to: str = None, method: str = None, params: dict = None, 63 | height: int = None) -> Call: 64 | self._from_ = from_ 65 | self._to = to 66 | self._method = method 67 | self._params = params 68 | self._height = height 69 | 70 | def from_(self, from_: str) -> 'CallBuilder': 71 | self._from_ = from_ 72 | return self 73 | 74 | def to(self, to: str) -> 'CallBuilder': 75 | self._to = to 76 | return self 77 | 78 | def method(self, method: str) -> 'CallBuilder': 79 | self._method = method 80 | return self 81 | 82 | def params(self, params: dict) -> 'CallBuilder': 83 | self._params = params 84 | return self 85 | 86 | def height(self, height: int) -> 'CallBuilder': 87 | self._height = height 88 | return self 89 | 90 | def build(self) -> Call: 91 | return Call(self._from_, self._to, self._method, self._params, self._height) 92 | 93 | @classmethod 94 | def from_dict(cls, call_as_dict: dict) -> 'CallBuilder': 95 | """Returns a CallBuilder made from dict""" 96 | try: 97 | return cls( 98 | from_=call_as_dict['from_'] if "from_" in call_as_dict else None, 99 | to=call_as_dict['to'], 100 | method=call_as_dict['method'], 101 | params=call_as_dict['params'] if "params" in call_as_dict else None, 102 | height=call_as_dict['height'], 103 | ) 104 | except KeyError: 105 | raise DataTypeException("The input data invalid. Mapping key not found.") 106 | -------------------------------------------------------------------------------- /iconsdk/exception.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | from enum import IntEnum, unique 17 | from typing import Optional 18 | 19 | 20 | @unique 21 | class IconServiceExceptionCode(IntEnum): 22 | """Result code enumeration""" 23 | OK = 0 24 | KEY_STORE_ERROR = 1 25 | ADDRESS_ERROR = 2 26 | BALANCE_ERROR = 3 27 | DATA_TYPE_ERROR = 4 28 | JSON_RPC_ERROR = 5 29 | ZIP_MEMORY_ERROR = 6 30 | URL_ERROR = 7 31 | 32 | def __str__(self) -> str: 33 | return str(self.name).capitalize().replace('_', ' ') 34 | 35 | 36 | class IconServiceBaseException(BaseException): 37 | 38 | def __init__(self, message: Optional[str], code: IconServiceExceptionCode = IconServiceExceptionCode.OK): 39 | if message is None: 40 | message = str(code) 41 | self.__message = message 42 | self.__code = code 43 | 44 | @property 45 | def message(self): 46 | return self.__message 47 | 48 | @property 49 | def code(self): 50 | return self.__code 51 | 52 | def __str__(self): 53 | return f'{self.message}' 54 | 55 | 56 | class KeyStoreException(IconServiceBaseException): 57 | """"Error when making or loading a keystore file.""" 58 | 59 | def __init__(self, message: Optional[str]): 60 | super().__init__(message, IconServiceExceptionCode.KEY_STORE_ERROR) 61 | 62 | 63 | class AddressException(IconServiceBaseException): 64 | """Error when having an invalid address.""" 65 | 66 | def __init__(self, message: Optional[str]): 67 | super().__init__(message, IconServiceExceptionCode.ADDRESS_ERROR) 68 | 69 | 70 | class BalanceException(IconServiceBaseException): 71 | """Error when having an invalid balance.""" 72 | 73 | def __init__(self, message: Optional[str]): 74 | super().__init__(message, IconServiceExceptionCode.BALANCE_ERROR) 75 | 76 | 77 | class DataTypeException(IconServiceBaseException): 78 | """Error when data type is invalid.""" 79 | 80 | def __init__(self, message: Optional[str]): 81 | super().__init__(message, IconServiceExceptionCode.DATA_TYPE_ERROR) 82 | 83 | 84 | class JSONRPCException(IconServiceBaseException): 85 | """Error when get JSON-RPC Error Response.""" 86 | 87 | def __init__(self, message: Optional[str]): 88 | super().__init__(message, IconServiceExceptionCode.JSON_RPC_ERROR) 89 | 90 | 91 | class ZipException(IconServiceBaseException): 92 | """"Error while write zip in memory""" 93 | 94 | def __init__(self, message: Optional[str]): 95 | super().__init__(message, IconServiceExceptionCode.ZIP_MEMORY_ERROR) 96 | 97 | 98 | class URLException(IconServiceBaseException): 99 | """Error regarding invalid URL""" 100 | 101 | def __init__(self, message: Optional[str]): 102 | super().__init__(message, IconServiceExceptionCode.URL_ERROR) 103 | -------------------------------------------------------------------------------- /iconsdk/libs/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | -------------------------------------------------------------------------------- /iconsdk/libs/in_memory_zip.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | from io import BytesIO 17 | from os import path, walk 18 | from zipfile import ZipFile, ZIP_DEFLATED 19 | 20 | from iconsdk.exception import ZipException 21 | 22 | 23 | def gen_deploy_data_content(_path: str) -> bytes: 24 | """Generate bytes of zip data of SCORE. 25 | 26 | :param _path: Path of the directory to be zipped. 27 | """ 28 | if path.isdir(_path) is False and path.isfile(_path) is False: 29 | raise ValueError(f"Invalid path {_path}") 30 | try: 31 | memory_zip = InMemoryZip() 32 | memory_zip.zip_in_memory(_path) 33 | except ZipException: 34 | raise ZipException(f"Can't zip SCORE contents") 35 | else: 36 | return memory_zip.data 37 | 38 | 39 | class InMemoryZip: 40 | """Class for compressing data in memory using zip and BytesIO.""" 41 | 42 | def __init__(self): 43 | self._in_memory = BytesIO() 44 | 45 | @property 46 | def data(self) -> bytes: 47 | """Returns zip data 48 | 49 | :return: zip data 50 | """ 51 | self._in_memory.seek(0) 52 | return self._in_memory.read() 53 | 54 | def zip_in_memory(self, _path: str): 55 | """Compress zip data (bytes) in memory. 56 | 57 | :param _path: The path of the directory to be zipped. 58 | """ 59 | try: 60 | # when it is a zip file 61 | if path.isfile(_path): 62 | zf = ZipFile(_path, 'r', ZIP_DEFLATED, False) 63 | zf.testzip() 64 | with open(_path, mode='rb') as fp: 65 | fp.seek(0) 66 | self._in_memory.seek(0) 67 | self._in_memory.write(fp.read()) 68 | else: 69 | # root path for figuring out directory of tests 70 | tmp_root = None 71 | with ZipFile(self._in_memory, 'a', ZIP_DEFLATED, False, compresslevel=9) as zf: 72 | for root, folders, files in walk(_path): 73 | if 'package.json' in files: 74 | tmp_root = root 75 | if tmp_root and root.replace(tmp_root,'') == '/tests': 76 | continue 77 | if root.find('__pycache__') != -1: 78 | continue 79 | if root.find('/.') != -1: 80 | continue 81 | for file in files: 82 | if file.startswith('.'): 83 | continue 84 | full_path = path.join(root, file) 85 | zf.write(full_path) 86 | except ZipException: 87 | raise ZipException 88 | 89 | -------------------------------------------------------------------------------- /iconsdk/libs/serializer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | from copy import deepcopy 17 | from hashlib import sha3_256 18 | from typing import Optional 19 | 20 | translator = str.maketrans({ 21 | "\\": "\\\\", 22 | "{": "\\{", 23 | "}": "\\}", 24 | "[": "\\[", 25 | "]": "\\]", 26 | ".": "\\." 27 | }) 28 | 29 | 30 | def __make_params_serialized(json_data: dict) -> str: 31 | 32 | def encode(data) -> str: 33 | if isinstance(data, dict): 34 | return encode_dict(data) 35 | elif isinstance(data, list): 36 | return encode_list(data) 37 | else: 38 | return escape(data) 39 | 40 | def encode_dict(data: dict) -> str: 41 | result = ".".join(_encode_dict(data)) 42 | return "{" + result + "}" 43 | 44 | def _encode_dict(data: dict) -> list: 45 | for key in sorted(data.keys()): 46 | yield key 47 | yield encode(data[key]) 48 | 49 | def encode_list(data: list) -> str: 50 | result = ".".join(_encode_list(data)) 51 | return f"[" + result + "]" 52 | 53 | def _encode_list(data: list) -> list: 54 | for item in data: 55 | yield encode(item) 56 | 57 | def escape(data) -> str: 58 | if data is None: 59 | return "\\0" 60 | 61 | data = str(data) 62 | return data.translate(translator) 63 | 64 | return ".".join(_encode_dict(json_data)) 65 | 66 | 67 | def serialize(params: dict) -> bytes: 68 | """ 69 | Serialized params of an original JSON request starting with `icx_sendTransaction` 70 | to generate a message hash for a signature. 71 | 72 | :param params: params in a original JSON request for transaction. 73 | :return: serialized params. 74 | For example, data like `icx_sendTransaction....` is converted to bytes. 75 | """ 76 | copy_tx = deepcopy(params) 77 | key_name_for_tx_hash = __get_key_name_for_tx_hash(params) 78 | 79 | if key_name_for_tx_hash in copy_tx: 80 | del copy_tx[key_name_for_tx_hash] 81 | 82 | if 'signature' in copy_tx: 83 | del copy_tx['signature'] 84 | 85 | partial_serialized_params = __make_params_serialized(copy_tx) 86 | return f"icx_sendTransaction.{partial_serialized_params}".encode() 87 | 88 | 89 | def generate_message(params: dict) -> str: 90 | """ 91 | Generates transaction's message hash from params in request for transaction. 92 | 93 | :param params: params in request for transaction. 94 | :return: the 256 bit hash digest of a message. Hexadecimal encoded. 95 | """ 96 | bytes_message_hash = serialize(params) 97 | return sha3_256(bytes_message_hash).hexdigest() 98 | 99 | 100 | def __get_key_name_for_tx_hash(params: dict) -> Optional[str]: 101 | if __get_tx_version(params) == hex(2): 102 | return "tx_hash" 103 | else: 104 | return None 105 | 106 | 107 | def __get_tx_version(params: dict) -> str: 108 | if 'version' not in params: 109 | return hex(2) 110 | else: 111 | return params['version'] 112 | -------------------------------------------------------------------------------- /iconsdk/libs/signer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | from coincurve import PrivateKey 17 | 18 | 19 | def sign(data: bytes, private_key: bytes) -> bytes: 20 | """ 21 | Generates on the ECDSA signature in bytes from data. 22 | 23 | :param data: data to be signed 24 | :param private_key: private key 25 | :return signature: signature made from input data 26 | """ 27 | private_key_object = PrivateKey(private_key) 28 | return private_key_object.sign_recoverable(data, hasher=None) 29 | -------------------------------------------------------------------------------- /iconsdk/providers/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | -------------------------------------------------------------------------------- /iconsdk/providers/provider.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | from abc import ABCMeta, abstractmethod 17 | from typing import Optional, Dict, Any 18 | 19 | 20 | class MonitorSpec(metaclass=ABCMeta): 21 | @abstractmethod 22 | def get_request(self) -> any: 23 | """ 24 | Request object to send to the websocket 25 | """ 26 | raise NotImplementedError("MonitorSpec must implement this method") 27 | 28 | @abstractmethod 29 | def get_path(self) -> str: 30 | """ 31 | Extra path fragment for the websocket 32 | """ 33 | raise NotImplementedError("MonitorSpec must implement this method") 34 | 35 | 36 | class MonitorTimeoutException(Exception): 37 | pass 38 | 39 | 40 | class Monitor(metaclass=ABCMeta): 41 | @abstractmethod 42 | def close(self): 43 | """ 44 | Close the monitor 45 | 46 | It releases related resources. 47 | """ 48 | pass 49 | 50 | @abstractmethod 51 | def read(self, timeout: Optional[float] = None) -> any: 52 | """ 53 | Read the notification 54 | 55 | :param timeout: Timeout to wait for the message in fraction of seconds 56 | :except MonitorTimeoutException: if it passes the timeout 57 | """ 58 | pass 59 | 60 | 61 | class Provider(metaclass=ABCMeta): 62 | """The provider defines how the IconService connects to RPC server.""" 63 | 64 | @abstractmethod 65 | def make_request(self, method: str, params: Optional[Dict[str, Any]] = None, full_response: bool = False): 66 | raise NotImplementedError("Providers must implement this method") 67 | 68 | @abstractmethod 69 | def make_monitor(self, spec: MonitorSpec, keep_alive: Optional[float] = None) -> Monitor: 70 | """ 71 | Make monitor for the spec 72 | :param spec: Monitoring spec 73 | :param keep_alive: Keep-alive message interval in fraction of seconds 74 | """ 75 | raise NotImplementedError() 76 | 77 | -------------------------------------------------------------------------------- /iconsdk/signed_transaction.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | from base64 import b64encode 17 | from hashlib import sha3_256 18 | 19 | from iconsdk.builder.transaction_builder import Transaction 20 | from iconsdk.exception import DataTypeException 21 | from iconsdk.libs.serializer import serialize 22 | from iconsdk.utils import get_timestamp 23 | from iconsdk.utils.convert_type import convert_int_to_hex_str 24 | from iconsdk.wallet.wallet import Wallet 25 | 26 | 27 | class SignedTransaction: 28 | 29 | def __init__(self, transaction: Transaction, wallet: Wallet, step_limit: int = None): 30 | """Converts raw transaction into the signed transaction object having a signature. 31 | 32 | :param transaction: A transaction object not having a signature field yet 33 | :param wallet: A wallet object 34 | """ 35 | if step_limit is not None: 36 | transaction.step_limit = step_limit 37 | if transaction.step_limit is None: 38 | raise DataTypeException("Transaction should have step limit when signed.") 39 | 40 | self.__signed_transaction_dict = self.convert_tx_to_jsonrpc_request(transaction, wallet) 41 | message_hash = sha3_256(serialize(self.__signed_transaction_dict)).digest() 42 | signature = wallet.sign(message_hash) 43 | self.__signed_transaction_dict["signature"] = b64encode(signature).decode() 44 | 45 | @property 46 | def signed_transaction_dict(self) -> dict: 47 | return self.__signed_transaction_dict 48 | 49 | @staticmethod 50 | def convert_tx_to_jsonrpc_request(transaction: Transaction, wallet: Wallet = None) -> dict: 51 | """Converts an instance of the transaction into JSON RPC request in dict""" 52 | dict_tx = { 53 | "version": convert_int_to_hex_str(transaction.version) if transaction.version else "0x3", 54 | "from": transaction.from_ if transaction.from_ else wallet.get_address(), 55 | "to": transaction.to, 56 | "stepLimit": convert_int_to_hex_str(transaction.step_limit), 57 | "timestamp": convert_int_to_hex_str(transaction.timestamp) if transaction.timestamp else get_timestamp(), 58 | "nid": convert_int_to_hex_str(transaction.nid) if transaction.nid else "0x1" 59 | } 60 | 61 | if transaction.value is not None: 62 | dict_tx["value"] = convert_int_to_hex_str(transaction.value) 63 | 64 | if transaction.nonce is not None: 65 | dict_tx["nonce"] = convert_int_to_hex_str(transaction.nonce) 66 | 67 | if transaction.data_type is not None: 68 | dict_tx["dataType"] = transaction.data_type 69 | 70 | if transaction.data is not None: 71 | dict_tx["data"] = transaction.data 72 | 73 | return dict_tx 74 | -------------------------------------------------------------------------------- /iconsdk/utils/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | import os 17 | from functools import wraps 18 | from logging import Formatter, StreamHandler 19 | from time import time 20 | from typing import Union 21 | 22 | from iconsdk import logger 23 | 24 | # https://docs.python.org/3/glossary.html#term-path-like-object 25 | PathLikeObject = Union[str, bytes, os.PathLike] 26 | 27 | 28 | def store_keystore_file_on_the_path(file_path: PathLikeObject, json_string: str): 29 | """Stores a created keystore string data which is JSON format on the file path. 30 | 31 | :param file_path: The path where the file will be saved. type(str) 32 | :param json_string: Contents of the keystore. 33 | """ 34 | if os.path.isfile(file_path): 35 | raise FileExistsError 36 | 37 | with open(file_path, 'wt') as f: 38 | f.write(json_string) 39 | 40 | 41 | def apply_to_return_value(callback): 42 | def outer(fn): 43 | @wraps(fn) 44 | def inner(*args, **kwargs): 45 | return callback(fn(*args, **kwargs)) 46 | 47 | return inner 48 | 49 | return outer 50 | 51 | 52 | to_dict = apply_to_return_value(dict) 53 | 54 | 55 | def set_logger(level, handler=StreamHandler(), 56 | format: str = '%(asctime)s %(name)-12s %(levelname)-5s ' 57 | '%(filename)-12s %(lineno)-4s %(funcName)-12s %(message)s') -> None: 58 | """ Set logger by setting level and handler 59 | 60 | :param level: the logging level of this logger. The level must be an int or a str. 61 | :param handler: the specified handler to be added to this logger 62 | :param format: the specified format strings which initialize the formatter with. 63 | :return: None 64 | """ 65 | formatter = Formatter(format) 66 | handler.setFormatter(formatter) 67 | logger.addHandler(handler) 68 | logger.setLevel(level) 69 | 70 | 71 | def get_timestamp() -> str: 72 | """Returns the timestamp in microseconds since the epoch""" 73 | return hex(int(time() * 10 ** 6)) 74 | -------------------------------------------------------------------------------- /iconsdk/utils/convert_type.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | from iconsdk.exception import DataTypeException 17 | from iconsdk.utils.hexadecimal import add_0x_prefix, remove_0x_prefix 18 | from iconsdk.utils.type import is_integer, is_bytes, is_str 19 | 20 | 21 | def convert_int_to_hex_str(value: int) -> str: 22 | """Converts an integer to hex string prefixed with '0x'.""" 23 | if is_integer(value): 24 | return hex(value) 25 | else: 26 | raise DataTypeException("Data type should be integer.") 27 | 28 | 29 | def convert_bytes_to_hex_str(value: bytes) -> str: 30 | """Converts bytes to hex string prefixed with '0x'.""" 31 | if is_bytes(value): 32 | return add_0x_prefix(value.hex()) 33 | else: 34 | raise DataTypeException("Data type should be bytes.") 35 | 36 | 37 | def convert_hex_str_to_int(value: str) -> int: 38 | """Converts hex string prefixed with '0x' into int.""" 39 | if is_str(value): 40 | return int(value, 16) 41 | else: 42 | raise DataTypeException("Data type should be string.") 43 | 44 | 45 | def convert_hex_str_to_bytes(value: str) -> bytes: 46 | """Converts hex string prefixed with '0x' into bytes.""" 47 | if is_str(value): 48 | return bytes.fromhex(remove_0x_prefix(value)) 49 | else: 50 | raise DataTypeException("Data type should be string.") 51 | -------------------------------------------------------------------------------- /iconsdk/utils/hexadecimal.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | """ 17 | This module is reference to `hexadecimal`. 18 | It is used for 19 | 20 | - value starting with `0x` 21 | - an address of a wallet starting with 'hx' 22 | - an address of SCORE starting with 'cx' 23 | """ 24 | from re import match 25 | 26 | from iconsdk.exception import DataTypeException 27 | from iconsdk.utils.type import is_str 28 | 29 | 30 | def is_0x_prefixed(value: str) -> bool: 31 | if not is_str(value): 32 | raise DataTypeException("Value type must be str. Got: {0}".format(repr(value))) 33 | return value.startswith('0x') 34 | 35 | 36 | def remove_0x_prefix(value: str) -> str: 37 | if is_0x_prefixed(value): 38 | return value[2:] 39 | return value 40 | 41 | 42 | def add_0x_prefix(value: str) -> str: 43 | if is_0x_prefixed(value): 44 | return value 45 | return '0x' + value 46 | 47 | 48 | def is_hx_prefixed(value: str) -> bool: 49 | """Used for checking an address of a wallet.""" 50 | if not is_str(value): 51 | raise DataTypeException("Value type must be str. Got: {0}.".format(repr(value))) 52 | return value.startswith('hx') 53 | 54 | 55 | def remove_hx_prefix(value: str) -> str: 56 | if is_hx_prefixed(value): 57 | return value[2:] 58 | return value 59 | 60 | 61 | def add_hx_prefix(value: str) -> str: 62 | if is_hx_prefixed(value): 63 | return value 64 | return 'hx' + value 65 | 66 | 67 | def is_cx_prefixed(value: str) -> bool: 68 | """Used for checking an address of SCORE.""" 69 | if not is_str(value): 70 | raise DataTypeException("Value type must be str. Got: {0}.".format(repr(value))) 71 | return value.startswith("cx") 72 | 73 | 74 | def remove_cx_prefix(value: str) -> str: 75 | if is_cx_prefixed(value): 76 | return value[2:] 77 | return value 78 | 79 | 80 | def add_cx_prefix(value: str) -> str: 81 | if is_cx_prefixed(value): 82 | return value 83 | return 'cx' + value 84 | 85 | 86 | def is_lowercase_hex_string(value: str) -> bool: 87 | """Check whether value is hexadecimal format or not 88 | 89 | :param value: text 90 | :return: True(lowercase hexadecimal) otherwise False 91 | """ 92 | try: 93 | result = None 94 | if isinstance(value, str): 95 | result = match('[0-9a-f]+', value) 96 | return result is not None and len(result.group(0)) == len(value) 97 | except Exception: 98 | raise DataTypeException("Not lowercase hex string. Get: {0}.".format(repr(value))) 99 | -------------------------------------------------------------------------------- /iconsdk/utils/type.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | from numbers import Number 17 | from typing import Mapping 18 | 19 | bytes_types = (bytes, bytearray) 20 | integer_types = (int,) 21 | str_types = (str,) 22 | string_types = (bytes, str, bytearray) 23 | 24 | 25 | def is_integer(value) -> bool: 26 | return isinstance(value, integer_types) and not isinstance(value, bool) 27 | 28 | 29 | def is_bytes(value) -> bool: 30 | return isinstance(value, bytes_types) 31 | 32 | 33 | def is_str(value) -> bool: 34 | return isinstance(value, str_types) 35 | 36 | 37 | def is_boolean(value) -> bool: 38 | return isinstance(value, bool) 39 | 40 | 41 | def is_dict(obj) -> bool: 42 | return isinstance(obj, Mapping) 43 | 44 | 45 | def is_list(obj) -> bool: 46 | return isinstance(obj, list) 47 | 48 | 49 | def is_tuple(obj) -> bool: 50 | return isinstance(obj, tuple) 51 | 52 | 53 | def is_null(obj) -> bool: 54 | return obj is None 55 | 56 | 57 | def is_number(obj) -> bool: 58 | return isinstance(obj, Number) 59 | -------------------------------------------------------------------------------- /iconsdk/utils/typing/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2020 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | -------------------------------------------------------------------------------- /iconsdk/utils/typing/conversion.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2020 ICON Foundation Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | from __future__ import annotations 17 | 18 | from typing import Any 19 | 20 | from ...wallet.wallet import Wallet 21 | 22 | BASE_TYPES = {bool, bytes, int, str, Wallet} 23 | TYPE_NAME_TO_TYPE = {_type.__name__: _type for _type in BASE_TYPES} 24 | 25 | 26 | def is_base_type(value: type) -> bool: 27 | try: 28 | return value in BASE_TYPES 29 | except: 30 | return False 31 | 32 | 33 | def name_to_type(type_name: str) -> type: 34 | return TYPE_NAME_TO_TYPE[type_name] 35 | 36 | 37 | def isinstance_ex(value: Any, _type: type) -> bool: 38 | if not isinstance(value, _type): 39 | return False 40 | 41 | if type(value) is bool and _type is not bool: 42 | return False 43 | 44 | return True 45 | 46 | 47 | def base_object_to_str(value: Any) -> str: 48 | if isinstance(value, Wallet): 49 | return value.get_address() 50 | elif isinstance(value, int): 51 | return hex(value) 52 | elif isinstance(value, bytes): 53 | return bytes_to_hex(value) 54 | elif isinstance(value, bool): 55 | return hex(value) 56 | elif isinstance(value, str): 57 | return value 58 | 59 | raise TypeError(f"Unsupported type: {type(value)}") 60 | 61 | 62 | def object_to_str(value: Any) -> Any | None: 63 | if is_base_type(type(value)): 64 | return base_object_to_str(value) 65 | 66 | if isinstance(value, list): 67 | return [object_to_str(i) for i in value] 68 | 69 | if isinstance(value, dict): 70 | return {k: object_to_str(value[k]) for k in value} 71 | 72 | if value is None: 73 | return None 74 | 75 | raise TypeError(f"Unsupported type: {type(value)}") 76 | 77 | 78 | def bytes_to_hex(value: bytes, prefix: str = "0x") -> str: 79 | return f"{prefix}{value.hex()}" 80 | -------------------------------------------------------------------------------- /iconsdk/version.py: -------------------------------------------------------------------------------- 1 | __version__ = "2.5.2" 2 | -------------------------------------------------------------------------------- /iconsdk/wallet/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | 17 | -------------------------------------------------------------------------------- /quickstart/examples/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | -------------------------------------------------------------------------------- /quickstart/examples/deploy_token_example.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | from os import path 16 | 17 | from iconsdk.builder.call_builder import CallBuilder 18 | from iconsdk.builder.transaction_builder import DeployTransactionBuilder 19 | from iconsdk.exception import JSONRPCException 20 | from iconsdk.icon_service import IconService 21 | from iconsdk.libs.in_memory_zip import gen_deploy_data_content 22 | from iconsdk.providers.http_provider import HTTPProvider 23 | from iconsdk.signed_transaction import SignedTransaction 24 | from iconsdk.utils.convert_type import convert_hex_str_to_int 25 | from iconsdk.wallet.wallet import KeyWallet 26 | from quickstart.examples.test.constant import ( 27 | BASE_DOMAIN_URL_V3_FOR_TEST, 28 | PRIVATE_KEY_FOR_TEST, 29 | SCORE_INSTALL_ADDRESS, 30 | GOVERNANCE_ADDRESS, 31 | VERSION_FOR_TEST 32 | ) 33 | from quickstart.examples.util.repeater import retry 34 | 35 | current_dir_path = path.abspath(path.dirname(__file__)) 36 | score_path_standard_token = path.join(current_dir_path, 'sample_data/standard_token.zip') 37 | score_path_sample_token = path.join(current_dir_path, 'sample_data/sample_token.zip') 38 | score_paths = [score_path_sample_token, score_path_standard_token] 39 | icon_service = IconService(HTTPProvider(BASE_DOMAIN_URL_V3_FOR_TEST, VERSION_FOR_TEST)) 40 | 41 | 42 | # Returns the max step limit 43 | def get_max_step_limit(): 44 | _param = { 45 | "contextType": "invoke" 46 | } 47 | _call = CallBuilder()\ 48 | .from_(wallet1.get_address())\ 49 | .to(GOVERNANCE_ADDRESS)\ 50 | .method("getMaxStepLimit")\ 51 | .params(_param)\ 52 | .build() 53 | _result = icon_service.call(_call) 54 | return convert_hex_str_to_int(_result) 55 | 56 | 57 | for score_path in score_paths: 58 | # Reads the zip file 'standard_token.zip' and returns bytes of the file 59 | install_content_bytes = gen_deploy_data_content(score_path) 60 | # Loads a wallet from a key store file 61 | wallet1 = KeyWallet.load(PRIVATE_KEY_FOR_TEST) 62 | print("="*100) 63 | print("[wallet1] address: ", wallet1.get_address(), " private key: ", wallet1.get_private_key()) 64 | 65 | # Enters transaction information 66 | deploy_transaction = DeployTransactionBuilder()\ 67 | .from_(wallet1.get_address())\ 68 | .to(SCORE_INSTALL_ADDRESS) \ 69 | .step_limit(get_max_step_limit())\ 70 | .nid(3)\ 71 | .nonce(3)\ 72 | .content_type("application/zip")\ 73 | .content(install_content_bytes)\ 74 | .version(3)\ 75 | .build() 76 | 77 | # Returns the signed transaction object having a signature 78 | signed_transaction_dict = SignedTransaction(deploy_transaction, wallet1) 79 | 80 | # Sends the transaction 81 | tx_hash = icon_service.send_transaction(signed_transaction_dict) 82 | print("txHash: ", tx_hash) 83 | 84 | @retry(JSONRPCException, tries=10, delay=2, back_off=2) 85 | def get_tx_result(): 86 | # Returns the result of a transaction by transaction hash 87 | tx_result = icon_service.get_transaction_result(tx_hash) 88 | print("transaction status(1:success, 0:failure): ", tx_result["status"]) 89 | print("score address: ", tx_result["scoreAddress"]) 90 | print("waiting a second for accepting score...\n") 91 | 92 | get_tx_result() 93 | -------------------------------------------------------------------------------- /quickstart/examples/icx_transaction_example.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | from pprint import pprint 16 | from time import time 17 | 18 | from iconsdk.builder.call_builder import CallBuilder 19 | from iconsdk.builder.transaction_builder import TransactionBuilder 20 | from iconsdk.exception import JSONRPCException 21 | from iconsdk.icon_service import IconService 22 | from iconsdk.providers.http_provider import HTTPProvider 23 | from iconsdk.signed_transaction import SignedTransaction 24 | from iconsdk.utils.convert_type import convert_hex_str_to_int 25 | from iconsdk.wallet.wallet import KeyWallet 26 | from quickstart.examples.test.constant import BASE_DOMAIN_URL_V3_FOR_TEST, PRIVATE_KEY_FOR_TEST, GOVERNANCE_ADDRESS, VERSION_FOR_TEST 27 | from quickstart.examples.util.repeater import retry 28 | 29 | # Loads a wallet from a key store file 30 | # Wallet for sending ICX 31 | wallet1 = KeyWallet.load(PRIVATE_KEY_FOR_TEST) 32 | print("[wallet1] address: ", wallet1.get_address(), " private key: ", wallet1.get_private_key()) 33 | 34 | # Generates a wallet 35 | # Wallet for receiving ICX 36 | wallet2 = KeyWallet.create() 37 | print("[wallet2] address: ", wallet2.get_address(), " private key: ", wallet2.get_private_key()) 38 | 39 | icon_service = IconService(HTTPProvider(BASE_DOMAIN_URL_V3_FOR_TEST, VERSION_FOR_TEST)) 40 | 41 | 42 | # Returns a step cost. You can use it for getting the recommended value of 'step limit'. 43 | def get_default_step_cost(): 44 | _call = CallBuilder()\ 45 | .from_(wallet1.get_address())\ 46 | .to(GOVERNANCE_ADDRESS)\ 47 | .method("getStepCosts")\ 48 | .build() 49 | _result = icon_service.call(_call) 50 | default_step_cost = convert_hex_str_to_int(_result["default"]) 51 | return default_step_cost 52 | 53 | 54 | # Generates an instance of transaction for sending icx. 55 | # nid(network id); 1:mainnet, 2~:etc 56 | transaction = TransactionBuilder()\ 57 | .from_(wallet1.get_address())\ 58 | .to(wallet2.get_address())\ 59 | .value(10000)\ 60 | .step_limit(get_default_step_cost()) \ 61 | .nid(3) \ 62 | .nonce(2) \ 63 | .version(3) \ 64 | .timestamp(int(time() * 10 ** 6))\ 65 | .build() 66 | 67 | # Returns the signed transaction object having a signature 68 | signed_transaction = SignedTransaction(transaction, wallet1) 69 | 70 | # Reads params to transfer to nodes 71 | print("\nparams: ") 72 | pprint(signed_transaction.signed_transaction_dict) 73 | 74 | # Sends the transaction 75 | tx_hash = icon_service.send_transaction(signed_transaction) 76 | print("txHash: ", tx_hash) 77 | 78 | 79 | @retry(JSONRPCException, tries=10, delay=1, back_off=2) 80 | def get_tx_result(): 81 | # Returns the result of a transaction by transaction hash 82 | tx_result = icon_service.get_transaction_result(tx_hash) 83 | print("\ntransaction status(1:success, 0:failure): ", tx_result["status"]) 84 | 85 | # Gets balance 86 | balance = icon_service.get_balance(wallet2.get_address()) 87 | print("balance: ", balance, "\n") 88 | 89 | 90 | get_tx_result() 91 | 92 | -------------------------------------------------------------------------------- /quickstart/examples/sample_data/sample_token.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/icon-sdk-python/3002f8270caf4bfe57c84d16608c63e6e236b749/quickstart/examples/sample_data/sample_token.zip -------------------------------------------------------------------------------- /quickstart/examples/sample_data/standard_token.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/icon-sdk-python/3002f8270caf4bfe57c84d16608c63e6e236b749/quickstart/examples/sample_data/standard_token.zip -------------------------------------------------------------------------------- /quickstart/examples/sync_block_example.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | from time import sleep 16 | 17 | from iconsdk.builder.call_builder import CallBuilder 18 | from iconsdk.icon_service import IconService 19 | from iconsdk.providers.http_provider import HTTPProvider 20 | from iconsdk.utils.convert_type import convert_hex_str_to_int 21 | from iconsdk.wallet.wallet import KeyWallet 22 | from quickstart.examples.test.constant import BASE_DOMAIN_URL_V3_FOR_TEST, VERSION_FOR_TEST 23 | from quickstart.examples.util.repeater import RepeatedTimer 24 | 25 | icon_service = IconService(HTTPProvider(BASE_DOMAIN_URL_V3_FOR_TEST, VERSION_FOR_TEST)) 26 | wallet = KeyWallet.load("./test/test_keystore", "abcd1234*") 27 | 28 | 29 | def scan_forward_block(_time: int=100000, _interval: int=1): 30 | """Scans forward block""" 31 | # Checks the last block height 32 | pre_last_height = icon_service.get_block("latest")["height"] 33 | print(f"Starts to scan forward block at block height({pre_last_height})") 34 | 35 | def find_new_block(): 36 | """Finds generating new block since the pre last block and prints it""" 37 | # Returns the last block 38 | last_block = icon_service.get_block("latest") 39 | last_height = last_block["height"] 40 | nonlocal pre_last_height 41 | if last_height > pre_last_height: 42 | for height in range(pre_last_height + 1, last_height + 1): 43 | print("="*100) 44 | print("block height: ", height) 45 | block = icon_service.get_block(height) 46 | get_financial_transaction(block) 47 | pre_last_height = last_height 48 | 49 | # Starts repeated timer 50 | try: 51 | rt = RepeatedTimer(_interval, find_new_block) # It auto-starts, no need of rt.start() 52 | sleep(_time) 53 | finally: 54 | rt.stop() 55 | 56 | 57 | def get_financial_transaction(block): 58 | """ 59 | Gets specific meaningful transactions and prints it. 60 | 61 | [What is the specific meaningful transaction] 62 | - First, finds ICX transactions on a block. 63 | - Second, finds token transfer on a block. """ 64 | # Returns the transaction list. 65 | tx_list = block["confirmed_transaction_list"] 66 | 67 | if len(tx_list) > 0: 68 | for tx in tx_list: 69 | print("\ntxHash:", tx["txHash"]) 70 | tx_result = icon_service.get_transaction_result(tx["txHash"]) 71 | 72 | # Finds ICX transaction 73 | if "value" in tx and tx["value"] > 0: 74 | print("[ICX]") 75 | print("status: ", tx_result["status"]) 76 | print("from : ", tx["from"]) 77 | print("to : ", tx["to"]) 78 | print("amount: ", tx["value"]) 79 | 80 | # Finds token transfer 81 | if "dataType" in tx and tx["dataType"] == "call" and "method" in tx["data"] and tx["data"]["method"] == "transfer": 82 | score_address = tx["to"] 83 | print(f"[{get_token_name(score_address)} Token({get_token_symbol(score_address)})]") 84 | print("status: ", tx_result["status"]) 85 | print("from : ", tx["from"]) 86 | print("to : ", tx["data"]["params"]["_to"]) 87 | print("amount: ", convert_hex_str_to_int(tx["data"]["params"]["_value"])) 88 | 89 | 90 | def get_token_name(token_address: str): 91 | """ 92 | Gets the token name 93 | 94 | If not have the external method `name` to get the score name, 95 | it will raise JSONRPCException. 96 | """ 97 | call = CallBuilder()\ 98 | .from_(wallet.get_address())\ 99 | .to(token_address)\ 100 | .method("name")\ 101 | .build() 102 | return icon_service.call(call) 103 | 104 | 105 | def get_token_symbol(token_address: str): 106 | """ 107 | Gets the token symbol 108 | 109 | If not have the external method `symbol` to get the score symbol, 110 | it will raise JSONRPCException. 111 | """ 112 | call = CallBuilder()\ 113 | .from_(wallet.get_address())\ 114 | .to(token_address)\ 115 | .method("symbol")\ 116 | .build() 117 | return icon_service.call(call) 118 | 119 | 120 | scan_forward_block(1000, 1) 121 | 122 | -------------------------------------------------------------------------------- /quickstart/examples/test/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | -------------------------------------------------------------------------------- /quickstart/examples/test/constant.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | from tests.example_config import BASE_DOMAIN_URL_V3_FOR_TEST, PRIVATE_KEY_FOR_TEST, VERSION_FOR_TEST 17 | 18 | GOVERNANCE_ADDRESS = "cx0000000000000000000000000000000000000001" 19 | SCORE_INSTALL_ADDRESS = "cx0000000000000000000000000000000000000000" 20 | SCORE_ADDRESS = "cxa755b2ef6eb46c1e817c636be3c21d26c81fe6cc" 21 | -------------------------------------------------------------------------------- /quickstart/examples/token_transaction_example.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | from pprint import pprint 16 | 17 | from iconsdk.builder.call_builder import CallBuilder 18 | from iconsdk.builder.transaction_builder import CallTransactionBuilder 19 | from iconsdk.exception import JSONRPCException 20 | from iconsdk.icon_service import IconService 21 | from iconsdk.providers.http_provider import HTTPProvider 22 | from iconsdk.signed_transaction import SignedTransaction 23 | from iconsdk.utils.convert_type import convert_hex_str_to_int 24 | from iconsdk.wallet.wallet import KeyWallet 25 | from quickstart.examples.test.constant import ( 26 | BASE_DOMAIN_URL_V3_FOR_TEST, 27 | PRIVATE_KEY_FOR_TEST, 28 | GOVERNANCE_ADDRESS, 29 | SCORE_ADDRESS, 30 | VERSION_FOR_TEST 31 | ) 32 | from quickstart.examples.util.repeater import retry 33 | 34 | 35 | # Returns a step cost. You can use it for getting the recommended value of 'step limit'. 36 | def get_default_step_cost(): 37 | _call = CallBuilder() \ 38 | .from_(wallet1.get_address()) \ 39 | .to(GOVERNANCE_ADDRESS) \ 40 | .method("getStepCosts") \ 41 | .build() 42 | _result = icon_service.call(_call) 43 | default_step_cost = convert_hex_str_to_int(_result["default"]) 44 | return default_step_cost 45 | 46 | 47 | icon_service = IconService(HTTPProvider(BASE_DOMAIN_URL_V3_FOR_TEST, VERSION_FOR_TEST)) 48 | 49 | wallet1 = KeyWallet.load(PRIVATE_KEY_FOR_TEST) 50 | 51 | # Loads a wallet from a key store file. 52 | wallet2 = KeyWallet.load("./test/test_keystore", "abcd1234*") 53 | print("[wallet1] address: ", wallet1.get_address(), " private key: ", wallet1.get_private_key()) 54 | 55 | params = {"_to": wallet2.get_address(), "_value": 10} 56 | 57 | # Enters transaction information. 58 | call_transaction = CallTransactionBuilder() \ 59 | .from_(wallet1.get_address()) \ 60 | .to(SCORE_ADDRESS) \ 61 | .step_limit(get_default_step_cost() * 2) \ 62 | .nid(3) \ 63 | .nonce(4) \ 64 | .method("transfer") \ 65 | .params(params) \ 66 | .build() 67 | 68 | # Returns the signed transaction object having a signature 69 | signed_transaction = SignedTransaction(call_transaction, wallet1) 70 | 71 | # Reads params to transfer to nodes 72 | print("params:") 73 | pprint(signed_transaction.signed_transaction_dict) 74 | 75 | # Sends transaction 76 | tx_hash = icon_service.send_transaction(signed_transaction) 77 | print("txHash: ", tx_hash) 78 | 79 | 80 | @retry(JSONRPCException, tries=10, delay=1, back_off=2) 81 | def get_tx_result(): 82 | # Returns the result of a transaction by transaction hash 83 | tx_result = icon_service.get_transaction_result(tx_hash) 84 | print("transaction status(1:success, 0:failure): ", tx_result["status"]) 85 | 86 | 87 | get_tx_result() 88 | 89 | params = { 90 | "_owner": wallet2.get_address() 91 | } 92 | 93 | call = CallBuilder() \ 94 | .from_(wallet1.get_address()) \ 95 | .to(SCORE_ADDRESS) \ 96 | .method("balanceOf") \ 97 | .params(params) \ 98 | .build() 99 | 100 | result = icon_service.call(call) 101 | print("balance: ", convert_hex_str_to_int(result)) 102 | -------------------------------------------------------------------------------- /quickstart/examples/util/repeater.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | from threading import Timer 16 | from functools import wraps 17 | from time import sleep 18 | from logging import Logger 19 | 20 | 21 | class RepeatedTimer(object): 22 | def __init__(self, interval, func, *args, **kwargs): 23 | self._timer = None 24 | self.interval = interval 25 | self.func = func 26 | self.args = args 27 | self.kwargs = kwargs 28 | self.is_running = False 29 | self.result = None 30 | self.start() 31 | 32 | def _run(self): 33 | self.is_running = False 34 | self.start() 35 | self.result = self.func(*self.args, **self.kwargs) 36 | 37 | def start(self): 38 | if not self.is_running: 39 | self._timer = Timer(self.interval, self._run) 40 | self._timer.start() 41 | self.is_running = True 42 | 43 | def stop(self): 44 | self._timer.cancel() 45 | self.is_running = False 46 | 47 | def get(self): 48 | return self.result 49 | 50 | 51 | def retry(exception_to_check: Exception or tuple, tries: int =10, delay: int =1, back_off: int=2, logger: Logger=None): 52 | """ 53 | Retry calling the decorated function using an exponential backoff. 54 | 55 | http://www.saltycrane.com/blog/2009/11/trying-out-retry-decorator-python/ 56 | original from: http://wiki.python.org/moin/PythonDecoratorLibrary#Retry 57 | 58 | :param exception_to_check: The exception to check. May be a tuple of exceptions to check 59 | :param tries: Number of times to try (not retry) before giving up 60 | :param delay: Initial delay between retries in seconds 61 | :param back_off: Back_off multiplier e.g. Value of 2 will double the delay each retry 62 | :param logger: Logger to use. If None, print 63 | """ 64 | def deco_retry(f): 65 | @wraps(f) 66 | def f_retry(*args, **kwargs): 67 | mtries, mdelay = tries, delay 68 | while mtries > 1: 69 | try: 70 | return f(*args, **kwargs) 71 | except exception_to_check as e: 72 | msg = "%s, Retrying in %d seconds..." % (str(e), mdelay) 73 | if logger: 74 | logger.warning(msg) 75 | else: 76 | print(msg) 77 | sleep(mdelay) 78 | mtries -= 1 79 | mdelay *= back_off 80 | return f(*args, **kwargs) 81 | return f_retry # true decorator 82 | return deco_retry 83 | 84 | 85 | -------------------------------------------------------------------------------- /quickstart/examples/wallet_example.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | from os import path, remove 16 | 17 | from iconsdk.wallet.wallet import KeyWallet 18 | from quickstart.examples.test.constant import PRIVATE_KEY_FOR_TEST 19 | 20 | # Generates a wallet 21 | wallet1 = KeyWallet.create() 22 | print("[wallet1] address: ", wallet1.get_address(), " private key: ", wallet1.get_private_key()) 23 | 24 | wallet2 = KeyWallet.load(PRIVATE_KEY_FOR_TEST) 25 | print("[wallet2] address: ", wallet2.get_address(), " private key: ", wallet2.get_private_key()) 26 | 27 | # Removes the file if exists 28 | file_path = "./test/test_keystore" 29 | if path.isfile(file_path): 30 | remove(file_path) 31 | 32 | # Stores a key store file on the file path 33 | wallet1.store(file_path, "abcd1234*") 34 | 35 | # Loads a wallet from a key store file 36 | wallet1 = KeyWallet.load("./test/test_keystore", "abcd1234*") 37 | print("[wallet1] address: ", wallet1.get_address(), " private key: ", wallet1.get_private_key()) 38 | 39 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | eth-keyfile~=0.6.0 2 | coincurve~=18.0.0 3 | multimethod~=1.9.1 4 | requests~=2.32.0 5 | requests-mock~=1.10.0 6 | websocket-client~=1.5.1 7 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | universal = 0 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | from iconsdk.version import __version__ 3 | 4 | with open("README.md", 'r') as f: 5 | long_description = f.read() 6 | 7 | with open("requirements.txt") as requirements: 8 | requires = list(requirements) 9 | 10 | extras_requires = { 11 | 'tests': ['pytest~=7.2.1'] 12 | } 13 | 14 | setup( 15 | name='iconsdk', 16 | version=__version__, 17 | description='ICON SDK for Python is a collection of libraries which allow you to interact ' 18 | 'with a local or remote ICON node using an HTTP connection.', 19 | long_description=long_description, 20 | long_description_content_type='text/markdown', 21 | author='ICON Foundation', 22 | author_email='foo@icon.foundation', 23 | url='https://github.com/icon-project/icon-sdk-python', 24 | packages=find_packages(exclude=['tests*']), 25 | install_requires=requires, 26 | extras_require=extras_requires, 27 | python_requires='>=3.8', 28 | license='Apache License 2.0', 29 | classifiers=[ 30 | 'Development Status :: 5 - Production/Stable', 31 | 'Intended Audience :: Developers', 32 | 'Intended Audience :: System Administrators', 33 | 'Natural Language :: English', 34 | 'License :: OSI Approved :: Apache Software License', 35 | 'Programming Language :: Python :: 3', 36 | 'Programming Language :: Python :: 3 :: Only', 37 | 'Programming Language :: Python :: 3.8', 38 | 'Programming Language :: Python :: 3.9', 39 | 'Programming Language :: Python :: 3.10', 40 | 'Programming Language :: Python :: 3.11', 41 | ] 42 | ) 43 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | -------------------------------------------------------------------------------- /tests/api_call/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | -------------------------------------------------------------------------------- /tests/api_call/test_call.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | import json 16 | from unittest import main 17 | from unittest.mock import patch 18 | 19 | import requests_mock 20 | 21 | from iconsdk.builder.call_builder import CallBuilder 22 | from tests.api_send.test_send_super import TestSendSuper 23 | 24 | 25 | @patch('iconsdk.providers.http_provider.HTTPProvider._make_id', return_value=1234) 26 | class TestCall(TestSendSuper): 27 | 28 | def test_call(self, _make_id): 29 | 30 | # with from 31 | test_call = CallBuilder() \ 32 | .from_(self.setting["from"]) \ 33 | .to(self.setting["to"]) \ 34 | .method("getStepCosts") \ 35 | .params({}) \ 36 | .build() 37 | 38 | with requests_mock.Mocker() as m: 39 | expected_request = { 40 | "jsonrpc": "2.0", 41 | "method": "icx_call", 42 | "id": 1234, 43 | "params": { 44 | "to": self.setting["to"], 45 | "dataType": "call", 46 | "data": { 47 | "method": "getStepCosts" 48 | }, 49 | "from": self.setting["from"] 50 | } 51 | } 52 | 53 | m.post(self.matcher, json=response_json) 54 | result = self.icon_service.call(test_call) 55 | actual_request = json.loads(m._adapter.last_request.text) 56 | self.assertEqual(expected_request, actual_request) 57 | self.assertTrue(result) 58 | 59 | def test_call_without_params(self, _make_id): 60 | test_call = CallBuilder() \ 61 | .from_(self.setting["from"]) \ 62 | .to(self.setting["to"]) \ 63 | .method("getStepCosts") \ 64 | .build() 65 | 66 | with requests_mock.Mocker() as m: 67 | expected_request = { 68 | "jsonrpc": "2.0", 69 | "method": "icx_call", 70 | "id": 1234, 71 | "params": { 72 | "to": self.setting["to"], 73 | "dataType": "call", 74 | "data": { 75 | "method": "getStepCosts" 76 | }, 77 | "from": self.setting["from"] 78 | } 79 | } 80 | m.post(self.matcher, json=response_json) 81 | result = self.icon_service.call(test_call) 82 | actual_request = json.loads(m._adapter.last_request.text) 83 | self.assertEqual(expected_request, actual_request) 84 | self.assertTrue(result) 85 | 86 | def test_call_without_from(self, _make_id): 87 | test_call = CallBuilder() \ 88 | .to(self.setting["to"]) \ 89 | .method("getStepCosts") \ 90 | .build() 91 | 92 | with requests_mock.Mocker() as m: 93 | expected_request = { 94 | "jsonrpc": "2.0", 95 | "method": "icx_call", 96 | "id": 1234, 97 | "params": { 98 | "to": self.setting["to"], 99 | "dataType": "call", 100 | "data": { 101 | "method": "getStepCosts" 102 | }, 103 | } 104 | } 105 | m.post(self.matcher, json=response_json) 106 | result = self.icon_service.call(test_call) 107 | actual_request = json.loads(m._adapter.last_request.text) 108 | self.assertEqual(expected_request, actual_request) 109 | self.assertTrue(result) 110 | 111 | 112 | response_json = { 113 | "jsonrpc": "2.0", 114 | "result": { 115 | "default": "0x186a0", 116 | "contractCall": "0x61a8", 117 | "contractCreate": "0x3b9aca00", 118 | "contractUpdate": "0x5f5e1000", 119 | "contractDestruct": "-0x11170", 120 | "contractSet": "0x7530", 121 | "get": "0x0", 122 | "set": "0x140", 123 | "replace": "0x50", 124 | "delete": "-0xf0", 125 | "input": "0xc8", 126 | "eventLog": "0x64", 127 | "apiCall": "0x2710" 128 | }, 129 | "id": 1234 130 | } 131 | 132 | 133 | if __name__ == "__main__": 134 | main() 135 | -------------------------------------------------------------------------------- /tests/api_debug/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | -------------------------------------------------------------------------------- /tests/api_debug/test_get_trace.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2021 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | import json 16 | from unittest import main 17 | from unittest.mock import patch 18 | 19 | import requests_mock 20 | 21 | from iconsdk.exception import DataTypeException 22 | from tests.api_send.test_send_super import TestSendSuper 23 | 24 | 25 | @patch('iconsdk.providers.http_provider.HTTPProvider._make_id', return_value=1234) 26 | class TestGetTrace(TestSendSuper): 27 | TX_HASH = "0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238" 28 | 29 | def test_get_trace(self, _make_id): 30 | with requests_mock.Mocker() as m: 31 | expected_request = { 32 | 'id': 1234, 33 | 'jsonrpc': '2.0', 34 | 'method': 'debug_getTrace', 35 | 'params': { 36 | 'txHash': TestGetTrace.TX_HASH 37 | } 38 | } 39 | response_json = { 40 | "jsonrpc": "2.0", 41 | "result": {"logs": [], "status": "0x1"}, 42 | "id": 1234 43 | } 44 | m.post(self.matcher, json=response_json) 45 | self.icon_service.get_trace(TestGetTrace.TX_HASH) 46 | actual_request = json.loads(m._adapter.last_request.text) 47 | self.assertEqual(expected_request, actual_request) 48 | 49 | def test_get_trace_invalid(self, _make_id): 50 | tx_hash = TestGetTrace.TX_HASH 51 | self.assertRaises(DataTypeException, self.icon_service.get_trace, tx_hash[2:]) 52 | self.assertRaises(DataTypeException, self.icon_service.get_trace, "123") 53 | self.assertRaises(DataTypeException, self.icon_service.get_trace, 123) 54 | self.assertRaises(DataTypeException, self.icon_service.get_trace, tx_hash[:len(tx_hash)-1]) 55 | 56 | 57 | if __name__ == "__main__": 58 | main() 59 | -------------------------------------------------------------------------------- /tests/api_full_response/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | -------------------------------------------------------------------------------- /tests/api_full_response/example_response.py: -------------------------------------------------------------------------------- 1 | result_success_v3 = {'id': 1234, 2 | 'jsonrpc': '2.0', 3 | 'result': {} 4 | } 5 | 6 | result_error_v3 = {'id': 1234, 7 | 'jsonrpc': '2.0', 8 | 'error': {} 9 | } 10 | -------------------------------------------------------------------------------- /tests/api_full_response/test_full_response_base.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | from tests.api_send.test_send_super import TestSendSuper 17 | 18 | 19 | class TestFullResponseBase(TestSendSuper): 20 | @classmethod 21 | def setUpClass(cls): 22 | super().setUpClass() 23 | cls.block: dict = { 24 | "version": "0.5", 25 | "height": 6226175, 26 | "signature": "pZc4zwOLky4JzSf9bxrY+UmMc3M/BM8v0/YrkmI5VyFiwoOXX5tXe+sp66Iz/dtFtYVBKgo0gZfIL3PSUROrMgE=", 27 | "prev_block_hash": "146eee45320f4eb7e0eeae2a7c3f7c5b70c2780383927964eb0386ae235ba6c0", 28 | "merkle_tree_root_hash": "9c9993ebf71cb4d4184b564ba3425a1535e65c9ae4029686d5976b2410f55c17", 29 | "time_stamp": 1598939091830275, 30 | "confirmed_transaction_list": [ 31 | { 32 | "version": "0x3", 33 | "timestamp": "0x5ae3a04960603", 34 | "dataType": "base", 35 | "data": { 36 | "prep": { 37 | "irep": "0xa968163f0a57b400000", 38 | "rrep": "0x449", 39 | "totalDelegation": "0x18d92fa3589e6608f707c9", 40 | "value": "0x24a9c0534f5126dc" 41 | }, 42 | "result": { 43 | "coveredByFee": "0x0", 44 | "coveredByOverIssuedICX": "0x0", 45 | "issue": "0x24a9c0534f5126dc" 46 | } 47 | }, 48 | "txHash": "0x9c9993ebf71cb4d4184b564ba3425a1535e65c9ae4029686d5976b2410f55c17" 49 | } 50 | ], 51 | "block_hash": "3c9d91c2ea42d6b20edd39093a5c4c6f332fa9cb381c29533399365e4c07916d", 52 | "peer_id": "hxd3be921dfe193cd49ed7494a53743044e3376cd3", 53 | "next_leader": "hxd3be921dfe193cd49ed7494a53743044e3376cd3" 54 | } 55 | 56 | cls.transaction = { 57 | "version": "0x3", 58 | "from": cls.setting["from"], 59 | "to": cls.setting["to"], 60 | "stepLimit": hex(cls.setting["step_limit"]), 61 | "value": "0x470de4df820000", 62 | "nid": hex(cls.setting["nid"]), 63 | "timestamp": "1517999520286000", 64 | "signature": "sILBL1MPwOou8ItM4s0Vqx21l62QyucgTLsEQ51BGi5v/IJ1hOCT/P/rz1V1pDSGAnTQ7rGw9rSOVM5TAGbJOAE=", 65 | "method": "icx_sendTransaction", 66 | "txHash": "0x33db06f38424207daa69c9df153649fd3913c21e162f16f4839c9c3318e44388", 67 | "txIndex": "0x0", 68 | "blockHeight": "0xa", 69 | "blockHash": "0x9a39a75d7075687f746d61191baf1a1ff3b5bc0acc4a8df0bb872e53e13cdc17" 70 | } 71 | 72 | cls.receipt = { 73 | "txHash": "0x33db06f38424207daa69c9df153649fd3913c21e162f16f4839c9c3318e44388", 74 | "blockHeight": "0x13f", 75 | "blockHash": "0x069e8a2431ae2c7e55924af477be87518476aa1eb1b2e7d1ee8d61d7874ea907", 76 | "txIndex": "0x1", 77 | "to": "cx0000000000000000000000000000000000000000", 78 | "stepUsed": "0x263b8", 79 | "stepPrice": "0x2540be400", 80 | "cumulativeStepUsed": "0x263b8", 81 | "eventLogs": [ 82 | { 83 | "scoreAddress": "cx0000000000000000000000000000000000000000", 84 | "indexed": [ 85 | "PRepSet(Address)" 86 | ], 87 | "data": [ 88 | "hx86aba2210918a9b116973f3c4b27c41a54d5dafe" 89 | ] 90 | } 91 | ], 92 | "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000080000000000000000000000000000000000000000000000000000000000020000000000000008000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 93 | "status": "0x1" 94 | } 95 | 96 | cls.block_hash = "0x3c9d91c2ea42d6b20edd39093a5c4c6f332fa9cb381c29533399365e4c07916d" 97 | cls.block_height = 6226175 98 | cls.transaction_hash = "0x33db06f38424207daa69c9df153649fd3913c21e162f16f4839c9c3318e44388" 99 | -------------------------------------------------------------------------------- /tests/api_full_response/test_get_balance.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | import json 17 | from unittest import main 18 | from unittest.mock import patch 19 | 20 | import requests_mock 21 | 22 | from iconsdk.exception import AddressException 23 | from tests.api_full_response.example_response import result_success_v3 24 | from tests.api_full_response.test_full_response_base import TestFullResponseBase 25 | 26 | 27 | @patch('iconsdk.providers.http_provider.HTTPProvider._make_id', return_value=1234) 28 | class TestFullResponseGetBalance(TestFullResponseBase): 29 | 30 | def test_get_balance_full_response(self, _make_id): 31 | 32 | # get_balance with full_response 33 | with requests_mock.Mocker() as m: 34 | expected_request = { 35 | 'jsonrpc': '2.0', 36 | 'method': 'icx_getBalance', 37 | 'id': 1234, 38 | 'params': { 39 | 'address': self.setting['from'] 40 | } 41 | } 42 | response_json = { 43 | 'jsonrpc': '2.0', 44 | 'result': hex(self.setting['value']), 45 | 'id': 1234 46 | } 47 | 48 | m.post(self.matcher, json=response_json) 49 | result_dict = self.icon_service.get_balance(self.setting['from'], full_response=True) 50 | actual_request = json.loads(m._adapter.last_request.text) 51 | result_keys = result_dict.keys() 52 | result_content = result_dict['result'] 53 | 54 | self.assertEqual(expected_request, actual_request) 55 | self.assertEqual(result_success_v3.keys(), result_keys) 56 | self.assertEqual(int(result_content, 16), self.setting['value']) 57 | 58 | def test_get_balance_invalid(self, _make_id): 59 | # case 1: when a param is wrong. 60 | self.assertRaises(AddressException, self.icon_service.get_balance, self.setting["to"][2:]) 61 | self.assertRaises(AddressException, self.icon_service.get_balance, self.setting["from"][2:]) 62 | self.assertRaises(AddressException, self.icon_service.get_balance, "123") 63 | self.assertRaises(AddressException, self.icon_service.get_balance, 123) 64 | # when the address's length is short 65 | self.assertRaises(AddressException, self.icon_service.get_balance, "cx882efc17c2f50e0d60142b9c0e746cbafb569d") 66 | 67 | 68 | if __name__ == '__main__': 69 | main() 70 | -------------------------------------------------------------------------------- /tests/api_full_response/test_get_block_by_hash.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | import json 17 | from unittest import main 18 | from unittest.mock import patch 19 | 20 | import requests_mock 21 | 22 | from iconsdk.utils.validation import is_block 23 | from tests.api_full_response.example_response import result_error_v3, result_success_v3 24 | from tests.api_full_response.test_full_response_base import TestFullResponseBase 25 | 26 | 27 | @patch('iconsdk.providers.http_provider.HTTPProvider._make_id', return_value=1234) 28 | class TestFullResponseGetBlockByHash(TestFullResponseBase): 29 | 30 | def test_get_block_by_hash_full_response(self, _make_id): 31 | # used valid hash and got and valid block 32 | with requests_mock.Mocker() as m: 33 | expected_request = { 34 | 'jsonrpc': '2.0', 35 | 'method': 'icx_getBlockByHash', 36 | 'id': 1234, 37 | 'params': { 38 | 'hash': self.block_hash 39 | } 40 | } 41 | 42 | response_json = { 43 | 'jsonrpc': '2.0', 44 | 'result': self.block, 45 | 'id': 1234 46 | } 47 | 48 | m.post(self.matcher, json=response_json) 49 | result_dict = self.icon_service.get_block(self.block_hash, full_response=True) 50 | actual_request = json.loads(m._adapter.last_request.text) 51 | result_keys = result_dict.keys() 52 | result_content = result_dict['result'] 53 | 54 | self.assertEqual(expected_request, actual_request) 55 | self.assertEqual(result_success_v3.keys(), result_keys) 56 | self.assertTrue(is_block(result_content)) 57 | 58 | def test_get_block_by_wrong_hash(self, _make_id): 59 | # used invalid hash and got and invalid block 60 | 61 | invalid_block_hash = "0x033f8d96045eb8301fd17cf078c28ae58a3ba329f6ada5cf128ee56dc2af26f7" 62 | with requests_mock.Mocker() as m: 63 | response_json = { 64 | 'jsonrpc': '2.0', 65 | 'error': { 66 | "code": -32602, 67 | "message": "fail wrong block hash" 68 | }, 69 | 'id': 1234 70 | } 71 | 72 | m.post(self.matcher, json=response_json, status_code=400) 73 | result_dict = self.icon_service.get_block(invalid_block_hash, full_response=True) 74 | self.assertEqual(result_dict.keys(), result_error_v3.keys()) 75 | 76 | 77 | if __name__ == "__main__": 78 | main() -------------------------------------------------------------------------------- /tests/api_full_response/test_get_block_by_height.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | import json 17 | from unittest import main 18 | from unittest.mock import patch 19 | 20 | import requests_mock 21 | 22 | from iconsdk.utils.validation import is_block 23 | from tests.api_full_response.example_response import result_error_v3, result_success_v3 24 | from tests.api_full_response.test_full_response_base import TestFullResponseBase 25 | 26 | 27 | @patch('iconsdk.providers.http_provider.HTTPProvider._make_id', return_value=1234) 28 | class TestFullResponseGetBlockByHeight(TestFullResponseBase): 29 | 30 | def test_get_block_by_height_full_response(self, _make_id): 31 | # used valid hash and got and valid block 32 | with requests_mock.Mocker() as m: 33 | expected_request = { 34 | 'jsonrpc': '2.0', 35 | 'method': 'icx_getBlockByHeight', 36 | 'id': 1234, 37 | 'params': { 38 | 'height': hex(self.block_height) 39 | } 40 | } 41 | 42 | response_json = { 43 | 'jsonrpc': '2.0', 44 | 'result': self.block, 45 | 'id': 1234 46 | } 47 | 48 | m.post(self.matcher, json=response_json) 49 | result_dict = self.icon_service.get_block(self.block_height, full_response=True) 50 | actual_request = json.loads(m._adapter.last_request.text) 51 | result_keys = result_dict.keys() 52 | result_content = result_dict['result'] 53 | 54 | self.assertEqual(expected_request, actual_request) 55 | self.assertEqual(result_success_v3.keys(), result_keys) 56 | self.assertTrue(is_block(result_content)) 57 | 58 | def test_get_block_by_wrong_height(self, _make_id): 59 | # used invalid hash and got and invalid block 60 | 61 | invalid_block_height = 5 62 | with requests_mock.Mocker() as m: 63 | response_json = { 64 | "jsonrpc": "2.0", 65 | "error": { 66 | "code": -32602, 67 | "message": "fail wrong block height" 68 | }, 69 | "id": 1234 70 | } 71 | 72 | m.post(self.matcher, json=response_json, status_code=400) 73 | result_dict = self.icon_service.get_block(invalid_block_height, full_response=True) 74 | self.assertEqual(result_dict.keys(), result_error_v3.keys()) 75 | 76 | 77 | if __name__ == "__main__": 78 | main() -------------------------------------------------------------------------------- /tests/api_full_response/test_get_last_block.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | import json 17 | from unittest import main 18 | from unittest.mock import patch 19 | 20 | import requests_mock 21 | 22 | from iconsdk.utils.validation import is_block 23 | from tests.api_full_response.example_response import result_success_v3 24 | from tests.api_full_response.test_full_response_base import TestFullResponseBase 25 | 26 | 27 | @patch('iconsdk.providers.http_provider.HTTPProvider._make_id', return_value=1234) 28 | class TestFullResponseGetBlockByHeight(TestFullResponseBase): 29 | 30 | def test_get_last_block_full_response(self, _make_id): 31 | # used valid hash and got and valid block 32 | with requests_mock.Mocker() as m: 33 | expected_request = { 34 | 'jsonrpc': '2.0', 35 | 'method': 'icx_getLastBlock', 36 | 'id': 1234, 37 | } 38 | 39 | response_json = { 40 | 'jsonrpc': '2.0', 41 | 'result': self.block, 42 | 'id': 1234 43 | } 44 | 45 | m.post(self.matcher, json=response_json) 46 | result_dict = self.icon_service.get_block("latest", full_response=True) 47 | actual_request = json.loads(m._adapter.last_request.text) 48 | result_keys = result_dict.keys() 49 | result_content = result_dict['result'] 50 | 51 | self.assertEqual(expected_request, actual_request) 52 | self.assertEqual(result_success_v3.keys(), result_keys) 53 | self.assertTrue(is_block(result_content)) 54 | 55 | 56 | if __name__ == "__main__": 57 | main() -------------------------------------------------------------------------------- /tests/api_full_response/test_get_total_supply.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | import json 17 | from unittest import main 18 | from unittest.mock import patch 19 | 20 | import requests_mock 21 | 22 | from tests.api_full_response.example_response import result_success_v3 23 | from tests.api_full_response.test_full_response_base import TestFullResponseBase 24 | 25 | 26 | @patch('iconsdk.providers.http_provider.HTTPProvider._make_id', return_value=1234) 27 | class TesFullResponseGetTotalSupply(TestFullResponseBase): 28 | 29 | def test_get_total_supply(self, _make_id): 30 | with requests_mock.Mocker() as m: 31 | supply = 1_000_000_000 32 | expected_request = { 33 | 'id': 1234, 34 | 'jsonrpc': '2.0', 35 | 'method': 'icx_getTotalSupply', 36 | } 37 | 38 | response_json = { 39 | 'jsonrpc': '2.0', 40 | 'result': hex(supply), 41 | 'id': 1234 42 | } 43 | m.post(self.matcher, json=response_json) 44 | result_dict = self.icon_service.get_total_supply(full_response=True) 45 | actual_request = json.loads(m._adapter.last_request.text) 46 | result_content = result_dict['result'] 47 | 48 | self.assertEqual(expected_request, actual_request) 49 | self.assertEqual(result_success_v3.keys(), result_dict.keys()) 50 | self.assertEqual(int(result_content, 16), supply) 51 | 52 | 53 | if __name__ == "__main__": 54 | main() 55 | -------------------------------------------------------------------------------- /tests/api_full_response/test_get_transaction_by_hash.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | import json 16 | from unittest import main 17 | from unittest.mock import patch 18 | 19 | import requests_mock 20 | 21 | from iconsdk.utils.validation import is_transaction 22 | from tests.api_full_response.example_response import result_success_v3, result_error_v3 23 | from tests.api_full_response.test_full_response_base import TestFullResponseBase 24 | 25 | 26 | @patch('iconsdk.providers.http_provider.HTTPProvider._make_id', return_value=1234) 27 | class TestFullResponseGetTransactionByHash(TestFullResponseBase): 28 | 29 | def test_get_transaction_by_hash(self, _make_id): 30 | with requests_mock.Mocker() as m: 31 | expected_request = { 32 | 'id': 1234, 33 | 'jsonrpc': '2.0', 34 | 'method': 'icx_getTransactionByHash', 35 | 'params': { 36 | 'txHash': self.transaction_hash 37 | } 38 | } 39 | response_json = { 40 | "jsonrpc": "2.0", 41 | "result": self.transaction, 42 | "id": 1234 43 | } 44 | 45 | m.post(self.matcher, json=response_json) 46 | result_dict = self.icon_service.get_transaction(self.transaction_hash, full_response=True) 47 | actual_request = json.loads(m._adapter.last_request.text) 48 | result_content = result_dict['result'] 49 | 50 | self.assertEqual(expected_request, actual_request) 51 | self.assertEqual(result_success_v3.keys(), result_dict.keys()) 52 | self.assertTrue(is_transaction(result_content)) 53 | 54 | def test_get_transaction_wrong_hash(self, _make_id): 55 | with requests_mock.Mocker() as m: 56 | wrong_tx_hash = "0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238" 57 | response_json = { 58 | "jsonrpc": "2.0", 59 | "error": { 60 | "code": -32602, 61 | "message": "Invalid params txHash" 62 | }, 63 | "id": 1234 64 | } 65 | 66 | m.post(self.matcher, json=response_json, status_code=400) 67 | result_dict = self.icon_service.get_block(wrong_tx_hash, full_response=True) 68 | self.assertEqual(result_dict.keys(), result_error_v3.keys()) 69 | 70 | 71 | if __name__ == "__main__": 72 | main() 73 | -------------------------------------------------------------------------------- /tests/api_full_response/test_get_transaction_result.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | import json 17 | from unittest import main 18 | from unittest.mock import patch 19 | 20 | import requests_mock 21 | 22 | from iconsdk.utils.validation import is_transaction_result 23 | from tests.api_full_response.example_response import result_success_v3, result_error_v3 24 | from tests.api_full_response.test_full_response_base import TestFullResponseBase 25 | 26 | 27 | @patch('iconsdk.providers.http_provider.HTTPProvider._make_id', return_value=1234) 28 | class TestGetTransactionResult(TestFullResponseBase): 29 | def test_get_transaction_result(self, _make_id): 30 | with requests_mock.Mocker() as m: 31 | expected_request = { 32 | 'id': 1234, 33 | 'jsonrpc': '2.0', 34 | 'method': 'icx_getTransactionResult', 35 | 'params': { 36 | 'txHash': self.transaction_hash 37 | } 38 | } 39 | 40 | response_json = { 41 | "jsonrpc": "2.0", 42 | "result": self.receipt, 43 | "id": 1234 44 | } 45 | m.post(self.matcher, json=response_json) 46 | result_dict = self.icon_service.get_transaction_result(self.transaction_hash, full_response=True) 47 | actual_request = json.loads(m._adapter.last_request.text) 48 | result_content = result_dict['result'] 49 | 50 | self.assertEqual(expected_request, actual_request) 51 | self.assertEqual(result_success_v3.keys(), result_dict.keys()) 52 | self.assertTrue(is_transaction_result(result_content)) 53 | 54 | def test_get_transaction_result_wrong_hash(self, _make_id): 55 | wrong_tx_hash: str = "0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238" 56 | with requests_mock.Mocker() as m: 57 | response_json = { 58 | "jsonrpc": "2.0", 59 | "error": { 60 | "code": -32602, 61 | "message": "Invalid params txHash" 62 | }, 63 | "id": 1234 64 | } 65 | 66 | m.post(self.matcher, json=response_json, status_code=400) 67 | result_dict = self.icon_service.get_block(wrong_tx_hash, full_response=True) 68 | self.assertEqual(result_dict.keys(), result_error_v3.keys()) 69 | 70 | 71 | if __name__ == "__main__": 72 | main() 73 | -------------------------------------------------------------------------------- /tests/api_full_response/test_send_call.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | import json 17 | from unittest.mock import patch 18 | 19 | import requests_mock 20 | 21 | from iconsdk.builder.transaction_builder import CallTransactionBuilder 22 | from iconsdk.signed_transaction import SignedTransaction 23 | from iconsdk.utils.validation import is_T_HASH 24 | from tests.api_full_response.example_response import result_success_v3, result_error_v3 25 | from tests.api_full_response.test_full_response_base import TestFullResponseBase 26 | 27 | 28 | @patch('iconsdk.providers.http_provider.HTTPProvider._make_id', return_value=1234) 29 | class TesFullResponseSendDeploy(TestFullResponseBase): 30 | def test_send_call(self, _make_id): 31 | call_transaction_without_step_limit = CallTransactionBuilder()\ 32 | .from_(self.setting["from"])\ 33 | .to(self.setting["to"]) \ 34 | .nid(self.setting["nid"])\ 35 | .step_limit(self.setting["step_limit"])\ 36 | .nonce(self.setting["nonce"]) \ 37 | .method(self.setting["method"])\ 38 | .params(self.setting["params_call"])\ 39 | .build() 40 | signed_transaction = SignedTransaction(call_transaction_without_step_limit, self.wallet) 41 | 42 | with requests_mock.Mocker() as m: 43 | response_json = { 44 | 'jsonrpc': '2.0', 45 | 'id': 1234, 46 | 'result': '0x4bf74e6aeeb43bde5dc8d5b62537a33ac8eb7605ebbdb51b015c1881b45b3aed' 47 | } 48 | 49 | m.post(self.matcher, json=response_json) 50 | result_dict = self.icon_service.send_transaction(signed_transaction, full_response=True) 51 | actual_request = json.loads(m._adapter.last_request.text) 52 | result_content = result_dict['result'] 53 | 54 | self.assertTrue(is_T_HASH(result_content)) 55 | self.assertEqual(result_success_v3.keys(), result_dict.keys()) 56 | 57 | def test_send_call_wrong_address(self, _make_id): 58 | wrong_address = "hx5bfdb090f43a808005ffc27c25b213145e8" 59 | call_transaction_without_step_limit = CallTransactionBuilder() \ 60 | .from_(self.setting["from"]) \ 61 | .to(wrong_address) \ 62 | .nid(self.setting["nid"]) \ 63 | .step_limit(self.setting["step_limit"]) \ 64 | .nonce(self.setting["nonce"]) \ 65 | .method(self.setting["method"]) \ 66 | .params(self.setting["params_call"]) \ 67 | .build() 68 | signed_transaction = SignedTransaction(call_transaction_without_step_limit, self.wallet) 69 | 70 | with requests_mock.Mocker() as m: 71 | response_json = { 72 | 'jsonrpc': '2.0', 73 | 'id': 1234, 74 | 'error': { 75 | 'code': -32601, 76 | 'message': 'Method not found' 77 | } 78 | } 79 | 80 | m.post(self.matcher, json=response_json) 81 | result_dict = self.icon_service.send_transaction(signed_transaction, full_response=True) 82 | self.assertEqual(result_error_v3.keys(), result_dict.keys()) -------------------------------------------------------------------------------- /tests/api_full_response/test_send_deploy.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | import json 17 | from unittest import main 18 | from unittest.mock import patch 19 | 20 | import requests_mock 21 | 22 | from iconsdk.builder.transaction_builder import DeployTransactionBuilder 23 | from iconsdk.signed_transaction import SignedTransaction 24 | from iconsdk.utils.validation import is_T_HASH 25 | from tests.api_full_response.example_response import result_success_v3, result_error_v3 26 | from tests.api_full_response.test_full_response_base import TestFullResponseBase 27 | 28 | 29 | @patch('iconsdk.providers.http_provider.HTTPProvider._make_id', return_value=1234) 30 | class TesFullResponseSendDeploy(TestFullResponseBase): 31 | def test_send_deploy(self, _make_id): 32 | deploy_transaction = DeployTransactionBuilder() \ 33 | .from_(self.setting["from"]) \ 34 | .to(self.setting["to_install"]) \ 35 | .step_limit(self.setting["step_limit"]) \ 36 | .nid(self.setting["nid"]) \ 37 | .nonce(self.setting["nonce"]) \ 38 | .content_type(self.setting["content_type"]) \ 39 | .content(self.setting["content_install"]) \ 40 | .params(self.setting["params_install"]) \ 41 | .build() 42 | signed_transaction = SignedTransaction(deploy_transaction, self.wallet) 43 | 44 | with requests_mock.Mocker() as m: 45 | response_json = { 46 | "jsonrpc": "2.0", 47 | "id": 1234, 48 | "result": "0x4bf74e6aeeb43bde5dc8d5b62537a33ac8eb7605ebbdb51b015c1881b45b3aed" 49 | } 50 | 51 | m.post(self.matcher, json=response_json) 52 | result_dict = self.icon_service.send_transaction(signed_transaction, full_response=True) 53 | actual_request = json.loads(m._adapter.last_request.text) 54 | result_content = result_dict['result'] 55 | 56 | self.assertEqual(result_success_v3.keys(), result_dict.keys()) 57 | self.assertTrue(is_T_HASH(result_content)) 58 | 59 | def test_deploy_wrong_address(self, _make_id): 60 | wrong_address = "hx5bfdb090f43a808005ffc27c25b213145e8" 61 | deploy_transaction = DeployTransactionBuilder() \ 62 | .from_(self.setting["from"]) \ 63 | .to(wrong_address) \ 64 | .step_limit(self.setting["step_limit"]) \ 65 | .nid(self.setting["nid"]) \ 66 | .nonce(self.setting["nonce"]) \ 67 | .content_type(self.setting["content_type"]) \ 68 | .content(self.setting["content_install"]) \ 69 | .params(self.setting["params_install"]) \ 70 | .build() 71 | signed_transaction = SignedTransaction(deploy_transaction, self.wallet) 72 | 73 | with requests_mock.Mocker() as m: 74 | response_json = { 75 | 'jsonrpc': '2.0', 76 | 'id': 1234, 77 | 'error': { 78 | "code": -32600, 79 | "message": f'Not a system SCORE {wrong_address}' 80 | } 81 | } 82 | 83 | m.post(self.matcher, json=response_json, status_code=400) 84 | result_dict = self.icon_service.send_transaction(signed_transaction, full_response=True) 85 | self.assertEqual(result_error_v3.keys(), result_dict.keys()) 86 | 87 | 88 | if __name__ == '__main__': 89 | main() 90 | -------------------------------------------------------------------------------- /tests/api_full_response/test_send_deposit.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | import json 17 | from unittest import main 18 | from unittest.mock import patch 19 | 20 | import requests_mock 21 | 22 | from iconsdk.builder.transaction_builder import DepositTransactionBuilder 23 | from iconsdk.signed_transaction import SignedTransaction 24 | from iconsdk.utils.validation import is_T_HASH 25 | from tests.api_full_response.example_response import result_success_v3 26 | from tests.api_full_response.test_full_response_base import TestFullResponseBase 27 | 28 | 29 | @patch('iconsdk.providers.http_provider.HTTPProvider._make_id', return_value=1234) 30 | class TesFullResponseSendDeposit(TestFullResponseBase): 31 | def test_add_deposit(self, _make_id): 32 | # transaction instance for add action 33 | action = "add" 34 | deposit_transaction = DepositTransactionBuilder() \ 35 | .from_(self.setting["from"]) \ 36 | .to(self.setting["to"]) \ 37 | .value(self.setting["value"]) \ 38 | .timestamp(self.setting["timestamp"]) \ 39 | .step_limit(self.setting["step_limit"]) \ 40 | .nid(self.setting["nid"]) \ 41 | .nonce(self.setting["nonce"]) \ 42 | .action(action) \ 43 | .build() 44 | signed_transaction = SignedTransaction(deposit_transaction, self.wallet) 45 | 46 | with requests_mock.Mocker() as m: 47 | tx_hash = "0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238" 48 | expected_request = { 49 | 'id': 1234, 50 | 'jsonrpc': '2.0', 51 | 'method': 'icx_sendTransaction', 52 | 'params': { 53 | 'data': { 54 | 'action': action 55 | }, 56 | 'dataType': 'deposit', 57 | 'from': self.setting["from"], 58 | 'nid': hex(self.setting["nid"]), 59 | 'nonce': hex(self.setting["nonce"]), 60 | 'signature': signed_transaction.signed_transaction_dict["signature"], 61 | 'stepLimit': hex(self.setting["step_limit"]), 62 | 'timestamp': hex(self.setting["timestamp"]), 63 | 'to': self.setting["to"], 64 | 'value': hex(self.setting["value"]), 65 | 'version': hex(3) 66 | } 67 | } 68 | 69 | response_json = { 70 | "jsonrpc": "2.0", 71 | "result": tx_hash, 72 | "id": 1234 73 | } 74 | m.post(self.matcher, json=response_json) 75 | result_dict = self.icon_service.send_transaction(signed_transaction, full_response=True) 76 | actual_request = json.loads(m._adapter.last_request.text) 77 | result_content = result_dict['result'] 78 | 79 | self.assertEqual(expected_request, actual_request) 80 | self.assertEqual(result_success_v3.keys(), result_dict.keys()) 81 | self.assertTrue(is_T_HASH(result_content)) 82 | 83 | 84 | if __name__ == "__main__": 85 | main() 86 | -------------------------------------------------------------------------------- /tests/api_full_response/test_send_message.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | import json 17 | from unittest import main 18 | from unittest.mock import patch 19 | 20 | import requests_mock 21 | 22 | from iconsdk.builder.transaction_builder import MessageTransactionBuilder 23 | from iconsdk.signed_transaction import SignedTransaction 24 | from iconsdk.utils.validation import is_message_transaction, is_T_HASH 25 | from tests.api_full_response.example_response import result_success_v3 26 | from tests.api_full_response.test_full_response_base import TestFullResponseBase 27 | 28 | 29 | @patch('iconsdk.providers.http_provider.HTTPProvider._make_id', return_value=1234) 30 | class TestFullResponseSendMessage(TestFullResponseBase): 31 | 32 | def test_send_message(self, _make_id): 33 | message_transaction = MessageTransactionBuilder() \ 34 | .from_(self.setting["from"]) \ 35 | .to(self.setting["to"]) \ 36 | .step_limit(self.setting["step_limit"]) \ 37 | .nid(self.setting["nid"]) \ 38 | .nonce(self.setting["nonce"]) \ 39 | .data(self.setting["data"]) \ 40 | .timestamp(self.setting["timestamp"]) \ 41 | .build() 42 | 43 | tx_dict = SignedTransaction.convert_tx_to_jsonrpc_request(message_transaction) 44 | self.assertTrue(is_message_transaction(tx_dict)) 45 | signed_transaction = SignedTransaction(message_transaction, self.wallet) 46 | 47 | with requests_mock.Mocker() as m: 48 | tx_hash = "0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238" 49 | expected_request = { 50 | 'id': 1234, 51 | 'jsonrpc': '2.0', 52 | 'method': 'icx_sendTransaction', 53 | 'params': { 54 | 'data': self.setting["data"], 55 | 'dataType': 'message', 56 | 'from': self.setting["from"], 57 | 'nid': hex(self.setting["nid"]), 58 | 'nonce': hex(self.setting["nonce"]), 59 | 'timestamp': hex(self.setting["timestamp"]), 60 | 'signature': signed_transaction.signed_transaction_dict["signature"], 61 | 'stepLimit': hex(self.setting["step_limit"]), 62 | 'to': self.setting["to"], 63 | 'version': hex(3) 64 | } 65 | } 66 | 67 | response_json = { 68 | 'jsonrpc': '2.0', 69 | 'result': tx_hash, 70 | 'id': 1234 71 | } 72 | 73 | m.post(self.matcher, json=response_json) 74 | result_dict = self.icon_service.send_transaction(signed_transaction, full_response=True) 75 | actual_request = json.loads(m._adapter.last_request.text) 76 | result_content = result_dict['result'] 77 | 78 | self.assertTrue(is_T_HASH(result_content)) 79 | self.assertEqual(result_success_v3.keys(), result_dict.keys()) 80 | self.assertEqual(expected_request, actual_request) 81 | 82 | 83 | if __name__ == '__main__': 84 | main() -------------------------------------------------------------------------------- /tests/api_full_response/test_send_transfer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | import json 17 | from unittest import main 18 | from unittest.mock import patch 19 | 20 | import requests_mock 21 | 22 | from iconsdk.builder.transaction_builder import TransactionBuilder 23 | from iconsdk.signed_transaction import SignedTransaction 24 | from iconsdk.utils.validation import is_T_HASH 25 | from tests.api_full_response.example_response import result_success_v3 26 | from tests.api_full_response.test_full_response_base import TestFullResponseBase 27 | 28 | 29 | @patch('iconsdk.providers.http_provider.HTTPProvider._make_id', return_value=1234) 30 | class TestFullResponseSendTransfer(TestFullResponseBase): 31 | 32 | def test_send_transfer(self, _make_id): 33 | icx_transaction = TransactionBuilder() \ 34 | .from_(self.setting["from"]) \ 35 | .to(self.setting["to"]) \ 36 | .value(self.setting["value"]) \ 37 | .step_limit(self.setting["step_limit"]) \ 38 | .nid(3) \ 39 | .nonce(self.setting["nonce"]) \ 40 | .version(3) \ 41 | .timestamp(self.setting["timestamp"]) \ 42 | .build() 43 | 44 | signed_transaction = SignedTransaction(icx_transaction, self.wallet) 45 | 46 | with requests_mock.Mocker() as m: 47 | response_json: dict = { 48 | "jsonrpc": "2.0", 49 | "result": "0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238", 50 | "id": 1234 51 | } 52 | 53 | m.post(self.matcher, json=response_json) 54 | result_dict = self.icon_service.send_transaction(signed_transaction, full_response=True) 55 | actual_request = json.loads(m._adapter.last_request.text) 56 | result_content = result_dict['result'] 57 | 58 | self.assertTrue(is_T_HASH(result_content)) 59 | self.assertEqual(result_success_v3.keys(), result_dict.keys()) 60 | 61 | 62 | if __name__ == '__main__': 63 | main() 64 | -------------------------------------------------------------------------------- /tests/api_get/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | -------------------------------------------------------------------------------- /tests/api_get/test_btp2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2022 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | import json 16 | from unittest import main 17 | from unittest.mock import patch 18 | 19 | import requests_mock 20 | 21 | from tests.api_send.test_send_super import TestSendSuper 22 | 23 | 24 | @patch('iconsdk.providers.http_provider.HTTPProvider._make_id', return_value=1234) 25 | class TestBTP2(TestSendSuper): 26 | HEIGHT = 100 27 | ID = 1 28 | 29 | @staticmethod 30 | def convert_param(param: dict) -> dict: 31 | def _convert_key(k: str): 32 | if k.endswith("_id"): 33 | return k[:len(k)-3] + "ID" 34 | return k 35 | 36 | def _convert_value(v): 37 | if isinstance(v, int): 38 | return hex(v) 39 | return v 40 | 41 | result = {} 42 | for k, v in param.items(): 43 | result[_convert_key(k)] = _convert_value(v) 44 | return result 45 | 46 | def run_test(self, _make_id, func: callable, method: str, params: dict): 47 | with requests_mock.Mocker() as m: 48 | expected_request = { 49 | 'id': 1234, 50 | 'jsonrpc': '2.0', 51 | 'method': method, 52 | } 53 | response_json = { 54 | "jsonrpc": "2.0", 55 | "result": hex(0), 56 | "id": 1234 57 | } 58 | m.post(self.matcher, json=response_json) 59 | if params is not None: 60 | expected_request['params'] = self.convert_param(params) 61 | func(**params) 62 | else: 63 | func() 64 | actual_request = json.loads(m._adapter.last_request.text) 65 | self.assertEqual(expected_request, actual_request) 66 | 67 | def test_get_btp_network_info(self, _make_id): 68 | func = self.icon_service.get_btp_network_info 69 | method = "btp_getNetworkInfo" 70 | params = {'id': self.ID} 71 | self.run_test(_make_id, func, method, params) 72 | 73 | params['height'] = self.HEIGHT 74 | self.run_test(_make_id, func, method, params) 75 | 76 | def test_get_btp_network_type_info(self, _make_id): 77 | func = self.icon_service.get_btp_network_type_info 78 | method = "btp_getNetworkTypeInfo" 79 | params = {'id': self.ID} 80 | self.run_test(_make_id, func, method, params) 81 | 82 | params['height'] = self.HEIGHT 83 | self.run_test(_make_id, func, method, params) 84 | 85 | def test_get_btp_messages(self, _make_id): 86 | func = self.icon_service.get_btp_messages 87 | method = "btp_getMessages" 88 | params = { 89 | 'height': self.HEIGHT, 90 | 'network_id': self.ID, 91 | } 92 | self.run_test(_make_id, func, method, params) 93 | 94 | def test_get_btp_header(self, _make_id): 95 | func = self.icon_service.get_btp_header 96 | method = "btp_getHeader" 97 | params = { 98 | 'height': self.HEIGHT, 99 | 'network_id': self.ID, 100 | } 101 | self.run_test(_make_id, func, method, params) 102 | 103 | def test_get_btp_proof(self, _make_id): 104 | func = self.icon_service.get_btp_proof 105 | method = "btp_getProof" 106 | params = { 107 | 'height': self.HEIGHT, 108 | 'network_id': self.ID, 109 | } 110 | self.run_test(_make_id, func, method, params) 111 | 112 | def test_get_btp_source_information(self, _make_id): 113 | func = self.icon_service.get_btp_source_information 114 | method = "btp_getSourceInformation" 115 | params = None 116 | self.run_test(_make_id, func, method, params) 117 | 118 | 119 | if __name__ == "__main__": 120 | main() 121 | -------------------------------------------------------------------------------- /tests/api_get/test_get_balance.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | import json 16 | from unittest import main 17 | from unittest.mock import patch 18 | 19 | import requests_mock 20 | 21 | from iconsdk.exception import AddressException 22 | from tests.api_send.test_send_super import TestSendSuper 23 | 24 | 25 | @patch('iconsdk.providers.http_provider.HTTPProvider._make_id', return_value=1234) 26 | class TestGetBalance(TestSendSuper): 27 | 28 | def test_get_balance_from_wallet(self, _make_id): 29 | # case 0: get balance from wallet or score successfully. 30 | with requests_mock.Mocker() as m: 31 | expected_result = 0 32 | expected_request = { 33 | 'id': 1234, 34 | 'jsonrpc': '2.0', 35 | 'method': 'icx_getBalance', 36 | 'params': { 37 | 'address': self.setting["from"] 38 | } 39 | } 40 | response_json = { 41 | "jsonrpc": "2.0", 42 | "result": hex(0), 43 | "id": 1234 44 | } 45 | m.post(self.matcher, json=response_json) 46 | result = self.icon_service.get_balance(self.setting["from"]) 47 | actual_request = json.loads(m._adapter.last_request.text) 48 | self.assertEqual(expected_request, actual_request) 49 | self.assertEqual(expected_result, result) 50 | 51 | # with height 52 | self.icon_service.get_balance(self.setting['from'], height=self.setting['height']) 53 | actual_request = json.loads(m._adapter.last_request.text) 54 | self.assertEqual(hex(self.setting['height']), actual_request['params']['height']) 55 | 56 | def test_get_balance_invalid(self, _make_id): 57 | # case 1: when a param is wrong. 58 | self.assertRaises(AddressException, self.icon_service.get_balance, self.setting["to"][2:]) 59 | self.assertRaises(AddressException, self.icon_service.get_balance, self.setting["from"][2:]) 60 | self.assertRaises(AddressException, self.icon_service.get_balance, "123") 61 | self.assertRaises(AddressException, self.icon_service.get_balance, 123) 62 | # when the address's length is short 63 | self.assertRaises(AddressException, self.icon_service.get_balance, "cx882efc17c2f50e0d60142b9c0e746cbafb569d") 64 | 65 | 66 | if __name__ == "__main__": 67 | main() 68 | -------------------------------------------------------------------------------- /tests/api_get/test_get_block_by_hash.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | import json 16 | from unittest import main 17 | from unittest.mock import patch 18 | 19 | import requests_mock 20 | 21 | from iconsdk.exception import DataTypeException, JSONRPCException 22 | from iconsdk.utils.hexadecimal import remove_0x_prefix 23 | from tests.api_send.test_send_super import TestSendSuper 24 | 25 | 26 | @patch('iconsdk.providers.http_provider.HTTPProvider._make_id', return_value=1234) 27 | class TestGetBlockByHash(TestSendSuper): 28 | 29 | def test_get_block_by_hash(self, _make_id): 30 | with requests_mock.Mocker() as m: 31 | block_hash = "0x033f8d96045eb8301fd17cf078c28ae58a3ba329f6ada5cf128ee56dc2af26f7" 32 | expected_request = { 33 | 'id': 1234, 34 | 'jsonrpc': '2.0', 35 | 'method': 'icx_getBlockByHash', 36 | 'params': { 37 | 'hash': block_hash 38 | } 39 | } 40 | 41 | response_json = { 42 | "jsonrpc": "2.0", 43 | "result": { 44 | "version": "0.1a", 45 | "prev_block_hash": "cf43b3fd45981431a0e64f79d07bfcf703e064b73b802c5f32834eec72142190", 46 | "merkle_tree_root_hash": "375540830d475a73b704cf8dee9fa9eba2798f9d2af1fa55a85482e48daefd3b", 47 | "time_stamp": 1516819217223222, 48 | "confirmed_transaction_list": [ 49 | { 50 | "from": "hx54f7853dc6481b670caf69c5a27c7c8fe5be8269", 51 | "to": "hx49a23bd156932485471f582897bf1bec5f875751", 52 | "value": "0x56bc75e2d63100000", 53 | "fee": "0x2386f26fc10000", 54 | "nonce": "0x1", 55 | "tx_hash": "375540830d475a73b704cf8dee9fa9eba2798f9d2af1fa55a85482e48daefd3b", 56 | "signature": "bjarKeF3izGy469dpSciP3TT9caBQVYgHdaNgjY+8wJTOVSFm4o/ODXycFOdXUJcIwqvcE9If8x6Zmgt//XmkQE=", 57 | "method": "icx_sendTransaction" 58 | } 59 | ], 60 | "block_hash": "3add53134014e940f6f6010173781c4d8bd677d9931a697f962483e04a685e5c", 61 | "height": 1, 62 | "peer_id": "hx7e1a1ece096ef3fa44ac9692394c2e11d0017e4a", 63 | "signature": "liAIa7aPYvBRdZAdBz6zt2Gc9vVo/4+gkDz5uscS8Mw+B5gkp6zQeHhD5sNpyWcIsq5c9OxwOCUaBp0vu8eAgwE=", 64 | "next_leader": "" 65 | }, 66 | "id": 1234 67 | } 68 | 69 | # case 0: when hash value of latest block is valid 70 | m.post(self.matcher, json=response_json) 71 | result = self.icon_service.get_block(block_hash) 72 | actual_request = json.loads(m._adapter.last_request.text) 73 | self.assertEqual(expected_request, actual_request) 74 | self.assertTrue(result) 75 | 76 | # case 1: when hash value is invalid not prefixed with `0x` 77 | invalid_hash = remove_0x_prefix(block_hash) 78 | self.assertRaises(DataTypeException, self.icon_service.get_block, invalid_hash) 79 | 80 | def test_get_block_by_wrong_hash(self, _make_id): 81 | with requests_mock.Mocker() as m: 82 | invalid_block_hash = "0x033f8d96045eb8301fd17cf078c28ae58a3ba329f6ada5cf128ee56dc2af26f7" 83 | expected_request = { 84 | 'id': 1234, 85 | 'jsonrpc': '2.0', 86 | 'method': 'icx_getBlockByHash', 87 | 'params': { 88 | 'hash': invalid_block_hash 89 | } 90 | } 91 | 92 | response_json = { 93 | 'id': 1234, 94 | "jsonrpc": "2.0", 95 | "error": { 96 | "code": -32602, 97 | "message": "fail wrong block hash" 98 | }, 99 | } 100 | m.post(self.matcher, json=response_json, status_code=400) 101 | self.assertRaises(JSONRPCException, self.icon_service.get_block, invalid_block_hash) 102 | actual_request = json.loads(m._adapter.last_request.text) 103 | self.assertEqual(expected_request, actual_request) 104 | 105 | 106 | if __name__ == "__main__": 107 | main() 108 | -------------------------------------------------------------------------------- /tests/api_get/test_get_block_by_height.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | import json 17 | from unittest import main 18 | from unittest.mock import patch 19 | 20 | import requests_mock 21 | 22 | from iconsdk.exception import DataTypeException, JSONRPCException 23 | from tests.api_send.test_send_super import TestSendSuper 24 | 25 | 26 | @patch('iconsdk.providers.http_provider.HTTPProvider._make_id', return_value=1234) 27 | class TestGetBlockByHeight(TestSendSuper): 28 | 29 | def test_get_block_by_height(self, _make_id): 30 | with requests_mock.Mocker() as m: 31 | height = 1 32 | expected_request = { 33 | 'id': 1234, 34 | 'jsonrpc': '2.0', 35 | 'method': 'icx_getBlockByHeight', 36 | 'params': { 37 | 'height': hex(height) 38 | } 39 | } 40 | 41 | response_json = { 42 | "jsonrpc": "2.0", 43 | "result": { 44 | "version": "0.1a", 45 | "prev_block_hash": "cf43b3fd45981431a0e64f79d07bfcf703e064b73b802c5f32834eec72142190", 46 | "merkle_tree_root_hash": "375540830d475a73b704cf8dee9fa9eba2798f9d2af1fa55a85482e48daefd3b", 47 | "time_stamp": 1516819217223222, 48 | "confirmed_transaction_list": [ 49 | { 50 | "from": "hx54f7853dc6481b670caf69c5a27c7c8fe5be8269", 51 | "to": "hx49a23bd156932485471f582897bf1bec5f875751", 52 | "value": "0x56bc75e2d63100000", 53 | "fee": "0x2386f26fc10000", 54 | "nonce": "0x1", 55 | "tx_hash": "375540830d475a73b704cf8dee9fa9eba2798f9d2af1fa55a85482e48daefd3b", 56 | "signature": "bjarKeF3izGy469dpSciP3TT9caBQVYgHdaNgjY+8wJTOVSFm4o/ODXycFOdXUJcIwqvcE9If8x6Zmgt//XmkQE=", 57 | "method": "icx_sendTransaction" 58 | } 59 | ], 60 | "block_hash": "3add53134014e940f6f6010173781c4d8bd677d9931a697f962483e04a685e5c", 61 | "height": 1, 62 | "peer_id": "hx7e1a1ece096ef3fa44ac9692394c2e11d0017e4a", 63 | "signature": "liAIa7aPYvBRdZAdBz6zt2Gc9vVo/4+gkDz5uscS8Mw+B5gkp6zQeHhD5sNpyWcIsq5c9OxwOCUaBp0vu8eAgwE=", 64 | "next_leader": "" 65 | }, 66 | "id": 1234 67 | } 68 | # case 0: when height is 0 69 | m.post(self.matcher, json=response_json) 70 | result = self.icon_service.get_block(height) 71 | actual_request = json.loads(m._adapter.last_request.text) 72 | self.assertEqual(expected_request, actual_request) 73 | self.assertTrue(result) 74 | 75 | def test_get_block_by_height_invalid(self, _make_id): 76 | self.assertRaises(DataTypeException, self.icon_service.get_block, "1") 77 | self.assertRaises(DataTypeException, self.icon_service.get_block, "0x123") 78 | self.assertRaises(DataTypeException, self.icon_service.get_block, -2) 79 | 80 | def test_get_block_by_wrong_height(self, _make_id): 81 | with requests_mock.Mocker() as m: 82 | wrong_height = 5 83 | expected_request = { 84 | 'id': 1234, 85 | 'jsonrpc': '2.0', 86 | 'method': 'icx_getBlockByHeight', 87 | 'params': { 88 | 'height': hex(wrong_height) 89 | } 90 | } 91 | response_json = { 92 | "jsonrpc": "2.0", 93 | "error": { 94 | "code": -32602, 95 | "message": "fail wrong block hash" 96 | }, 97 | "id": 1234 98 | } 99 | m.post(self.matcher, json=response_json, status_code=400) 100 | self.assertRaises(JSONRPCException, self.icon_service.get_block, wrong_height) 101 | actual_request = json.loads(m._adapter.last_request.text) 102 | self.assertEqual(expected_request, actual_request) 103 | 104 | 105 | if __name__ == "__main__": 106 | main() 107 | -------------------------------------------------------------------------------- /tests/api_get/test_get_last_block.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | import json 16 | from unittest import main 17 | from unittest.mock import patch 18 | 19 | import requests_mock 20 | 21 | from iconsdk.exception import DataTypeException 22 | from tests.api_send.test_send_super import TestSendSuper 23 | 24 | 25 | @patch('iconsdk.providers.http_provider.HTTPProvider._make_id', return_value=1234) 26 | class TestGetLastBlock(TestSendSuper): 27 | 28 | def test_get_block_by_height(self, _make_id): 29 | with requests_mock.Mocker() as m: 30 | expected_request = { 31 | 'id': 1234, 32 | 'jsonrpc': '2.0', 33 | 'method': 'icx_getLastBlock', 34 | } 35 | response_json = { 36 | "jsonrpc": "2.0", 37 | "result": { 38 | "version": "0.1a", 39 | "prev_block_hash": "cf43b3fd45981431a0e64f79d07bfcf703e064b73b802c5f32834eec72142190", 40 | "merkle_tree_root_hash": "375540830d475a73b704cf8dee9fa9eba2798f9d2af1fa55a85482e48daefd3b", 41 | "time_stamp": 1516819217223222, 42 | "confirmed_transaction_list": [ 43 | { 44 | "from": "hx54f7853dc6481b670caf69c5a27c7c8fe5be8269", 45 | "to": "hx49a23bd156932485471f582897bf1bec5f875751", 46 | "value": "0x56bc75e2d63100000", 47 | "fee": "0x2386f26fc10000", 48 | "nonce": "0x1", 49 | "tx_hash": "375540830d475a73b704cf8dee9fa9eba2798f9d2af1fa55a85482e48daefd3b", 50 | "signature": "bjarKeF3izGy469dpSciP3TT9caBQVYgHdaNgjY+8wJTOVSFm4o/ODXycFOdXUJcIwqvcE9If8x6Zmgt//XmkQE=", 51 | "method": "icx_sendTransaction" 52 | } 53 | ], 54 | "block_hash": "3add53134014e940f6f6010173781c4d8bd677d9931a697f962483e04a685e5c", 55 | "height": 1, 56 | "peer_id": "hx7e1a1ece096ef3fa44ac9692394c2e11d0017e4a", 57 | "signature": "liAIa7aPYvBRdZAdBz6zt2Gc9vVo/4+gkDz5uscS8Mw+B5gkp6zQeHhD5sNpyWcIsq5c9OxwOCUaBp0vu8eAgwE=", 58 | "next_leader": "" 59 | }, 60 | "id": 1234 61 | } 62 | # case 0: when param is `latest` 63 | m.post(self.matcher, json=response_json) 64 | result = self.icon_service.get_block("latest") 65 | actual_request = json.loads(m._adapter.last_request.text) 66 | self.assertEqual(expected_request, actual_request) 67 | self.assertTrue(result) 68 | 69 | def test_get_last_block_invalid(self, _make_id): 70 | # case 2: when param is wrong 71 | self.assertRaises(DataTypeException, self.icon_service.get_block, "latest1") 72 | self.assertRaises(DataTypeException, self.icon_service.get_block, "late") 73 | 74 | 75 | if __name__ == "__main__": 76 | main() 77 | -------------------------------------------------------------------------------- /tests/api_get/test_get_total_supply.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | import json 16 | from unittest import main 17 | from unittest.mock import patch 18 | 19 | import requests_mock 20 | 21 | from tests.api_send.test_send_super import TestSendSuper 22 | 23 | 24 | @patch('iconsdk.providers.http_provider.HTTPProvider._make_id', return_value=1234) 25 | class TestGetTotalSupply(TestSendSuper): 26 | 27 | def test_get_total_supply(self, _make_id): 28 | with requests_mock.Mocker() as m: 29 | supply = 1_000_000_000 30 | expected_request = { 31 | 'id': 1234, 32 | 'jsonrpc': '2.0', 33 | 'method': 'icx_getTotalSupply', 34 | } 35 | 36 | response_json = { 37 | 'jsonrpc': '2.0', 38 | 'result': hex(supply), 39 | 'id': 1234 40 | } 41 | m.post(self.matcher, json=response_json) 42 | # case 0: when calling the method successfully 43 | result = self.icon_service.get_total_supply() 44 | actual_request = json.loads(m._adapter.last_request.text) 45 | self.assertEqual(expected_request, actual_request) 46 | self.assertTrue(result, supply) 47 | 48 | # with height 49 | self.icon_service.get_total_supply(height=self.setting['height']) 50 | actual_request = json.loads(m._adapter.last_request.text) 51 | self.assertEqual(hex(self.setting['height']), actual_request['params']['height']) 52 | 53 | 54 | if __name__ == "__main__": 55 | main() 56 | -------------------------------------------------------------------------------- /tests/api_get/test_get_transaction_by_hash.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | import json 16 | from unittest import main 17 | from unittest.mock import patch 18 | 19 | import requests_mock 20 | 21 | from iconsdk.exception import JSONRPCException, DataTypeException 22 | from iconsdk.utils.hexadecimal import remove_0x_prefix, add_cx_prefix 23 | from iconsdk.utils.validation import is_transaction 24 | from tests.api_send.test_send_super import TestSendSuper 25 | 26 | 27 | @patch('iconsdk.providers.http_provider.HTTPProvider._make_id', return_value=1234) 28 | class TestGetTransactionByHash(TestSendSuper): 29 | 30 | def test_get_transaction_by_hash(self, _make_id): 31 | with requests_mock.Mocker() as m: 32 | tx_hash = "0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238" 33 | expected_request = { 34 | 'id': 1234, 35 | 'jsonrpc': '2.0', 36 | 'method': 'icx_getTransactionByHash', 37 | 'params': { 38 | 'txHash': tx_hash 39 | } 40 | } 41 | response_json = { 42 | "jsonrpc": "2.0", 43 | "result": { 44 | "version": "0x3", 45 | "from": self.setting["from"], 46 | "to": self.setting["to"], 47 | "stepLimit": hex(self.setting["step_limit"]), 48 | "value": "0x470de4df820000", 49 | "nid": hex(self.setting["nid"]), 50 | "timestamp": "1517999520286000", 51 | "signature": "sILBL1MPwOou8ItM4s0Vqx21l62QyucgTLsEQ51BGi5v/IJ1hOCT/P/rz1V1pDSGAnTQ7rGw9rSOVM5TAGbJOAE=", 52 | "method": "icx_sendTransaction", 53 | "txHash": tx_hash, 54 | "txIndex": "0x0", 55 | "blockHeight": "0xa", 56 | "blockHash": "0x9a39a75d7075687f746d61191baf1a1ff3b5bc0acc4a8df0bb872e53e13cdc17" 57 | }, 58 | "id": 1234 59 | } 60 | 61 | m.post(self.matcher, json=response_json) 62 | # case 0: when tx_hash is valid 63 | result = self.icon_service.get_transaction(tx_hash) 64 | actual_request = json.loads(m._adapter.last_request.text) 65 | self.assertEqual(expected_request, actual_request) 66 | self.assertTrue(is_transaction(result)) 67 | 68 | def test_get_transaction_invalid(self, _make_id): 69 | tx_hash = "0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238" 70 | # case 1: when tx_hash is invalid - no prefixed 71 | self.assertRaises(DataTypeException, self.icon_service.get_transaction, remove_0x_prefix(tx_hash)) 72 | # case 2: when tx_hash is invalid - wrong prefixed 73 | self.assertRaises(DataTypeException, self.icon_service.get_transaction, 74 | add_cx_prefix(remove_0x_prefix(tx_hash))) 75 | # case 3: when tx_hash is invalid - too short 76 | self.assertRaises(DataTypeException, self.icon_service.get_transaction, tx_hash[:15]) 77 | 78 | def test_get_transaction_wrong_hash(self, _make_id): 79 | with requests_mock.Mocker() as m: 80 | wrong_tx_hash = "0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238" 81 | response_json = { 82 | "jsonrpc": "2.0", 83 | "error": { 84 | "code": -32602, 85 | "message": "Invalid params txHash" 86 | }, 87 | "id": 1234 88 | } 89 | 90 | m.post(self.matcher, json=response_json, status_code=400) 91 | self.assertRaises(JSONRPCException, self.icon_service.get_transaction, wrong_tx_hash) 92 | 93 | 94 | if __name__ == "__main__": 95 | main() 96 | -------------------------------------------------------------------------------- /tests/api_get/test_wait_transaction_result.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2021 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | import json 17 | from unittest import main 18 | from unittest.mock import patch 19 | 20 | import requests_mock 21 | 22 | from iconsdk.exception import DataTypeException, JSONRPCException 23 | from iconsdk.utils.hexadecimal import remove_0x_prefix, add_cx_prefix 24 | from iconsdk.utils.validation import is_transaction_result 25 | from tests.api_send.test_send_super import TestSendSuper 26 | 27 | 28 | @patch('iconsdk.providers.http_provider.HTTPProvider._make_id', return_value=1234) 29 | class TestWaitTransactionResult(TestSendSuper): 30 | def test_wait_transaction_result(self, _make_id): 31 | tx_hash: str = "0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238" 32 | with requests_mock.Mocker() as m: 33 | expected_request = { 34 | 'id': 1234, 35 | 'jsonrpc': '2.0', 36 | 'method': 'icx_waitTransactionResult', 37 | 'params': { 38 | 'txHash': tx_hash 39 | } 40 | } 41 | response_json = { 42 | "jsonrpc": "2.0", 43 | "result": { 44 | "txHash": "0x33db06f38424207daa69c9df153649fd3913c21e162f16f4839c9c3318e44388", 45 | "blockHeight": "0x13f", 46 | "blockHash": "0x069e8a2431ae2c7e55924af477be87518476aa1eb1b2e7d1ee8d61d7874ea907", 47 | "txIndex": "0x1", 48 | "to": "cx0000000000000000000000000000000000000000", 49 | "stepUsed": "0x263b8", 50 | "stepPrice": "0x2540be400", 51 | "cumulativeStepUsed": "0x263b8", 52 | "eventLogs": [ 53 | { 54 | "scoreAddress": "cx0000000000000000000000000000000000000000", 55 | "indexed": [ 56 | "PRepSet(Address)" 57 | ], 58 | "data": [ 59 | "hx86aba2210918a9b116973f3c4b27c41a54d5dafe" 60 | ] 61 | } 62 | ], 63 | "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000080000000000000000000000000000000000000000000000000000000000020000000000000008000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 64 | "status": "0x1" 65 | }, 66 | "id": 1234 67 | } 68 | m.post(self.matcher, json=response_json) 69 | self.icon_service.wait_transaction_result(tx_hash) 70 | actual_request = json.loads(m._adapter.last_request.text) 71 | self.assertEqual(expected_request, actual_request) 72 | 73 | def test_wait_transaction_result_invalid(self, _make_id): 74 | invalid_tx_hash: str = "0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238" 75 | # case 1: when tx_hash is invalid - no prefixed 76 | self.assertRaises( 77 | DataTypeException, 78 | self.icon_service.wait_transaction_result, 79 | remove_0x_prefix(invalid_tx_hash) 80 | ) 81 | # case 2: when tx_hash is invalid - wrong prefixed 82 | self.assertRaises( 83 | DataTypeException, 84 | self.icon_service.wait_transaction_result, 85 | add_cx_prefix(remove_0x_prefix(invalid_tx_hash)) 86 | ) 87 | # case 3: when tx_hash is invalid - too short 88 | self.assertRaises( 89 | DataTypeException, 90 | self.icon_service.wait_transaction_result, 91 | invalid_tx_hash[:15] 92 | ) 93 | 94 | 95 | if __name__ == "__main__": 96 | main() 97 | -------------------------------------------------------------------------------- /tests/api_send/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | -------------------------------------------------------------------------------- /tests/api_send/sample_token/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/icon-sdk-python/3002f8270caf4bfe57c84d16608c63e6e236b749/tests/api_send/sample_token/__init__.py -------------------------------------------------------------------------------- /tests/api_send/sample_token/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.0.1", 3 | "main_file": "sample_token", 4 | "main_score": "SampleToken" 5 | } -------------------------------------------------------------------------------- /tests/api_send/sample_token/sample_token.py: -------------------------------------------------------------------------------- 1 | # from iconservice import * 2 | # 3 | # 4 | # class SampleToken(IconScoreBase): 5 | # 6 | # __BALANCES = 'balances' 7 | # __TOTAL_SUPPLY = 'total_supply' 8 | # 9 | # @eventlog(indexed=3) 10 | # def Transfer(self, addr_from: Address, addr_to: Address, value: int): pass 11 | # 12 | # def __init__(self, db: IconScoreDatabase) -> None: 13 | # super().__init__(db) 14 | # self.__total_supply = VarDB(self.__TOTAL_SUPPLY, db, value_type=int) 15 | # self.__balances = DictDB(self.__BALANCES, db, value_type=int) 16 | # 17 | # def on_install(self, init_supply: int = 1000, decimal: int = 18) -> None: 18 | # super().on_install() 19 | # 20 | # total_supply = init_supply * 10 ** decimal 21 | # 22 | # self.__total_supply.set(total_supply) 23 | # self.__balances[self.msg.sender] = total_supply 24 | # 25 | # def on_update(self) -> None: 26 | # super().on_update() 27 | # 28 | # @external(readonly=True) 29 | # def total_supply(self) -> int: 30 | # return self.__total_supply.get() 31 | # 32 | # @external(readonly=True) 33 | # def balance_of(self, addr_from: Address) -> int: 34 | # return self.__balances[addr_from] 35 | # 36 | # def __transfer(self, _addr_from: Address, _addr_to: Address, _value: int) -> bool: 37 | # 38 | # if self.balance_of(_addr_from) < _value: 39 | # self.revert(f"{_addr_from}'s balance < {_value}") 40 | # 41 | # self.__balances[_addr_from] = self.__balances[_addr_from] - _value 42 | # self.__balances[_addr_to] = self.__balances[_addr_to] + _value 43 | # 44 | # self.Transfer(_addr_from, _addr_to, _value) 45 | # return True 46 | # 47 | # @external 48 | # def transfer(self, addr_to: Address, value: int) -> bool: 49 | # return self.__transfer(self.msg.sender, addr_to, value) 50 | # 51 | # def fallback(self) -> None: 52 | # pass 53 | # 54 | # 55 | -------------------------------------------------------------------------------- /tests/api_send/sample_token2/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/icon-sdk-python/3002f8270caf4bfe57c84d16608c63e6e236b749/tests/api_send/sample_token2/__init__.py -------------------------------------------------------------------------------- /tests/api_send/sample_token2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.0.2", 3 | "main_file": "sample_token", 4 | "main_score": "SampleToken" 5 | } -------------------------------------------------------------------------------- /tests/api_send/sample_token2/sample_token.py: -------------------------------------------------------------------------------- 1 | # from iconservice import * 2 | # 3 | # 4 | # class SampleToken(IconScoreBase): 5 | # 6 | # __BALANCES = 'balances' 7 | # __TOTAL_SUPPLY = 'total_supply' 8 | # 9 | # @eventlog(indexed=3) 10 | # def Transfer(self, addr_from: Address, addr_to: Address, value: int): pass 11 | # 12 | # def __init__(self, db: IconScoreDatabase) -> None: 13 | # super().__init__(db) 14 | # self.__total_supply = VarDB(self.__TOTAL_SUPPLY, db, value_type=int) 15 | # self.__balances = DictDB(self.__BALANCES, db, value_type=int) 16 | # 17 | # def on_install(self, init_supply: int = 1000, decimal: int = 18) -> None: 18 | # super().on_install() 19 | # 20 | # total_supply = init_supply * 10 ** decimal 21 | # 22 | # self.__total_supply.set(total_supply) 23 | # self.__balances[self.msg.sender] = total_supply 24 | # 25 | # def on_update(self) -> None: 26 | # super().on_update() 27 | # 28 | # @external(readonly=True) 29 | # def total_supply(self) -> int: 30 | # return 0 31 | # 32 | # @external(readonly=True) 33 | # def balance_of(self, addr_from: Address) -> int: 34 | # return self.__balances[addr_from] 35 | # 36 | # def __transfer(self, _addr_from: Address, _addr_to: Address, _value: int) -> bool: 37 | # 38 | # if self.balance_of(_addr_from) < _value: 39 | # self.revert(f"{_addr_from}'s balance < {_value}") 40 | # 41 | # self.__balances[_addr_from] = self.__balances[_addr_from] - _value 42 | # self.__balances[_addr_to] = self.__balances[_addr_to] + _value 43 | # 44 | # self.Transfer(_addr_from, _addr_to, _value) 45 | # return True 46 | # 47 | # @external 48 | # def transfer(self, addr_to: Address, value: int) -> bool: 49 | # return self.__transfer(self.msg.sender, addr_to, value) 50 | # 51 | # def fallback(self) -> None: 52 | # pass 53 | # 54 | # @external(readonly=True) 55 | # def hello(self) -> str: 56 | # return "Hello" 57 | # 58 | -------------------------------------------------------------------------------- /tests/api_send/test_send_tx_wait.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2021 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | import json 16 | from unittest.mock import patch 17 | 18 | import requests_mock 19 | 20 | from iconsdk.builder.transaction_builder import TransactionBuilder 21 | from iconsdk.exception import JSONRPCException, DataTypeException 22 | from iconsdk.signed_transaction import SignedTransaction 23 | from iconsdk.utils.validation import is_icx_transaction, is_T_HASH 24 | from tests.api_send.test_send_super import TestSendSuper 25 | 26 | 27 | @patch('iconsdk.providers.http_provider.HTTPProvider._make_id', return_value=1234) 28 | class TestSendAndWait(TestSendSuper): 29 | 30 | def test_transfer(self, _make_id): 31 | icx_transaction = TransactionBuilder() \ 32 | .from_(self.setting["from"]) \ 33 | .to(self.setting["to"]) \ 34 | .value(self.setting["value"]) \ 35 | .step_limit(self.setting["step_limit"]) \ 36 | .nid(3) \ 37 | .nonce(self.setting["nonce"]) \ 38 | .version(3) \ 39 | .timestamp(self.setting["timestamp"]) \ 40 | .build() 41 | tx_dict = SignedTransaction.convert_tx_to_jsonrpc_request(icx_transaction) 42 | self.assertTrue(is_icx_transaction(tx_dict)) 43 | signed_transaction = SignedTransaction(icx_transaction, self.wallet) 44 | 45 | with requests_mock.Mocker() as m: 46 | tx_hash: str = "0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238" 47 | expected_request = { 48 | 'id': 1234, 49 | 'jsonrpc': '2.0', 50 | 'method': 'icx_sendTransactionAndWait', 51 | 'params': { 52 | 'from': self.setting["from"], 53 | 'nid': hex(self.setting["nid"]), 54 | 'nonce': hex(self.setting["nonce"]), 55 | 'signature': signed_transaction.signed_transaction_dict["signature"], 56 | 'stepLimit': hex(self.setting["step_limit"]), 57 | 'timestamp': hex(self.setting["timestamp"]), 58 | 'to': self.setting["to"], 59 | 'value': hex(self.setting["value"]), 60 | 'version': '0x3' 61 | } 62 | } 63 | 64 | response_json: dict = { 65 | "jsonrpc": "2.0", 66 | "result": tx_hash, 67 | "id": 1234 68 | } 69 | m.post(self.matcher, json=response_json) 70 | self.icon_service.send_transaction_and_wait(signed_transaction) 71 | actual_request = json.loads(m._adapter.last_request.text) 72 | self.assertEqual(expected_request, actual_request) 73 | -------------------------------------------------------------------------------- /tests/builder/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | -------------------------------------------------------------------------------- /tests/builder/test_call_builder.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | from unittest import TestCase, main 17 | 18 | from iconsdk.builder.call_builder import CallBuilder 19 | 20 | 21 | class TestCallBuilder(TestCase): 22 | 23 | def test_make_call_builder(self): 24 | """Testing for making a couple of call builders successfully""" 25 | height = 100 26 | 27 | call_1 = CallBuilder() \ 28 | .from_("1_FROM") \ 29 | .to("1_TO") \ 30 | .method("1_METHOD") \ 31 | .params({"test": 123}) \ 32 | .height(height) \ 33 | .build() 34 | 35 | call_2 = CallBuilder().from_("2_FROM").to("2_TO").method("2_METHOD").params({"test": 123}).height(100).build() 36 | 37 | properties = ["from_", "to", "method", "params", "height"] 38 | values_call_1 = ["1_FROM", "1_TO", "1_METHOD", {'test': '0x7b'}, hex(height)] 39 | values_call_2 = ["2_FROM", "2_TO", "2_METHOD", {'test': '0x7b'}, hex(height)] 40 | 41 | # Checks all of property is collect. 42 | for idx, property in enumerate(properties): 43 | self.assertEqual(getattr(call_1, property), values_call_1[idx]) 44 | self.assertEqual(getattr(call_2, property), values_call_2[idx]) 45 | 46 | def test_make_call_builder_from_dict_to_dict(self): 47 | """Testing for from dict and to dict method.""" 48 | call_1 = CallBuilder() \ 49 | .to("1_TO") \ 50 | .method("1_METHOD") \ 51 | .params({"test": 123}) \ 52 | .height(100) \ 53 | .build() 54 | 55 | call_1_as_dict = call_1.to_dict() 56 | call_2 = CallBuilder.from_dict(call_1_as_dict).build() 57 | call_2_as_dict = call_2.to_dict() 58 | self.assertEqual(call_1_as_dict, call_2_as_dict) 59 | 60 | def test_make_call_builder_changed(self): 61 | """Testing for making a call builder changed, it should not work.""" 62 | 63 | call_1 = CallBuilder() \ 64 | .from_("1_FROM") \ 65 | .to("1_TO") \ 66 | .method("1_METHOD") \ 67 | .params("1_PARAMS") \ 68 | .build() 69 | 70 | def test_set_call_builder(): 71 | call_1.from_ = "1_NEW_PROM" 72 | 73 | self.assertRaises(AttributeError, test_set_call_builder) 74 | 75 | 76 | if __name__ == "__main__": 77 | main() 78 | -------------------------------------------------------------------------------- /tests/converter/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | -------------------------------------------------------------------------------- /tests/converter/example_tx_results.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | TX_RESULT_0 = { 17 | "status": "0x0", 18 | "failure": { 19 | "code": "0x7d00", 20 | "message": "Out of balance" 21 | }, 22 | "to": "cx4d6f646441a3f9c9b91019c9b98e3c342cceb114", 23 | "txHash": "0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238", 24 | "txIndex": "0x1", 25 | "blockHeight": "0x1234", 26 | "blockHash": "0xc71303ef8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238", 27 | "cumulativeStepUsed": "0x1234", 28 | "stepUsed": "0x1234", 29 | "stepPrice": "0x5678" 30 | } 31 | 32 | TX_RESULT_1 = { 33 | "txHash": "0x36d46e6f8ce3fb037f72c227214a391ba680fb771bb8062b7391a9ef084fdebc", 34 | "blockHeight": "0x7", 35 | "blockHash": "0x6979f9ad26fcf54a59998337fe6383c1feb32ef111d0cc9b3a78eec595e1bf4e", 36 | "txIndex": "0x0", 37 | "to": "cx0000000000000000000000000000000000000000", 38 | "scoreAddress": "cx6d34efb31a521f680a77d736678de30ef9aff9ce", 39 | "stepUsed": "0x298f290", 40 | "stepPrice": "0x0", 41 | "cumulativeStepUsed": "0x298f290", 42 | "eventLogs": [], 43 | "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 44 | "status": "0x1" 45 | } 46 | 47 | TX_RESULT_2 = { 48 | "txHash": "0xd34941501ef27bd2eba6c35b382e5ca2da5f5e44bec3bf460367a8ee04fd3fae", 49 | "blockHeight": "0x7", 50 | "blockHash": "0x6979f9ad26fcf54a59998337fe6383c1feb32ef111d0cc9b3a78eec595e1bf4e", 51 | "txIndex": "0x1", 52 | "to": "cx0000000000000000000000000000000000000001", 53 | "stepUsed": "0xf9e70", 54 | "stepPrice": "0x0", 55 | "cumulativeStepUsed": "0x2a89100", 56 | "eventLogs": [], 57 | "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 58 | "status": "0x0", 59 | "failure": { 60 | "code": "0x7d64", 61 | "message": "Invalid sender: no permission" 62 | } 63 | } 64 | 65 | TX_RESULT_3 = { 66 | "txHash": "0xa24ffb1152aa9c58dab9b9b2b7102d5b238e0076222991ba289581a63b6ac0a5", 67 | "blockHeight": "0x4", 68 | "blockHash": "0x255822446aca1cf42e0a68302560f6f7e6e33ff9beea25c3ee23255bb5504165", 69 | "txIndex": "0x0", 70 | "to": "hxf1d9719d29488684039712e881830b5b37a64f11", 71 | "stepUsed": "0xf4240", 72 | "stepPrice": "0x0", 73 | "cumulativeStepUsed": "0xf4240", 74 | "eventLogs": [], 75 | "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 76 | "status": "0x1" 77 | } 78 | -------------------------------------------------------------------------------- /tests/example_config.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # integrate test 17 | BASE_DOMAIN_URL_V3_FOR_TEST = "http://localhost:9000" 18 | PRIVATE_KEY_FOR_TEST = bytes.fromhex("592eb276d534e2c41a2d9356c0ab262dc233d87e4dd71ce705ec130a8d27ff0c") 19 | VERSION_FOR_TEST = 3 20 | -------------------------------------------------------------------------------- /tests/keystore_file/not_a_keystore_file.txt: -------------------------------------------------------------------------------- 1 | { 2 | "a":"B" 3 | } -------------------------------------------------------------------------------- /tests/keystore_file/test_keystore.txt: -------------------------------------------------------------------------------- 1 | {"address": "hxfd7e4560ba363f5aabd32caac7317feeee70ea57", "crypto": {"cipher": "aes-128-ctr", "cipherparams": {"iv": "952dc6c2e7d4ed5df45f4ddeac817dfb"}, "ciphertext": "c5482f14fcdae4848b74be65593452efe11c106fac2fe82801cb7735d5fa9b55", "kdf": "pbkdf2", "kdfparams": {"c": 262144, "dklen": 32, "prf": "hmac-sha256", "salt": "65fa9a3d6e8991a01f883990b0b0ca91"}, "mac": "380e8daf36daff0e02949e4a40c0c8263890978071f0794581025b8e41e589a4"}, "id": "dd9fd4f9-3279-42d1-96f1-9ef637ddb6e6", "version": 3, "coinType": "icx"} -------------------------------------------------------------------------------- /tests/keystore_file/test_keystore_for_transfer.txt: -------------------------------------------------------------------------------- 1 | {"version":3,"id":"0dca2957-3172-4821-9a51-aa446816c848","address":"hx66425784bfddb5b430136b38268c3ce1fb68e8c5","crypto":{"ciphertext":"21dda34264c13b9b2a41aee55e30a59338a870b69dafc2f9232438658b70c09c","cipherparams":{"iv":"a332eafe89f070a8de97fce8240b7880"},"cipher":"aes-128-ctr","kdf":"pbkdf2","kdfparams":{"dklen":32,"salt":"836c95dacceb884a7c3bb96af2b32b04a17eedde0036d8b6aeb783e34f9131f2","c":262144,"prf":"hmac-sha256"},"mac":"18c9e62736c08e25482b8cef1045f7139585dcf1eee6dc0ef94ca20e1a5c6976"},"coinType":"icx"} -------------------------------------------------------------------------------- /tests/keystore_file/test_keystore_for_transfer2.txt: -------------------------------------------------------------------------------- 1 | {"address": "hx95e12b1f98f9b847175849f51bed5d121e742f6a", "crypto": {"cipher": "aes-128-ctr", "cipherparams": {"iv": "9395579d40833525c2751f4799d80271"}, "ciphertext": "862a0512ab09d066d8895f3c2086444200035a49a017a9e4515132c99b2fd7a4", "kdf": "pbkdf2", "kdfparams": {"c": 262144, "dklen": 32, "prf": "hmac-sha256", "salt": "7762366b9a8b036c58beeb695afc6955"}, "mac": "c87fe3b3ac9c7d0c621c641dca901468471c7ca2e34f199026f8da6486fdff24"}, "id": "ec33ec77-16c0-4bfe-bfcc-8816244e5816", "version": 3, "coinType": "icx"} -------------------------------------------------------------------------------- /tests/libs/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | -------------------------------------------------------------------------------- /tests/libs/sample_token/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/icon-sdk-python/3002f8270caf4bfe57c84d16608c63e6e236b749/tests/libs/sample_token/__init__.py -------------------------------------------------------------------------------- /tests/libs/sample_token/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.0.1", 3 | "main_file": "sample_token", 4 | "main_score": "SampleToken" 5 | } -------------------------------------------------------------------------------- /tests/libs/sample_token/sample_token.py: -------------------------------------------------------------------------------- 1 | # from iconservice import * 2 | # 3 | # 4 | # class SampleToken(IconScoreBase): 5 | # 6 | # __BALANCES = 'balances' 7 | # __TOTAL_SUPPLY = 'total_supply' 8 | # 9 | # @eventlog(indexed=3) 10 | # def Transfer(self, addr_from: Address, addr_to: Address, value: int): pass 11 | # 12 | # def __init__(self, db: IconScoreDatabase) -> None: 13 | # super().__init__(db) 14 | # self.__total_supply = VarDB(self.__TOTAL_SUPPLY, db, value_type=int) 15 | # self.__balances = DictDB(self.__BALANCES, db, value_type=int) 16 | # 17 | # def on_install(self, init_supply: int = 1000, decimal: int = 18) -> None: 18 | # super().on_install() 19 | # 20 | # total_supply = init_supply * 10 ** decimal 21 | # 22 | # self.__total_supply.set(total_supply) 23 | # self.__balances[self.msg.sender] = total_supply 24 | # 25 | # def on_update(self) -> None: 26 | # super().on_update() 27 | # 28 | # @external(readonly=True) 29 | # def total_supply(self) -> int: 30 | # return self.__total_supply.get() 31 | # 32 | # @external(readonly=True) 33 | # def balance_of(self, addr_from: Address) -> int: 34 | # return self.__balances[addr_from] 35 | # 36 | # def __transfer(self, _addr_from: Address, _addr_to: Address, _value: int) -> bool: 37 | # 38 | # if self.balance_of(_addr_from) < _value: 39 | # self.revert(f"{_addr_from}'s balance < {_value}") 40 | # 41 | # self.__balances[_addr_from] = self.__balances[_addr_from] - _value 42 | # self.__balances[_addr_to] = self.__balances[_addr_to] + _value 43 | # 44 | # self.Transfer(_addr_from, _addr_to, _value) 45 | # return True 46 | # 47 | # @external 48 | # def transfer(self, addr_to: Address, value: int) -> bool: 49 | # return self.__transfer(self.msg.sender, addr_to, value) 50 | # 51 | # def fallback(self) -> None: 52 | # pass 53 | # 54 | # 55 | -------------------------------------------------------------------------------- /tests/libs/sample_token/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icon-project/icon-sdk-python/3002f8270caf4bfe57c84d16608c63e6e236b749/tests/libs/sample_token/tests/__init__.py -------------------------------------------------------------------------------- /tests/libs/sample_token/tests/test_integrate_sample_token.py: -------------------------------------------------------------------------------- 1 | # from iconservice import * 2 | # 3 | # 4 | # class SampleToken(IconScoreBase): 5 | # 6 | # __BALANCES = 'balances' 7 | # __TOTAL_SUPPLY = 'total_supply' 8 | # 9 | # @eventlog(indexed=3) 10 | # def Transfer(self, addr_from: Address, addr_to: Address, value: int): pass 11 | # 12 | # def __init__(self, db: IconScoreDatabase) -> None: 13 | # super().__init__(db) 14 | # self.__total_supply = VarDB(self.__TOTAL_SUPPLY, db, value_type=int) 15 | # self.__balances = DictDB(self.__BALANCES, db, value_type=int) 16 | # 17 | # def on_install(self, init_supply: int = 1000, decimal: int = 18) -> None: 18 | # super().on_install() 19 | # 20 | # total_supply = init_supply * 10 ** decimal 21 | # 22 | # self.__total_supply.set(total_supply) 23 | # self.__balances[self.msg.sender] = total_supply 24 | # 25 | # def on_update(self) -> None: 26 | # super().on_update() 27 | # 28 | # @external(readonly=True) 29 | # def total_supply(self) -> int: 30 | # return self.__total_supply.get() 31 | # 32 | # @external(readonly=True) 33 | # def balance_of(self, addr_from: Address) -> int: 34 | # return self.__balances[addr_from] 35 | # 36 | # def __transfer(self, _addr_from: Address, _addr_to: Address, _value: int) -> bool: 37 | # 38 | # if self.balance_of(_addr_from) < _value: 39 | # self.revert(f"{_addr_from}'s balance < {_value}") 40 | # 41 | # self.__balances[_addr_from] = self.__balances[_addr_from] - _value 42 | # self.__balances[_addr_to] = self.__balances[_addr_to] + _value 43 | # 44 | # self.Transfer(_addr_from, _addr_to, _value) 45 | # return True 46 | # 47 | # @external 48 | # def transfer(self, addr_to: Address, value: int) -> bool: 49 | # return self.__transfer(self.msg.sender, addr_to, value) 50 | # 51 | # def fallback(self) -> None: 52 | # pass 53 | # 54 | # 55 | -------------------------------------------------------------------------------- /tests/libs/test_in_memory_zip.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | from os import path 17 | from unittest import TestCase, main 18 | 19 | from iconsdk.libs.in_memory_zip import gen_deploy_data_content 20 | 21 | 22 | class TestInMemoryZip(TestCase): 23 | 24 | def test_in_memory_zip(self): 25 | current_dir_path = path.abspath(path.dirname(__file__)) 26 | score_path = path.join(current_dir_path, 'sample_token') 27 | tests_path = path.join(current_dir_path, 'sample_token','tests') 28 | # bytes of sample_token's content 29 | content_bytes = gen_deploy_data_content(score_path) 30 | content_bytes_as_str = str(content_bytes) 31 | self.assertFalse('test_integrate_sample_token.py' in content_bytes_as_str ) 32 | self.assertFalse(tests_path[1:] in content_bytes_as_str) 33 | self.assertTrue(score_path[1:] in content_bytes_as_str) 34 | 35 | 36 | if __name__ == "__main__": 37 | main() 38 | -------------------------------------------------------------------------------- /tests/libs/test_serializer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | from unittest import TestCase, main 17 | 18 | from iconsdk.libs.serializer import serialize 19 | from tests.example_tx_requests import TEST_REQUEST_TRANSFER_ICX, TEST_REQUEST_SCORE_FUNCTION_CALL 20 | 21 | 22 | class TestSerializer(TestCase): 23 | 24 | def test_for_serialize_case_for_sending_normal_tx(self): 25 | """Test when serializer serializes perfectly in this case when is normal send transaction.""" 26 | tx_request = TEST_REQUEST_TRANSFER_ICX 27 | correct_serialized_params = "icx_sendTransaction.from.hxbe258ceb872e08851f1f59694dac2558708ece11.nid.0x3f." \ 28 | "nonce.0x1.stepLimit.0x12345.timestamp.0x563a6cf330136.to.hx5bfdb090f43a808005" \ 29 | "ffc27c25b213145e80b7cd.value.0xde0b6b3a7640000.version.0x3" 30 | self.assertEqual(correct_serialized_params.encode(), serialize(tx_request["params"])) 31 | 32 | def test_for_serialize_case_for_calling(self): 33 | """Test when serializer serializes perfectly in this case when dataType is call.""" 34 | tx_request = TEST_REQUEST_SCORE_FUNCTION_CALL 35 | correct_serialized_params = "icx_sendTransaction.data.{method.transfer.params.{to.hxab2d8215eab14bc6bdd8b" \ 36 | "fb2c8151257032ecd8b.value.0x1}}.dataType.call.from.hxbe258ceb872e08851f1f596" \ 37 | "94dac2558708ece11.nid.0x3f.nonce.0x1.stepLimit.0x12345.timestamp.0x563a6cf33" \ 38 | "0136.to.cxb0776ee37f5b45bfaea8cff1d8232fbb6122ec32.version.0x3" 39 | self.assertEqual(correct_serialized_params.encode(), serialize(tx_request["params"])) 40 | 41 | 42 | if __name__ == "__main__": 43 | main() 44 | -------------------------------------------------------------------------------- /tests/libs/test_signer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | from hashlib import sha3_256 17 | from unittest import TestCase, main 18 | 19 | from coincurve import PrivateKey, PublicKey 20 | 21 | from iconsdk.libs.serializer import serialize 22 | from iconsdk.libs.signer import sign 23 | from tests.example_tx_requests import ( 24 | TEST_REQUEST_TRANSFER_ICX, 25 | TEST_REQUEST_SCORE_FUNCTION_CALL, 26 | TEST_REQUEST_SEND_MESSAGE, 27 | TEST_REQUEST_SCORE_UPDATE, 28 | TEST_REQUEST_SCORE_ISNTALL 29 | ) 30 | 31 | 32 | class TestIcxSigner(TestCase): 33 | 34 | def test_verify_recoverable_sign(self): 35 | """Verifies recovering a signature.""" 36 | 37 | test_requests = [ 38 | TEST_REQUEST_TRANSFER_ICX, 39 | TEST_REQUEST_SCORE_FUNCTION_CALL, 40 | TEST_REQUEST_SEND_MESSAGE, 41 | TEST_REQUEST_SCORE_UPDATE, 42 | TEST_REQUEST_SCORE_ISNTALL 43 | ] 44 | 45 | for request in test_requests: 46 | # Serialize a signature 47 | private_key_object: PrivateKey = PrivateKey() 48 | private_key_bytes: bytes = private_key_object.secret 49 | 50 | msg = serialize(request["params"]) 51 | message_hash = sha3_256(msg).digest() 52 | sign_bytes = sign(message_hash, private_key_bytes) 53 | 54 | public_key = PublicKey.from_signature_and_message(sign_bytes, message_hash, hasher=None) 55 | self.assertEqual(public_key.format(), private_key_object.public_key.format()) 56 | 57 | 58 | if __name__ == "__main__": 59 | main() 60 | -------------------------------------------------------------------------------- /tests/providers/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | -------------------------------------------------------------------------------- /tests/providers/test_http_provider.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | from unittest import main 17 | 18 | from iconsdk.exception import URLException 19 | from iconsdk.providers.http_provider import HTTPProvider 20 | from tests.api_send.test_send_super import TestSendSuper 21 | 22 | 23 | class TestHTTPProvider(TestSendSuper): 24 | FULL_PATH_URL = "http://localhost:9000/api/v3" 25 | BASE_PATH_URL = "http://localhost:9000" 26 | VERSION = 3 27 | 28 | def test_set_http_provider_with_param(self): 29 | try: 30 | # the initializer 31 | HTTPProvider(self.FULL_PATH_URL) 32 | 33 | # the new initializer 34 | HTTPProvider(self.BASE_PATH_URL, self.VERSION) 35 | except URLException: 36 | self.fail('Unexpected exception') 37 | 38 | def test_set_http_provider_with_request_kwargs(self): 39 | # the legacy initializer 40 | try: 41 | HTTPProvider(self.FULL_PATH_URL, 42 | request_kwargs={'timeout': 60, 'allow_redirects': False, 'verify': True}) 43 | except URLException: 44 | self.fail(f'Unexpected exception') 45 | 46 | # the new initializer to be failed 47 | with self.assertRaises(URLException): 48 | HTTPProvider(self.BASE_PATH_URL, 49 | request_kwargs={'timeout': 60, 'allow_redirects': False, 'verify': True}) 50 | 51 | # the new initializer to be success 52 | try: 53 | HTTPProvider(self.BASE_PATH_URL, 3, 54 | request_kwargs={'timeout': 60, 'allow_redirects': False, 'verify': True}) 55 | except URLException: 56 | self.fail(f'Unexpected exception') 57 | 58 | def test_set_http_provider_by_the_initializer_with_valid_url(self): 59 | """The initializer should pass all kind of URLs""" 60 | valid_urls = [ 61 | "http://localhost:9000/api/v3", 62 | "https://ctz.solidwallet.io/api/v3", 63 | "http://localhost:9000/api/v3/channel" 64 | ] 65 | for url in valid_urls: 66 | try: 67 | HTTPProvider(url) 68 | except URLException: 69 | self.fail(f'Unexpected exception with {url}') 70 | 71 | def test_set_http_provider_by_new_initializer_with_invalid_url(self): 72 | invalid_urls = [ 73 | "http://localhost:9000/", 74 | "http://localhost:9000/api/v3", 75 | "http://localhost:9000/api/v3/", 76 | "http://localhost:9000/api/v3/channel", 77 | "https://ctz.solidwallet.io/", 78 | "https://ctz.solidwallet.io/api/v3", 79 | ] 80 | for url in invalid_urls: 81 | with self.assertRaises(URLException): 82 | HTTPProvider(url, self.VERSION) 83 | 84 | 85 | if __name__ == "__main__": 86 | main() 87 | -------------------------------------------------------------------------------- /tests/utils/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | -------------------------------------------------------------------------------- /tests/utils/test_convert_type.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2019 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | from unittest import TestCase 17 | 18 | from iconsdk.utils.convert_type import convert_int_to_hex_str, convert_hex_str_to_bytes 19 | 20 | 21 | class TestConvertType(TestCase): 22 | """Unit tests for functions of convert type module""" 23 | 24 | def test_convert_negative_value_to_hex_str(self): 25 | """ 26 | Given: Input data is negative. 27 | When : Converts negative value into hex string. 28 | Then : Gets return value correctly. 29 | """ 30 | negative_value = -1 31 | self.assertEqual(convert_int_to_hex_str(negative_value), '-0x1') 32 | 33 | def test_convert_positive_value_to_hex_str(self): 34 | """ 35 | Given: Input data is positive. 36 | When : Converts positive value into hex string. 37 | Then : Gets return value correctly. 38 | """ 39 | positive_value = 1 40 | self.assertEqual(convert_int_to_hex_str(positive_value), '0x1') 41 | 42 | def test_convert_zero_value_to_hex_str(self): 43 | """ 44 | Given: Input data is zero. 45 | When : Converts zero value into hex string. 46 | Then : Gets return value correctly. 47 | """ 48 | zero_value = 0 49 | self.assertEqual(convert_int_to_hex_str(zero_value), '0x0') 50 | 51 | def test_convert_hex_str_to_bytes(self): 52 | """ 53 | Given: Input data is hex str. 54 | When : Converts hex string to bytes. 55 | THen : Gets return value correctly. 56 | """ 57 | hex_str = "0x0000" 58 | self.assertEqual(convert_hex_str_to_bytes(hex_str), b'\x00\x00') 59 | -------------------------------------------------------------------------------- /tests/utils/test_hexadecimal.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | from unittest import TestCase 17 | 18 | from iconsdk.utils.hexadecimal import ( 19 | is_lowercase_hex_string, 20 | add_0x_prefix, 21 | add_cx_prefix, 22 | is_0x_prefixed, 23 | remove_0x_prefix 24 | ) 25 | 26 | 27 | class TestHexadecimal(TestCase): 28 | """Unit tests for functions of hexadecimal module""" 29 | 30 | def test_is_lowercase_hex_string(self): 31 | """Unit test for functional test for the function `is_lowercase_hex_string`. 32 | """ 33 | valid_value1 = "test".encode().hex() 34 | valid_value2 = "123".encode().hex() 35 | is_lowercase_hex_string(valid_value1) 36 | is_lowercase_hex_string(valid_value2) 37 | 38 | invalid_value1 = "Abcd" 39 | invalid_value2 = 1234 40 | invalid_value3 = "123aAc" 41 | invalid_value4 = "test".encode() 42 | self.assertFalse(is_lowercase_hex_string(invalid_value1)) 43 | self.assertFalse(is_lowercase_hex_string(invalid_value2)) 44 | self.assertFalse(is_lowercase_hex_string(invalid_value3)) 45 | self.assertFalse(is_lowercase_hex_string(invalid_value4)) 46 | 47 | def test_is_hex_string_prefixed_with_0x(self): 48 | """Unit test for checking whether hex string with 0x or not. 49 | """ 50 | 51 | def is_hex_string_prefixed_with_0x(value: str): 52 | if is_0x_prefixed(value) and is_lowercase_hex_string(remove_0x_prefix(value)): 53 | return True 54 | else: 55 | return False 56 | 57 | # 0x74657374 58 | valid_value1 = add_0x_prefix("test".encode().hex()) 59 | self.assertTrue(is_hex_string_prefixed_with_0x(valid_value1)) 60 | 61 | # 74657374 62 | invalid_value1 = remove_0x_prefix(valid_value1) 63 | self.assertFalse(is_hex_string_prefixed_with_0x(invalid_value1)) 64 | self.assertFalse(is_hex_string_prefixed_with_0x(add_cx_prefix(invalid_value1))) 65 | 66 | invalid_value2 = "Abcd" 67 | self.assertFalse(is_hex_string_prefixed_with_0x(invalid_value2)) 68 | self.assertFalse(is_hex_string_prefixed_with_0x(add_0x_prefix(invalid_value2))) 69 | 70 | invalid_value3 = "123aAc" 71 | self.assertFalse(is_hex_string_prefixed_with_0x(invalid_value3)) 72 | self.assertFalse(is_hex_string_prefixed_with_0x(add_0x_prefix(invalid_value3))) 73 | 74 | 75 | -------------------------------------------------------------------------------- /tests/utils/test_validation.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | from os import path 17 | from unittest import TestCase, main 18 | 19 | from eth_keyfile import load_keyfile 20 | 21 | from iconsdk.exception import KeyStoreException 22 | from iconsdk.utils.validation import is_keystore_file, has_keys, is_keystore_file_for_icon 23 | 24 | 25 | class TestValidation(TestCase): 26 | TEST_CUR_DIR = path.dirname(__file__) 27 | TEST_KEYSTORE_FILE_PATH = path.abspath(path.join(TEST_CUR_DIR, '../keystore_file/test_keystore.txt')) 28 | TEST_NOT_KEYSTORE_FILE_PATH = path.abspath(path.join(TEST_CUR_DIR, '../keystore_file/not_a_keystore_file.txt')) 29 | 30 | def test_method_validate_keystore_file(self): 31 | """Case when validating a keystore file correctly. """ 32 | keystore = load_keyfile(self.TEST_KEYSTORE_FILE_PATH) 33 | self.assertTrue(is_keystore_file(keystore)) 34 | keystore = load_keyfile(self.TEST_NOT_KEYSTORE_FILE_PATH) 35 | self.assertRaises(KeyStoreException, is_keystore_file, keystore) 36 | 37 | def test_method_has_keys(self): 38 | """Case when a dictionary data in a keystore file have all of keys correctly. """ 39 | target_data = { 40 | 'address': 'hxfd7e4560ba363f5aabd32caac7317feeee70ea57', 41 | 'crypto': { 42 | 'cipher': 'aes-128-ctr', 43 | 'cipherparams': { 44 | 'iv': '952dc6c2e7d4ed5df45f4ddeac817dfb' 45 | }, 46 | 'ciphertext': 'c5482f14fcdae4848b74be65593452efe11c106fac2fe82801cb7735d5fa9b55', 47 | 'kdf': 'pbkdf2', 48 | 'kdfparams': { 49 | 'c': 262144, 50 | 'dklen': 32, 51 | 'prf': 'hmac-sha256', 52 | 'salt': '65fa9a3d6e8991a01f883990b0b0ca91' 53 | }, 54 | 'mac': '380e8daf36daff0e02949e4a40c0c8263890978071f0794581025b8e41e589a4' 55 | }, 56 | 'id': 'dd9fd4f9-3279-42d1-96f1-9ef637ddb6e6', 57 | 'version': 3, 58 | 'coinType': 'icx' 59 | } 60 | 61 | root_keys = ["version", "id", "address", "crypto", "coinType"] 62 | crypto_keys = ["ciphertext", "cipherparams", "cipher", "kdf", "kdfparams", "mac"] 63 | crypto_cipherparams_keys = ["iv"] 64 | crypto_kdfparams_keys = ["dklen", "salt", "c", "prf"] 65 | 66 | self.assertTrue(has_keys(target_data, root_keys)) 67 | self.assertTrue(has_keys(target_data["crypto"], crypto_keys)) 68 | self.assertTrue(has_keys(target_data["crypto"]["cipherparams"], crypto_cipherparams_keys)) 69 | self.assertTrue(has_keys(target_data["crypto"]["kdfparams"], crypto_kdfparams_keys)) 70 | 71 | def test_validate_keystore_file_is_for_icon(self): 72 | """Case when validating keystore file if for icon or not.""" 73 | keystore = load_keyfile(self.TEST_KEYSTORE_FILE_PATH) 74 | self.assertTrue(is_keystore_file_for_icon(keystore)) 75 | 76 | # when an address's length is too short. 77 | keystore["address"] = "hx123" 78 | self.assertRaises(KeyStoreException, is_keystore_file_for_icon, keystore) 79 | 80 | # when an address doesn't start with 'hx'. 81 | keystore["address"] = "axfd7e4560ba363f5aabd32caac7317feeee70ea57" 82 | self.assertRaises(KeyStoreException, is_keystore_file_for_icon, keystore) 83 | 84 | # when an value of key 'coinType' is not same as 'icx'. 85 | keystore["address"] = "hxfd7e4560ba363f5aabd32caac7317feeee70ea57" 86 | keystore["coinType"] = "ic" 87 | self.assertRaises(KeyStoreException, is_keystore_file_for_icon, keystore) 88 | 89 | 90 | if __name__ == "__main__": 91 | main() 92 | -------------------------------------------------------------------------------- /tests/wallet/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | -------------------------------------------------------------------------------- /tests/wallet/test_wallet.py: -------------------------------------------------------------------------------- 1 | import copy 2 | from typing import Union, Dict 3 | from copy import deepcopy 4 | 5 | import pytest 6 | 7 | from iconsdk.wallet.wallet import KeyWallet, public_key_to_address, convert_public_key_format 8 | 9 | 10 | class TestKeyWallet: 11 | 12 | @pytest.mark.parametrize( 13 | "compressed,hexadecimal,ret_type,size", 14 | ( 15 | (True, True, str, 33), 16 | (True, False, bytes, 33), 17 | (False, True, str, 65), 18 | (False, False, bytes, 65), 19 | ) 20 | ) 21 | def test_get_public_key(self, compressed: bool, hexadecimal: bool, ret_type: type, size: int): 22 | wallet: KeyWallet = KeyWallet.create() 23 | public_key: Union[str, bytes] = wallet.get_public_key(compressed, hexadecimal) 24 | assert isinstance(public_key, ret_type) 25 | if hexadecimal: 26 | print(public_key) 27 | pub_key: bytes = bytes.fromhex(public_key) 28 | assert len(pub_key) == size 29 | else: 30 | assert len(public_key) == size 31 | 32 | def test_get_private_key(self): 33 | wallet: KeyWallet = KeyWallet.create() 34 | private_key: str = wallet.get_private_key() 35 | assert isinstance(private_key, str) 36 | assert not private_key.startswith("0x") 37 | 38 | private_key: bytes = wallet.get_private_key(hexadecimal=False) 39 | assert isinstance(private_key, bytes) 40 | assert wallet.private_key == private_key 41 | 42 | def test_private_key(self): 43 | wallet: KeyWallet = KeyWallet.create() 44 | wallet2: KeyWallet = KeyWallet.load(wallet.private_key) 45 | assert wallet == wallet2 46 | 47 | wallet3 = KeyWallet.create() 48 | assert wallet != wallet3 49 | 50 | def test_public_key(self): 51 | wallet: KeyWallet = KeyWallet.create() 52 | public_key: bytes = wallet.public_key 53 | assert isinstance(public_key, bytes) 54 | assert len(public_key) == 65 55 | 56 | def test_to_dict(self): 57 | password = "1234" 58 | wallet: KeyWallet = KeyWallet.create() 59 | jso: Dict[str, str] = wallet.to_dict(password) 60 | assert jso["address"] == wallet.get_address() 61 | assert jso["coinType"] == "icx" 62 | 63 | wallet2 = KeyWallet.from_dict(jso, password) 64 | assert wallet2 == wallet 65 | 66 | def test_copy(self): 67 | wallet = KeyWallet.create() 68 | wallet2 = copy.deepcopy(wallet) 69 | assert wallet == wallet2 70 | 71 | wallet3 = copy.copy(wallet) 72 | assert wallet == wallet3 73 | 74 | def test_hash(self): 75 | wallet = KeyWallet.create() 76 | wallet_dict = {wallet: wallet.public_key} 77 | assert wallet.public_key == wallet_dict[wallet] 78 | 79 | 80 | def test_public_key_to_address(): 81 | wallet: KeyWallet = KeyWallet.create() 82 | address: str = public_key_to_address(wallet.public_key) 83 | assert address == wallet.get_address() 84 | 85 | compressed_public_key: bytes = wallet.get_public_key(compressed=True, hexadecimal=False) 86 | address2: str = public_key_to_address(compressed_public_key) 87 | assert address2 == wallet.get_address() 88 | 89 | 90 | @pytest.mark.parametrize( 91 | "iformat,oformat,size", 92 | ( 93 | (True, True, 33), 94 | (True, False, 65), 95 | (False, True, 33), 96 | (False, False, 65), 97 | ) 98 | ) 99 | def test_convert_public_key_format(iformat: bool, oformat: bool, size: int): 100 | wallet: KeyWallet = KeyWallet.create() 101 | 102 | public_key: bytes = wallet.get_public_key(compressed=iformat, hexadecimal=False) 103 | ret: bytes = convert_public_key_format(public_key, compressed=oformat) 104 | if iformat == oformat: 105 | assert ret == public_key 106 | assert len(ret) == size 107 | assert public_key_to_address(public_key) == public_key_to_address(ret) 108 | -------------------------------------------------------------------------------- /tests/wallet/test_wallet_create.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | from unittest import TestCase, main 17 | 18 | from iconsdk.utils.validation import is_wallet_address 19 | from iconsdk.wallet.wallet import KeyWallet 20 | 21 | 22 | class TestWalletCreate(TestCase): 23 | 24 | def test_wallet_create_successfully(self): 25 | """Case both of each wallets are created successfully without a private key.""" 26 | wallet1 = KeyWallet.create() 27 | wallet2 = KeyWallet.create() 28 | self.assertTrue(wallet1.get_address() != wallet2.get_address()) 29 | self.assertTrue(is_wallet_address(wallet1.get_address())) 30 | self.assertTrue(is_wallet_address(wallet2.get_address())) 31 | 32 | 33 | if __name__ == "__main__": 34 | main() 35 | -------------------------------------------------------------------------------- /tests/wallet/test_wallet_load_by_private_key.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | from unittest import TestCase, main 17 | 18 | from coincurve import PrivateKey 19 | 20 | from iconsdk.utils.validation import is_wallet_address 21 | from iconsdk.wallet.wallet import KeyWallet 22 | 23 | 24 | class TestWalletLoadByPrivateKey(TestCase): 25 | 26 | def test_wallet_load_by_private_key(self): 27 | """A wallet loads by a private key correctly.""" 28 | 29 | # Creates a wallet. 30 | private_key_object = PrivateKey() 31 | private_key: bytes = private_key_object.secret 32 | wallet1 = KeyWallet.load(private_key) 33 | 34 | # Checks a private key as same. 35 | self.assertEqual(private_key.hex(), wallet1.get_private_key()) 36 | 37 | # Checks a wallet's address is correct. 38 | self.assertTrue(is_wallet_address(wallet1.get_address())) 39 | 40 | # Creates the other wallet. 41 | private_key_object2 = PrivateKey() 42 | private_key2: bytes = private_key_object2.secret 43 | wallet2 = KeyWallet.load(private_key2) 44 | 45 | # Checks a private key as same. 46 | self.assertEqual(private_key2.hex(), wallet2.get_private_key()) 47 | 48 | # Checks a wallet's address is correct. 49 | self.assertTrue(is_wallet_address(wallet2.get_address())) 50 | 51 | self.assertNotEqual(private_key2, private_key) 52 | 53 | 54 | if __name__ == "__main__": 55 | main() 56 | -------------------------------------------------------------------------------- /tests/wallet/test_wallet_load_from_keystore_file.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | import pathlib 16 | import re 17 | 18 | import requests_mock 19 | from unittest.mock import patch 20 | 21 | from os import path 22 | from unittest import TestCase, main 23 | 24 | from iconsdk.exception import KeyStoreException 25 | from iconsdk.icon_service import IconService 26 | from iconsdk.providers.http_provider import HTTPProvider 27 | from iconsdk.wallet.wallet import KeyWallet 28 | from tests.example_config import BASE_DOMAIN_URL_V3_FOR_TEST, VERSION_FOR_TEST 29 | 30 | 31 | class TestWalletLoadFromKeystoreFile(TestCase): 32 | TEST_CUR_DIR = path.dirname(__file__) 33 | TEST_KEYSTORE_FILE_PATH = path.abspath(path.join(TEST_CUR_DIR, '../keystore_file/test_keystore.txt')) 34 | 35 | TEST_KEYSTORE_FILE_PASSWORD = "Adas21312**" 36 | matcher = re.compile(re.escape(f"{BASE_DOMAIN_URL_V3_FOR_TEST}/api/v3/") + "?") 37 | 38 | def test_wallet_load_from_keystore_file(self): 39 | """A wallet loads from a keystore file correctly.""" 40 | 41 | # Loads a wallet. 42 | wallet = KeyWallet.load(self.TEST_KEYSTORE_FILE_PATH, self.TEST_KEYSTORE_FILE_PASSWORD) 43 | 44 | # Checks a wallet's address is correct. 45 | self.assertEqual(wallet.get_address(), "hxfd7e4560ba363f5aabd32caac7317feeee70ea57") 46 | 47 | # Loads a wallet using path-like object. 48 | keystore_path = pathlib.Path(self.TEST_KEYSTORE_FILE_PATH) 49 | wallet = KeyWallet.load(keystore_path, self.TEST_KEYSTORE_FILE_PASSWORD) 50 | 51 | # Checks a wallet's address is correct. 52 | self.assertEqual(wallet.get_address(), "hxfd7e4560ba363f5aabd32caac7317feeee70ea57") 53 | 54 | def test_wallet_load_from_invalid_directory(self): 55 | """Case when loading a wallet from a invalid directory not existing.""" 56 | keystore_file_path = path.join(self.TEST_CUR_DIR, "../keystore_file/unknown_folder", "test_keystore.txt") 57 | self.assertRaises(KeyStoreException, KeyWallet.load, keystore_file_path, self.TEST_KEYSTORE_FILE_PASSWORD) 58 | 59 | def test_wallet_load_with_invalid_password(self): 60 | """Case when loading a wallet with a invalid password.""" 61 | password = "1234wrongpassword**" 62 | self.assertRaises(KeyStoreException, KeyWallet.load, self.TEST_KEYSTORE_FILE_PATH, password) 63 | 64 | @patch('iconsdk.providers.http_provider.HTTPProvider._make_id', return_value=1234) 65 | def test_wallet_load_and_call_api(self, _make_id): 66 | """Case when loading a wallet and call an api.""" 67 | # Loads a wallet. 68 | wallet = KeyWallet.load(self.TEST_KEYSTORE_FILE_PATH, self.TEST_KEYSTORE_FILE_PASSWORD) 69 | 70 | # Checks a wallet's address is correct. 71 | self.assertEqual(wallet.get_address(), "hxfd7e4560ba363f5aabd32caac7317feeee70ea57") 72 | 73 | # Calls an api. 74 | icon_service = IconService(HTTPProvider(BASE_DOMAIN_URL_V3_FOR_TEST, VERSION_FOR_TEST)) 75 | with requests_mock.Mocker() as m: 76 | response_json = { 77 | "jsonrpc": "2.0", 78 | "result": hex(0), 79 | "id": 1234 80 | } 81 | m.post(self.matcher, json=response_json) 82 | balance = icon_service.get_balance(wallet.get_address()) 83 | self.assertEqual(balance, 0) 84 | 85 | 86 | if __name__ == "__main__": 87 | main() 88 | -------------------------------------------------------------------------------- /tests/wallet/test_wallet_store.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright 2018 ICON Foundation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | from os import path, remove 17 | from unittest import TestCase, main 18 | 19 | from iconsdk.exception import KeyStoreException 20 | from iconsdk.wallet.wallet import KeyWallet 21 | 22 | 23 | class TestWalletStore(TestCase): 24 | 25 | TEST_CUR_DIR = path.dirname(__file__) 26 | TEST_RIGHT_PATH_OF_NEW_KEYSTORE_FILE = path.abspath( 27 | path.join(TEST_CUR_DIR, '../keystore_file/test_new_keystore.txt')) 28 | TEST_WRONG_PATH_OF_KEYSTORE_FILE = path.abspath(path.join(TEST_CUR_DIR, "../unknown_folder", "test_keystore.txt")) 29 | 30 | TEST_RIGHT_PASSWORD_OF_KEYSTORE_FILE = "Adas21312**" 31 | TEST_WRONG_PASSWORD_OF_KEYSTORE_FILE = "123456" 32 | 33 | def test_wallet_store_successfully(self): 34 | """Creates a wallet and validate the wallet.""" 35 | wallet = KeyWallet.create() 36 | wallet.store(self.TEST_RIGHT_PATH_OF_NEW_KEYSTORE_FILE, self.TEST_RIGHT_PASSWORD_OF_KEYSTORE_FILE) 37 | wallet2 = wallet.load(self.TEST_RIGHT_PATH_OF_NEW_KEYSTORE_FILE, self.TEST_RIGHT_PASSWORD_OF_KEYSTORE_FILE) 38 | 39 | self.assertEqual(wallet.get_address(), wallet2.get_address()) 40 | self.assertEqual(wallet.get_private_key(), wallet2.get_private_key()) 41 | 42 | def test_wallet_store_on_the_wrong_path(self): 43 | """Case When storing a keystore file on a wrong path that does not exist.""" 44 | wallet = KeyWallet.create() 45 | self.assertRaises(KeyStoreException, wallet.store, self.TEST_WRONG_PATH_OF_KEYSTORE_FILE, 46 | self.TEST_RIGHT_PASSWORD_OF_KEYSTORE_FILE) 47 | 48 | def test_wallet_store_with_wrong_password(self): 49 | """Successful Case to store wallet even though entering a invalid password.""" 50 | wallet = KeyWallet.create() 51 | wallet.store(self.TEST_RIGHT_PATH_OF_NEW_KEYSTORE_FILE, self.TEST_WRONG_PASSWORD_OF_KEYSTORE_FILE) 52 | 53 | def test_wallet_store_overwriting(self): 54 | """Case when overwriting the existing keystore file.""" 55 | wallet = KeyWallet.create() 56 | wallet.store(self.TEST_RIGHT_PATH_OF_NEW_KEYSTORE_FILE, self.TEST_RIGHT_PASSWORD_OF_KEYSTORE_FILE) 57 | 58 | wallet2 = KeyWallet.create() 59 | self.assertRaises(KeyStoreException, wallet2.store, self.TEST_RIGHT_PATH_OF_NEW_KEYSTORE_FILE, 60 | self.TEST_RIGHT_PASSWORD_OF_KEYSTORE_FILE) 61 | 62 | def tearDown(self): 63 | # Remove used file. 64 | if path.isfile(self.TEST_RIGHT_PATH_OF_NEW_KEYSTORE_FILE): 65 | remove(self.TEST_RIGHT_PATH_OF_NEW_KEYSTORE_FILE) 66 | 67 | 68 | if __name__ == "__main__": 69 | main() 70 | --------------------------------------------------------------------------------