Skip to content

Commit

Permalink
Merge pull request #18 from psylopunk/psylopunk/v0.25
Browse files Browse the repository at this point in the history
v0.25: timeouts fix, -1 wc utils.cell.read_address, refactor
  • Loading branch information
psylopunk authored Nov 7, 2022
2 parents 371e862 + ecb4e3f commit c6dafe4
Show file tree
Hide file tree
Showing 12 changed files with 67 additions and 59 deletions.
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
crc16==0.1.1
tonsdk==1.0.6
tonsdk==1.0.7
requests==2.27.1
pynacl==1.5.0
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setuptools.setup(
name="ton",
version="0.24",
version="0.25",
author="psylopunk",
author_email="[email protected]",
description="Python client for The Open Network",
Expand All @@ -16,7 +16,7 @@
install_requires=[
'crc16>=0.1.1',
'requests>=2.27.1',
'tonsdk>=1.0.6',
'tonsdk>=1.0.7',
'PyNaCl>=1.5.0'
],
package_data={
Expand Down
4 changes: 2 additions & 2 deletions ton/account/ft_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ async def get_wallet_address(self, owner_address, **kwargs):
'address': read_address(Cell.one_from_boc(b64decode(response.stack[0].cell.bytes)))
}

async def get_wallet_data(self):
response = await self.run_get_method('get_wallet_data')
async def get_wallet_data(self, **kwargs):
response = await self.run_get_method('get_wallet_data', **kwargs)
if response.exit_code != 0:
raise Exception('get_wallet_data exit_code: {}'.format(response.exit_code))

Expand Down
16 changes: 8 additions & 8 deletions ton/account/smc_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@


class SmcMethods:
async def load_smc(self):
async def load_smc(self, **kwargs):
self.smc_id = (await self.client.tonlib_wrapper.execute(
Smc_Load(self.account_address)
Smc_Load(self.account_address), **kwargs
)).id

async def run_get_method(self, method: Union[str, int], stack: list = [], force=False):
async def run_get_method(self, method: Union[str, int], stack: list = [], force=False, **kwargs):
if self.smc_id is None or force is True:
await self.load_smc()
await self.load_smc(**kwargs)

return await self.client.tonlib_wrapper.execute(
Smc_RunGetMethod(self.smc_id, method, stack)
Smc_RunGetMethod(self.smc_id, method, stack), **kwargs
)

async def send_message(self, body: bytes):
async def send_message(self, body: bytes, **kwargs):
return await self.client.tonlib_wrapper.execute(
Raw_CreateAndSendMessage(self.account_address, body)
)
Raw_CreateAndSendMessage(self.account_address, body), **kwargs
)
19 changes: 10 additions & 9 deletions ton/account/state_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,30 @@
from ..tl.types import Internal_TransactionId
from ..utils import sha256, contracts


class StateMethods:
async def load_state(self):
async def load_state(self, **kwargs):
self.state = await self.client.tonlib_wrapper.execute(
Raw_GetAccountState(self.account_address)
Raw_GetAccountState(self.account_address), **kwargs
)

async def get_state(self, force=False):
async def get_state(self, force=False, **kwargs):
if self.state is None or force:
await self.load_state()
await self.load_state(**kwargs)

return self.state

async def detect_type(self):
state = await self.get_state()
async def detect_type(self, **kwargs):
state = await self.get_state(**kwargs)
return contracts.get(sha256(state.code), None)

async def get_balance(self):
async def get_balance(self, **kwargs):
return int(
(await self.get_state(force=True)).balance # in nanocoins
(await self.get_state(force=True, **kwargs)).balance # nanoTONs
)

async def get_transactions(self, from_transaction_lt=None, from_transaction_hash=None, to_transaction_lt=0, limit=10):
if from_transaction_lt == None or from_transaction_hash == None:
if from_transaction_lt is None or from_transaction_hash is None:
state = await self.get_state(force=True)
from_transaction_lt, from_transaction_hash = state.last_transaction_id.lt, state.last_transaction_id.hash

Expand Down
8 changes: 4 additions & 4 deletions ton/account/wallet_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,22 +97,22 @@ async def transfer_nft(
return await self.transfer(nft_address, self.client.to_nano(0.05), data=body)


async def seqno(self):
result = await self.run_get_method('seqno', force=True)
async def seqno(self, **kwargs):
result = await self.run_get_method('seqno', force=True, **kwargs)
if result.exit_code != 0:
return 0

return int(result.stack[0].number.number)


async def get_public_key(self):
async def get_public_key(self, **kwargs):
if hasattr(self, 'key'):
return b64decode(
await self.client.export_key(InputKeyRegular(self.key, local_password=self.__dict__.get('local_password')))
)
else:
try:
result = await self.run_get_method('get_public_key')
result = await self.run_get_method('get_public_key', **kwargs)
assert result.exit_code == 0, 'get_public_key failed'
return int(result.stack[0].number.number).to_bytes(32, 'big')
except Exception as e:
Expand Down
21 changes: 10 additions & 11 deletions ton/client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ def __init__(
config='https://ton.org/global-config.json',
keystore=None,
workchain_id=0,
verbosity_level=0
verbosity_level=0,
default_timeout=10
):
if keystore is None: # while memory keystore libtonlibjson bug keep
if '.keystore' not in os.listdir(path='.'): os.system('mkdir .keystore')
if keystore is None: # while memory keystore libtonlibjson bug keep
keystore = '.keystore'

self.loop = None
Expand All @@ -33,23 +33,22 @@ def __init__(
self.keystore = keystore
self.workchain_id = workchain_id
self.verbosity_level = verbosity_level
self.default_timeout = default_timeout

self.queue = []
self.queue = [] # legacy
self.lock = asyncio.Lock()
self.locked = False

async def find_account(self, account_address: Union[AccountAddress, str], preload_state: bool=True):
async def find_account(self, account_address: Union[AccountAddress, str], preload_state: bool=True, **kwargs):
"""
Getting a Account object by account address
Getting an Account object by account address
:param account_address:
:return: Account
"""

account = Account(account_address, client=self)
if preload_state: await account.load_state()
return account
if preload_state:
await account.load_state(**kwargs)

# TODO: remove in next version
async def wallet_from_exported_key(self, exported_key: str, revision: int=0, workchain_id: int=None):
raise Exception('TonlibClient.wallet_from_exported_key is deprecated, try Tonlibclient.find_wallet instead')
return account
8 changes: 4 additions & 4 deletions ton/client/converter_methods.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

class ConverterMethods:
def from_nano(self, value):
return round(value / 10 ** 9, 9)
def from_nano(self, value) -> float:
return round(int(value) / 10 ** 9, 9)

def to_nano(self, value):
return int(value * (10 ** 9))
def to_nano(self, value) -> int:
return int(float(value) * (10 ** 9))
4 changes: 2 additions & 2 deletions ton/client/function_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ async def set_verbosity_level(self, level):
)


async def send_boc(self, message: bytes):
async def send_boc(self, message: bytes, **kwargs):
"""
Sending a message to the network
Expand All @@ -18,7 +18,7 @@ async def send_boc(self, message: bytes):
"""

query = Raw_SendMessage(message)
return await self.tonlib_wrapper.execute(query)
return await self.tonlib_wrapper.execute(query, **kwargs)


async def create_new_key(self, mnemonic_password='', random_extra_seed=None, local_password=None):
Expand Down
9 changes: 5 additions & 4 deletions ton/client/tonlib_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

logger = getLogger('ton')


class TonlibMethods:
@classmethod
def enable_unaudited_binaries(cls):
Expand All @@ -24,15 +25,15 @@ def local_config(self):
local['liteservers'] = [local['liteservers'][self.ls_index]]
return local

async def execute(self, query, timeout=30):
async def execute(self, query, timeout=None):
"""
Direct request to liteserver
:param query: dict or TLObject
:param timeout:
:return: TLObject
"""
result = await self.tonlib_wrapper.execute(query, timeout=timeout)
result = await self.tonlib_wrapper.execute(query, timeout=(timeout or self.default_timeout))
if result.type == 'error':
raise Exception(result.message)

Expand Down Expand Up @@ -63,7 +64,7 @@ async def init_tonlib(self, cdll_path=None):

cdll_path = get_tonlib_path()

wrapper = TonLib(self.loop, self.ls_index, cdll_path, self.verbosity_level)
wrapper = TonLib(self.loop, self.ls_index, cdll_path, self.verbosity_level, default_timeout=self.default_timeout)
keystore_obj = {
'@type': 'keyStoreTypeDirectory',
'directory': self.keystore
Expand Down Expand Up @@ -109,4 +110,4 @@ async def reconnect(self):
logger.info(f'Client #{self.ls_index:03d} reconnecting')
self.tonlib_wrapper.shutdown_state = "started"
await self.init_tonlib()
logger.info(f'Client #{self.ls_index:03d} reconnected')
logger.info(f'Client #{self.ls_index:03d} reconnected')
27 changes: 16 additions & 11 deletions ton/tonlibjson.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@
class TonlibException(Exception):
pass


class TonlibNoResponse(TonlibException):
def __str__(self):
return 'tonlibjson did not respond'


class TonlibError(TonlibException):
def __init__(self, result):
self.result = result
Expand All @@ -33,18 +35,23 @@ def code(self):
def __str__(self):
return self.result.get('message')


class LiteServerTimeout(TonlibError):
pass


class BlockNotFound(TonlibError):
pass


class BlockDeleted(TonlibError):
pass


class ExternalMessageNotAccepted(TonlibError):
pass


def parse_tonlib_error(result):
if result.get('@type') == 'error':
message = result.get('message')
Expand All @@ -59,6 +66,7 @@ def parse_tonlib_error(result):
return TonlibError(result)
return None


def get_tonlib_path():
arch_name = platform.system().lower()
machine = platform.machine().lower()
Expand All @@ -76,15 +84,13 @@ def get_tonlib_path():


class TonLib:
def __init__(self, loop, ls_index, cdll_path=None, verbosity_level=0):
def __init__(self, loop, ls_index, cdll_path=None, verbosity_level=0, default_timeout=None):
self.loop = loop
self.default_timeout = default_timeout

cdll_path = get_tonlib_path() if not cdll_path else cdll_path
tonlib = CDLL(cdll_path)

# tonlib_client_set_verbosity_level = tonlib.tonlib_client_set_verbosity_level
# tonlib_client_set_verbosity_level.restype = None
# tonlib_client_set_verbosity_level.argtypes = [c_int]

tonlib_json_client_create = tonlib.tonlib_client_json_create
tonlib_json_client_create.restype = c_void_p
tonlib_json_client_create.argtypes = []
Expand Down Expand Up @@ -140,7 +146,7 @@ def send(self, query):
logger.error(f"Exception in tonlibjson.send: {traceback.format_exc()}")
raise RuntimeError(f'Error in tonlibjson.send: {ee}')

def receive(self, timeout=10):
def receive(self, timeout=30):
result = None
try:
result = self._tonlib_json_client_receive(self._client, timeout) # time.sleep # asyncio.sleep
Expand All @@ -151,7 +157,7 @@ def receive(self, timeout=10):
result = json.loads(result.decode('utf-8'))
return result

def _execute(self, query, timeout=10):
def _execute(self, query, timeout=30):
if not self._is_working:
raise RuntimeError(f"TonLib failed with state: {self._state}")

Expand All @@ -163,7 +169,7 @@ def _execute(self, query, timeout=10):
self.loop.run_in_executor(None, lambda: self.send(query))
return future_result

async def execute(self, query, timeout=10):
async def execute(self, query, timeout=30):
logger.debug(f'SENT' + '\n' + f'{query}')
if isinstance(query, TLObject): query = query.to_json()
result = await self._execute(query, timeout=timeout)
Expand Down Expand Up @@ -206,15 +212,14 @@ def cancel_futures(self, cancel_all=False):

# tasks
async def read_results(self):
timeout = 1
delta = 5
timeout = self.default_timeout or 1
receive_func = functools.partial(self.receive, timeout)
try:
while self._is_working and not self._is_closing:
# return reading result
result = None
try:
result = await asyncio.wait_for(self.loop.run_in_executor(None, receive_func), timeout=timeout + delta)
result = await asyncio.wait_for(self.loop.run_in_executor(None, receive_func), timeout=timeout)
except asyncio.TimeoutError:
logger.critical(f"Tonlib #{self.ls_index:03d} stuck (timeout error)")
self._state = "stuck"
Expand Down
4 changes: 3 additions & 1 deletion ton/utils/cell.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@
def read_address(cell):
data = ''.join([str(cell.bits.get(x)) for x in range(cell.bits.length)])
if len(data) < 267: return None
return Address(f"{int(data[3:11], 2)}:{int(data[11:11+256], 2).to_bytes(32, 'big').hex()}")
wc = int(data[3:11], 2)
hashpart = int(data[11:11+256], 2).to_bytes(32, 'big').hex()
return Address(f"{wc if wc != 255 else -1}:{hashpart}")

0 comments on commit c6dafe4

Please sign in to comment.