# pytoniq
[![PyPI version](https://badge.fury.io/py/pytoniq.svg)](https://badge.fury.io/py/pytoniq)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/pytoniq)](https://pypi.org/project/pytoniq/)
[![Downloads](https://static.pepy.tech/badge/pytoniq)](https://pepy.tech/project/pytoniq)
[![Downloads](https://static.pepy.tech/badge/pytoniq/month)](https://pepy.tech/project/pytoniq)
[![](https://img.shields.io/badge/%F0%9F%92%8E-TON-grey)](https://ton.org)
Pytoniq is a Python SDK for the TON Blockchain. This library extends [pytoniq-core](https://github.com/yungwine/pytoniq-core) with native `LiteClient` and `ADNL`.
If you have any questions join Python - TON [developers chat](https://t.me/pythonnton).
## Documentation
[GitBook](https://yungwine.gitbook.io/pytoniq-doc/)
## Installation
```commandline
pip install pytoniq
```
## Examples
You can find them in the [examples](examples/) folder.
## LiteClient
### General LiteClient usage examples
#### Client initializing
```python
from pytoniq import LiteClient
async def main():
client = LiteClient.from_mainnet_config( # choose mainnet, testnet or custom config dict
ls_i=0, # index of liteserver from config
trust_level=2, # trust level to liteserver
timeout=15 # timeout not includes key blocks synchronization as it works in pytonlib
)
await client.connect()
await client.get_masterchain_info()
await client.reconnect() # can reconnect to an exising object if had any errors
await client.close()
""" or use it with context manager: """
async with LiteClient.from_mainnet_config(ls_i=0, trust_level=2, timeout=15) as client:
await client.get_masterchain_info()
```
#### Blocks transactions scanning
See `BlockScanner` code [here](examples/blocks/block_scanner.py).
```python
from pytoniq_core import BlockIdExt
from pytoniq import LiteClient
from examples.blocks.block_scanner import BlockScanner # this import is not available if downloaded from pypi
async def handle_block(block: BlockIdExt):
if block.workchain == -1: # skip masterchain blocks
return
print(block)
transactions = await client.raw_get_block_transactions_ext(block)
for transaction in transactions:
print(transaction.in_msg)
client = LiteClient.from_mainnet_config(ls_i=14, trust_level=0, timeout=20)
async def main():
await client.connect()
await BlockScanner(client=client, block_handler=handle_block).run()
```
## LiteBalancer
`LiteBalancer` is constantly pinging LiteServers to identify "alive" peers.
When you make a request through `LiteBalancer`, it forwards the request to the "best" peer -
the "alive" peer with the maximum last masterchain block seqno among all and minimum average response time.
`LiteBalancer` can also retry the request if a `asyncio.TimeoutError` occurs, but this must be explicitly set using the
`LiteBalancer.set_max_retries(retries_num)` method.
```python
client = LiteBalancer.from_mainnet_config(trust_level=1)
await client.start_up()
result = await client.run_get_method(address='EQBvW8Z5huBkMJYdnfAEM5JqTNkuWX3diqYENkWsIL0XggGG', method='seqno', stack=[])
await client.close_all()
""" or use it with context manager: """
async with LiteBalancer.from_mainnet_config(trust_level=1) as client:
result = await client.run_get_method(address='EQBvW8Z5huBkMJYdnfAEM5JqTNkuWX3diqYENkWsIL0XggGG', method='seqno', stack=[])
```
Moreover, one of the most important features of `LiteBalancer` is that it detects [archival](https://docs.ton.org/participate/run-nodes/archive-node#overview) LiteServers,
so you can do requests only to archival LiteServers providing `True` for argument `only_archive` in **any** method:
```python
# ask for very very old block
blk, _ = await client.lookup_block(-1, -2**63, 100, only_archive=True)
# ask for old block and run get method for that block:
blk, _ = await client.lookup_block(-1, -2**63, 25000000, only_archive=True)
result = await client.run_get_method(address='EQBvW8Z5huBkMJYdnfAEM5JqTNkuWX3diqYENkWsIL0XggGG', method='seqno',
stack=[], block=blk, only_archive=True)
```
### Blockstore
The library can prove all data it receives from a Liteserver (Learn about trust levels [here](https://yungwine.gitbook.io/pytoniq-doc/liteclient/trust-levels)).
If you want to use `LiteClient` or `LiteBalancer` with the zero trust level, at the first time run library will prove block link from the `init_block` to the last masterchain block.
Last proved blocks will be stored in the `.blockstore` folder. The file data contains `ttl` and `gen_utime` of the last synced key block, its data serialized according to the `BlockIdExt` TL scheme (but in big–endian), last synced masterchain block data.
Filename is first 88 bytes of data described above with init block hash.
## ADNL
```python
from pytoniq.adnl.adnl import AdnlTransport, Node
adnl = AdnlTransport(timeout=3)
# start adnl receiving server
await adnl.start()
# take peer from public config
peer = Node('172.104.59.125', 14432, "/YDNd+IwRUgL0mq21oC0L3RxrS8gTu0nciSPUrhqR78=", adnl)
await adnl.connect_to_peer(peer)
# or await peer.connect()
await peer.disconnect()
# send pings
await asyncio.sleep(10)
# stop adnl receiving server
await adnl.close()
```
## DHT
```python
import time
from pytoniq.adnl.adnl import AdnlTransport
from pytoniq.adnl.dht import DhtClient, DhtNode
adnl = AdnlTransport(timeout=5)
client = DhtClient.from_mainnet_config(adnl)
await adnl.start()
foundation_adnl_addr = '516618cf6cbe9004f6883e742c9a2e3ca53ed02e3e36f4cef62a98ee1e449174'
resp = await client.find_value(key=DhtClient.get_dht_key_id(bytes.fromhex(foundation_adnl_addr)))
print(resp)
# {'@type': 'dht.valueFound', 'value': {'key': {'key': {'id': '516618cf6cbe9004f6883e742c9a2e3ca53ed02e3e36f4cef62a98ee1e449174', 'name': b'address', 'idx': 0, '@type': 'dht.key'}, 'id': {'key': '927d3e71e3ce651c3f172134d39163f70e4c792169e39f3d520bfad9388ad4ca', '@type': 'pub.ed25519'}, 'update_rule': {'@type': 'dht.updateRule.signature'}, 'signature': b"g\x08\xf8yo\xed1\xb83\x17\xb9\x10\xb4\x8f\x00\x17]D\xd2\xae\xfa\x87\x9f\xf7\xfa\x192\x971\xee'2\x83\x0fk\x03w\xbb0\xfcU\xc8\x89Zm\x8e\xba\xce \xfc\xde\xf2F\xdb\x0cI*\xe0\xaeN\xef\xc2\x9e\r", '@type': 'dht.keyDescription'}, 'value': {'@type': 'adnl.addressList', 'addrs': [{'@type': 'adnl.address.udp', 'ip': -1537433966, 'port': 3333}], 'version': 1694227845, 'reinit_date': 1694227845, 'priority': 0, 'expire_at': 0}, 'ttl': 1695832194, 'signature': b'z\x8aW\x80k\xceXQ\xff\xb9D{C\x98T\x02e\xef&\xfc\xb6\xde\x80y\xf7\xb4\x92\xae\xd2\xd0\xbakU}3\xfa\xec\x03\xb6v\x98\xb0\xcb\xe8\x05\xb9\xd0\x07o\xb6\xa0)I\x17\xcb\x1a\xc4(Dt\xe6y\x18\x0b', '@type': 'dht.value'}}
key = client.get_dht_key(id_=adnl.client.get_key_id())
ts = int(time.time())
value_data = {
'addrs': [
{
"@type": "adnl.address.udp",
"ip": 1111111,
"port": 12000
}
],
'version': ts,
'reinit_date': ts,
'priority': 0,
'expire_at': 0,
}
value = client.schemas.serialize(client.schemas.get_by_name('adnl.addressList'), value_data)
stored = await client.store_value( # store our address list in dht as value
key=key,
value=value,
private_key=adnl.client.ed25519_private.encode(),
ttl=100,
try_find_after=False
)
print(stored) # True if value was stored, False otherwise
# disconnect from all peers
await client.close()
```
Raw data
{
"_id": null,
"home_page": "https://github.com/yungwine/pytoniq",
"name": "pytoniq",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.9",
"maintainer_email": null,
"keywords": null,
"author": "Maksim Kurbatov",
"author_email": "cyrbatoff@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/26/a0/4d3f4f8d143dc242e544238389dcb3d539409cac764b8e06c60d8f893558/pytoniq-0.1.40.tar.gz",
"platform": null,
"description": "# pytoniq\n\n[![PyPI version](https://badge.fury.io/py/pytoniq.svg)](https://badge.fury.io/py/pytoniq) \n[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/pytoniq)](https://pypi.org/project/pytoniq/)\n[![Downloads](https://static.pepy.tech/badge/pytoniq)](https://pepy.tech/project/pytoniq)\n[![Downloads](https://static.pepy.tech/badge/pytoniq/month)](https://pepy.tech/project/pytoniq)\n[![](https://img.shields.io/badge/%F0%9F%92%8E-TON-grey)](https://ton.org)\n\nPytoniq is a Python SDK for the TON Blockchain. This library extends [pytoniq-core](https://github.com/yungwine/pytoniq-core) with native `LiteClient` and `ADNL`.\n\nIf you have any questions join Python - TON [developers chat](https://t.me/pythonnton).\n\n## Documentation\n[GitBook](https://yungwine.gitbook.io/pytoniq-doc/)\n\n## Installation\n\n```commandline\npip install pytoniq \n```\n\n## Examples\nYou can find them in the [examples](examples/) folder.\n\n## LiteClient\n\n### General LiteClient usage examples\n\n#### Client initializing\n\n```python\nfrom pytoniq import LiteClient\n\n\nasync def main():\n client = LiteClient.from_mainnet_config( # choose mainnet, testnet or custom config dict\n ls_i=0, # index of liteserver from config\n trust_level=2, # trust level to liteserver\n timeout=15 # timeout not includes key blocks synchronization as it works in pytonlib\n )\n\n await client.connect()\n \n await client.get_masterchain_info()\n\n await client.reconnect() # can reconnect to an exising object if had any errors\n\n await client.close()\n \n \"\"\" or use it with context manager: \"\"\"\n async with LiteClient.from_mainnet_config(ls_i=0, trust_level=2, timeout=15) as client:\n await client.get_masterchain_info()\n\n```\n\n#### Blocks transactions scanning\n\nSee `BlockScanner` code [here](examples/blocks/block_scanner.py).\n\n```python\nfrom pytoniq_core import BlockIdExt\nfrom pytoniq import LiteClient\nfrom examples.blocks.block_scanner import BlockScanner # this import is not available if downloaded from pypi\n\nasync def handle_block(block: BlockIdExt):\n if block.workchain == -1: # skip masterchain blocks\n return\n print(block)\n transactions = await client.raw_get_block_transactions_ext(block)\n for transaction in transactions:\n print(transaction.in_msg)\n\n\nclient = LiteClient.from_mainnet_config(ls_i=14, trust_level=0, timeout=20)\n\n\nasync def main():\n\n await client.connect()\n await BlockScanner(client=client, block_handler=handle_block).run()\n```\n\n## LiteBalancer\n\n`LiteBalancer` is constantly pinging LiteServers to identify \"alive\" peers.\nWhen you make a request through `LiteBalancer`, it forwards the request to the \"best\" peer - \nthe \"alive\" peer with the maximum last masterchain block seqno among all and minimum average response time.\n\n`LiteBalancer` can also retry the request if a `asyncio.TimeoutError` occurs, but this must be explicitly set using the\n`LiteBalancer.set_max_retries(retries_num)` method.\n\n```python\nclient = LiteBalancer.from_mainnet_config(trust_level=1)\n\nawait client.start_up()\n\nresult = await client.run_get_method(address='EQBvW8Z5huBkMJYdnfAEM5JqTNkuWX3diqYENkWsIL0XggGG', method='seqno', stack=[])\n\nawait client.close_all()\n\n\"\"\" or use it with context manager: \"\"\"\n\nasync with LiteBalancer.from_mainnet_config(trust_level=1) as client:\n result = await client.run_get_method(address='EQBvW8Z5huBkMJYdnfAEM5JqTNkuWX3diqYENkWsIL0XggGG', method='seqno', stack=[])\n\n```\n\nMoreover, one of the most important features of `LiteBalancer` is that it detects [archival](https://docs.ton.org/participate/run-nodes/archive-node#overview) LiteServers,\nso you can do requests only to archival LiteServers providing `True` for argument `only_archive` in **any** method:\n\n```python\n# ask for very very old block\nblk, _ = await client.lookup_block(-1, -2**63, 100, only_archive=True) \n\n# ask for old block and run get method for that block:\nblk, _ = await client.lookup_block(-1, -2**63, 25000000, only_archive=True)\nresult = await client.run_get_method(address='EQBvW8Z5huBkMJYdnfAEM5JqTNkuWX3diqYENkWsIL0XggGG', method='seqno',\n stack=[], block=blk, only_archive=True)\n\n```\n\n\n### Blockstore\nThe library can prove all data it receives from a Liteserver (Learn about trust levels [here](https://yungwine.gitbook.io/pytoniq-doc/liteclient/trust-levels)).\nIf you want to use `LiteClient` or `LiteBalancer` with the zero trust level, at the first time run library will prove block link from the `init_block` to the last masterchain block.\nLast proved blocks will be stored in the `.blockstore` folder. The file data contains `ttl` and `gen_utime` of the last synced key block, its data serialized according to the `BlockIdExt` TL scheme (but in big\u2013endian), last synced masterchain block data. \nFilename is first 88 bytes of data described above with init block hash.\n\n## ADNL\n\n```python\nfrom pytoniq.adnl.adnl import AdnlTransport, Node\n\nadnl = AdnlTransport(timeout=3)\n\n# start adnl receiving server\nawait adnl.start()\n\n# take peer from public config\npeer = Node('172.104.59.125', 14432, \"/YDNd+IwRUgL0mq21oC0L3RxrS8gTu0nciSPUrhqR78=\", adnl)\nawait adnl.connect_to_peer(peer)\n# or await peer.connect()\n\nawait peer.disconnect()\n\n# send pings\nawait asyncio.sleep(10)\n\n# stop adnl receiving server\nawait adnl.close()\n```\n\n## DHT\n\n```python\nimport time\n\nfrom pytoniq.adnl.adnl import AdnlTransport\nfrom pytoniq.adnl.dht import DhtClient, DhtNode\n\n\nadnl = AdnlTransport(timeout=5)\nclient = DhtClient.from_mainnet_config(adnl)\n\nawait adnl.start()\n\nfoundation_adnl_addr = '516618cf6cbe9004f6883e742c9a2e3ca53ed02e3e36f4cef62a98ee1e449174'\nresp = await client.find_value(key=DhtClient.get_dht_key_id(bytes.fromhex(foundation_adnl_addr)))\nprint(resp)\n# {'@type': 'dht.valueFound', 'value': {'key': {'key': {'id': '516618cf6cbe9004f6883e742c9a2e3ca53ed02e3e36f4cef62a98ee1e449174', 'name': b'address', 'idx': 0, '@type': 'dht.key'}, 'id': {'key': '927d3e71e3ce651c3f172134d39163f70e4c792169e39f3d520bfad9388ad4ca', '@type': 'pub.ed25519'}, 'update_rule': {'@type': 'dht.updateRule.signature'}, 'signature': b\"g\\x08\\xf8yo\\xed1\\xb83\\x17\\xb9\\x10\\xb4\\x8f\\x00\\x17]D\\xd2\\xae\\xfa\\x87\\x9f\\xf7\\xfa\\x192\\x971\\xee'2\\x83\\x0fk\\x03w\\xbb0\\xfcU\\xc8\\x89Zm\\x8e\\xba\\xce \\xfc\\xde\\xf2F\\xdb\\x0cI*\\xe0\\xaeN\\xef\\xc2\\x9e\\r\", '@type': 'dht.keyDescription'}, 'value': {'@type': 'adnl.addressList', 'addrs': [{'@type': 'adnl.address.udp', 'ip': -1537433966, 'port': 3333}], 'version': 1694227845, 'reinit_date': 1694227845, 'priority': 0, 'expire_at': 0}, 'ttl': 1695832194, 'signature': b'z\\x8aW\\x80k\\xceXQ\\xff\\xb9D{C\\x98T\\x02e\\xef&\\xfc\\xb6\\xde\\x80y\\xf7\\xb4\\x92\\xae\\xd2\\xd0\\xbakU}3\\xfa\\xec\\x03\\xb6v\\x98\\xb0\\xcb\\xe8\\x05\\xb9\\xd0\\x07o\\xb6\\xa0)I\\x17\\xcb\\x1a\\xc4(Dt\\xe6y\\x18\\x0b', '@type': 'dht.value'}}\n\nkey = client.get_dht_key(id_=adnl.client.get_key_id())\nts = int(time.time())\nvalue_data = {\n 'addrs': [\n {\n \"@type\": \"adnl.address.udp\",\n \"ip\": 1111111,\n \"port\": 12000\n }\n ],\n 'version': ts,\n 'reinit_date': ts,\n 'priority': 0,\n 'expire_at': 0,\n}\n\nvalue = client.schemas.serialize(client.schemas.get_by_name('adnl.addressList'), value_data)\n\nstored = await client.store_value( # store our address list in dht as value\n key=key,\n value=value,\n private_key=adnl.client.ed25519_private.encode(),\n ttl=100,\n try_find_after=False\n)\n\nprint(stored) # True if value was stored, False otherwise\n\n# disconnect from all peers\nawait client.close()\n```\n",
"bugtrack_url": null,
"license": null,
"summary": "TON Blockchain SDK",
"version": "0.1.40",
"project_urls": {
"Homepage": "https://github.com/yungwine/pytoniq"
},
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "be7259e05c74c03772a579c4c491a9d8353d28c3fda80ac05ba6805a9e1d8129",
"md5": "f563ae6b5164207afe3a3f521898ca8d",
"sha256": "5d10927abd199937094ed76a7f586d670f04c12138ee28fe149295987240f58b"
},
"downloads": -1,
"filename": "pytoniq-0.1.40-py3-none-any.whl",
"has_sig": false,
"md5_digest": "f563ae6b5164207afe3a3f521898ca8d",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9",
"size": 47454,
"upload_time": "2024-10-08T07:54:32",
"upload_time_iso_8601": "2024-10-08T07:54:32.344100Z",
"url": "https://files.pythonhosted.org/packages/be/72/59e05c74c03772a579c4c491a9d8353d28c3fda80ac05ba6805a9e1d8129/pytoniq-0.1.40-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "26a04d3f4f8d143dc242e544238389dcb3d539409cac764b8e06c60d8f893558",
"md5": "f1205fcf71a6ed88d89dca51629dba41",
"sha256": "c532a82cc990d9c73a2606e26742b98a2b9e0aa39bd34d2cb2353bdb89b8488d"
},
"downloads": -1,
"filename": "pytoniq-0.1.40.tar.gz",
"has_sig": false,
"md5_digest": "f1205fcf71a6ed88d89dca51629dba41",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9",
"size": 44071,
"upload_time": "2024-10-08T07:54:34",
"upload_time_iso_8601": "2024-10-08T07:54:34.445043Z",
"url": "https://files.pythonhosted.org/packages/26/a0/4d3f4f8d143dc242e544238389dcb3d539409cac764b8e06c60d8f893558/pytoniq-0.1.40.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-10-08 07:54:34",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "yungwine",
"github_project": "pytoniq",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"requirements": [],
"lcname": "pytoniq"
}