tonutils 2.0.1b4__tar.gz → 2.0.1b5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tonutils-2.0.1b4/tonutils.egg-info → tonutils-2.0.1b5}/PKG-INFO +1 -1
- tonutils-2.0.1b5/tonutils/__meta__.py +1 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/base.py +5 -1
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/tools/block_scanner/__init__.py +1 -11
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/tools/block_scanner/scanner.py +3 -5
- tonutils-2.0.1b5/tonutils/tools/block_scanner/traversal.py +97 -0
- tonutils-2.0.1b5/tonutils/tools/block_scanner/where.py +53 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5/tonutils.egg-info}/PKG-INFO +1 -1
- tonutils-2.0.1b4/tonutils/__meta__.py +0 -1
- tonutils-2.0.1b4/tonutils/tools/block_scanner/traversal.py +0 -96
- tonutils-2.0.1b4/tonutils/tools/block_scanner/where.py +0 -151
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/LICENSE +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/README.md +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/pyproject.toml +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/setup.cfg +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/__init__.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/cli.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/__init__.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/adnl/__init__.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/adnl/balancer.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/adnl/client.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/adnl/provider/__init__.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/adnl/provider/config.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/adnl/provider/models.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/adnl/provider/provider.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/adnl/provider/transport.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/adnl/provider/workers/__init__.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/adnl/provider/workers/base.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/adnl/provider/workers/pinger.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/adnl/provider/workers/reader.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/adnl/provider/workers/updater.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/adnl/utils.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/http/__init__.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/http/balancer.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/http/clients/__init__.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/http/clients/chainstack.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/http/clients/quicknode.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/http/clients/tatum.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/http/clients/tonapi.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/http/clients/toncenter.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/http/providers/__init__.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/http/providers/base.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/http/providers/response.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/http/providers/tonapi/__init__.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/http/providers/tonapi/models.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/http/providers/tonapi/provider.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/http/providers/toncenter/__init__.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/http/providers/toncenter/models.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/http/providers/toncenter/provider.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/http/utils.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/limiter.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/clients/protocol.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/__init__.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/base.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/codes.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/dns/__init__.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/dns/collection.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/dns/item.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/dns/methods.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/dns/tlb.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/jetton/__init__.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/jetton/master.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/jetton/methods.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/jetton/tlb.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/jetton/wallet.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/nft/__init__.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/nft/collection.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/nft/item.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/nft/methods.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/nft/tlb.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/opcodes.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/protocol.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/telegram/__init__.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/telegram/collection.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/telegram/item.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/telegram/methods.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/telegram/tlb.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/vanity/__init__.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/vanity/models.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/vanity/tlb.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/vanity/vanity.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/versions.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/wallet/__init__.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/wallet/base.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/wallet/configs.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/wallet/messages.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/wallet/methods.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/wallet/params.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/wallet/protocol.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/wallet/tlb.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/wallet/versions/__init__.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/wallet/versions/hw.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/wallet/versions/pp.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/wallet/versions/v1.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/wallet/versions/v2.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/wallet/versions/v3.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/wallet/versions/v4.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/contracts/wallet/versions/v5.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/exceptions.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/py.typed +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/tonconnect/__init__.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/tools/__init__.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/tools/block_scanner/annotations.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/tools/block_scanner/dispatcher.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/tools/block_scanner/events.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/tools/status_monitor/__init__.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/tools/status_monitor/console.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/tools/status_monitor/models.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/tools/status_monitor/monitor.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/types.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils/utils.py +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils.egg-info/SOURCES.txt +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils.egg-info/dependency_links.txt +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils.egg-info/entry_points.txt +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils.egg-info/requires.txt +0 -0
- {tonutils-2.0.1b4 → tonutils-2.0.1b5}/tonutils.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.0.1b5"
|
|
@@ -272,7 +272,11 @@ class BaseClient(abc.ABC):
|
|
|
272
272
|
|
|
273
273
|
blen = len(domain) * 8
|
|
274
274
|
rlen = t.cast(int, res[0])
|
|
275
|
-
|
|
275
|
+
|
|
276
|
+
cell = res[1]
|
|
277
|
+
|
|
278
|
+
if cell is None:
|
|
279
|
+
return None
|
|
276
280
|
|
|
277
281
|
if rlen % 8 != 0 or rlen > blen:
|
|
278
282
|
raise ValueError(f"Invalid resolved length: result {rlen}, bytes {blen}.")
|
|
@@ -4,13 +4,7 @@ from .events import (
|
|
|
4
4
|
TransactionsEvent,
|
|
5
5
|
)
|
|
6
6
|
from .scanner import BlockScanner
|
|
7
|
-
from .where import
|
|
8
|
-
Where,
|
|
9
|
-
comment,
|
|
10
|
-
destination,
|
|
11
|
-
opcode,
|
|
12
|
-
sender,
|
|
13
|
-
)
|
|
7
|
+
from .where import Where
|
|
14
8
|
|
|
15
9
|
|
|
16
10
|
__all__ = [
|
|
@@ -19,8 +13,4 @@ __all__ = [
|
|
|
19
13
|
"TransactionEvent",
|
|
20
14
|
"TransactionsEvent",
|
|
21
15
|
"Where",
|
|
22
|
-
"comment",
|
|
23
|
-
"destination",
|
|
24
|
-
"opcode",
|
|
25
|
-
"sender",
|
|
26
16
|
]
|
|
@@ -186,8 +186,9 @@ class BlockScanner:
|
|
|
186
186
|
get_header=self._client.get_block_header,
|
|
187
187
|
)
|
|
188
188
|
)
|
|
189
|
+
# Update seen_seqno after collecting blocks for this shard
|
|
190
|
+
shards_seqno[self._traversal.shard_key(shard_tip)] = shard_tip.seqno
|
|
189
191
|
|
|
190
|
-
blocks.sort(key=lambda b: (b.workchain, b.shard, b.seqno))
|
|
191
192
|
return blocks
|
|
192
193
|
|
|
193
194
|
def _emit_block(self, mc_block: BlockIdExt, block: BlockIdExt) -> None:
|
|
@@ -239,12 +240,9 @@ class BlockScanner:
|
|
|
239
240
|
self,
|
|
240
241
|
mc_block: BlockIdExt,
|
|
241
242
|
block: BlockIdExt,
|
|
242
|
-
shards_seqno: t.Dict[t.Tuple[int, int], int],
|
|
243
243
|
) -> None:
|
|
244
244
|
"""Process shard block and emit events for block + transactions."""
|
|
245
245
|
self._ensure_running()
|
|
246
|
-
|
|
247
|
-
shards_seqno[self._traversal.shard_key(block)] = block.seqno
|
|
248
246
|
self._emit_block(mc_block, block)
|
|
249
247
|
|
|
250
248
|
if not self._include_transactions:
|
|
@@ -304,7 +302,7 @@ class BlockScanner:
|
|
|
304
302
|
shards_seqno=state.shards_seqno,
|
|
305
303
|
)
|
|
306
304
|
for block in blocks:
|
|
307
|
-
await self._handle_block(state.mc_block, block
|
|
305
|
+
await self._handle_block(state.mc_block, block)
|
|
308
306
|
state.mc_block = await self._wait_next_mc_block(state.mc_block)
|
|
309
307
|
finally:
|
|
310
308
|
await self._dispatcher.aclose()
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import typing as t
|
|
2
|
+
|
|
3
|
+
from pytoniq_core.tl import BlockIdExt
|
|
4
|
+
from pytoniq_core.tlb.block import ExtBlkRef
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ShardTraversal:
|
|
8
|
+
|
|
9
|
+
@staticmethod
|
|
10
|
+
def shard_key(blk: BlockIdExt) -> t.Tuple[int, int]:
|
|
11
|
+
return blk.workchain, blk.shard
|
|
12
|
+
|
|
13
|
+
@staticmethod
|
|
14
|
+
def simulate_overflow(x: int) -> int:
|
|
15
|
+
return (x + 2**63) % 2**64 - 2**63
|
|
16
|
+
|
|
17
|
+
@staticmethod
|
|
18
|
+
def lower_bit64(num: int) -> int:
|
|
19
|
+
return num & (~num + 1)
|
|
20
|
+
|
|
21
|
+
def get_child_shard(self, shard: int, *, left: bool) -> int:
|
|
22
|
+
x = self.lower_bit64(shard) >> 1
|
|
23
|
+
if left:
|
|
24
|
+
return self.simulate_overflow(shard - x)
|
|
25
|
+
return self.simulate_overflow(shard + x)
|
|
26
|
+
|
|
27
|
+
def get_parent_shard(self, shard: int) -> int:
|
|
28
|
+
x = self.lower_bit64(shard)
|
|
29
|
+
return self.simulate_overflow((shard - x) | (x << 1))
|
|
30
|
+
|
|
31
|
+
async def walk_unseen(
|
|
32
|
+
self,
|
|
33
|
+
*,
|
|
34
|
+
root: BlockIdExt,
|
|
35
|
+
seen_seqno: t.Dict[t.Tuple[int, int], int],
|
|
36
|
+
get_header: t.Callable[[BlockIdExt], t.Awaitable[t.Any]],
|
|
37
|
+
out: t.Optional[t.List[BlockIdExt]] = None,
|
|
38
|
+
) -> t.List[BlockIdExt]:
|
|
39
|
+
"""Recursively walk from root block back to seen blocks."""
|
|
40
|
+
if out is None:
|
|
41
|
+
out = []
|
|
42
|
+
|
|
43
|
+
key = self.shard_key(root)
|
|
44
|
+
if seen_seqno.get(key, -1) >= root.seqno:
|
|
45
|
+
return out
|
|
46
|
+
|
|
47
|
+
_, header = await get_header(root)
|
|
48
|
+
prev_ref = header.info.prev_ref
|
|
49
|
+
|
|
50
|
+
if prev_ref.type_ == "prev_blk_info":
|
|
51
|
+
prev: ExtBlkRef = prev_ref.prev
|
|
52
|
+
prev_shard = (
|
|
53
|
+
self.get_parent_shard(root.shard)
|
|
54
|
+
if header.info.after_split
|
|
55
|
+
else root.shard
|
|
56
|
+
)
|
|
57
|
+
await self.walk_unseen(
|
|
58
|
+
root=BlockIdExt(
|
|
59
|
+
workchain=root.workchain,
|
|
60
|
+
shard=prev_shard,
|
|
61
|
+
seqno=prev.seqno,
|
|
62
|
+
root_hash=prev.root_hash,
|
|
63
|
+
file_hash=prev.file_hash,
|
|
64
|
+
),
|
|
65
|
+
seen_seqno=seen_seqno,
|
|
66
|
+
get_header=get_header,
|
|
67
|
+
out=out,
|
|
68
|
+
)
|
|
69
|
+
else:
|
|
70
|
+
prev1, prev2 = prev_ref.prev1, prev_ref.prev2
|
|
71
|
+
await self.walk_unseen(
|
|
72
|
+
root=BlockIdExt(
|
|
73
|
+
workchain=root.workchain,
|
|
74
|
+
shard=self.get_child_shard(root.shard, left=True),
|
|
75
|
+
seqno=prev1.seqno,
|
|
76
|
+
root_hash=prev1.root_hash,
|
|
77
|
+
file_hash=prev1.file_hash,
|
|
78
|
+
),
|
|
79
|
+
seen_seqno=seen_seqno,
|
|
80
|
+
get_header=get_header,
|
|
81
|
+
out=out,
|
|
82
|
+
)
|
|
83
|
+
await self.walk_unseen(
|
|
84
|
+
root=BlockIdExt(
|
|
85
|
+
workchain=root.workchain,
|
|
86
|
+
shard=self.get_child_shard(root.shard, left=False),
|
|
87
|
+
seqno=prev2.seqno,
|
|
88
|
+
root_hash=prev2.root_hash,
|
|
89
|
+
file_hash=prev2.file_hash,
|
|
90
|
+
),
|
|
91
|
+
seen_seqno=seen_seqno,
|
|
92
|
+
get_header=get_header,
|
|
93
|
+
out=out,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
out.append(root)
|
|
97
|
+
return out
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import typing as t
|
|
4
|
+
|
|
5
|
+
from tonutils.tools.block_scanner.annotations import TEvent
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Where(t.Generic[TEvent]):
|
|
9
|
+
__slots__ = ()
|
|
10
|
+
|
|
11
|
+
def __call__(self, event: TEvent) -> bool:
|
|
12
|
+
raise NotImplementedError
|
|
13
|
+
|
|
14
|
+
def __and__(self, other: Where[TEvent]) -> _And[TEvent]:
|
|
15
|
+
return _And(self, other)
|
|
16
|
+
|
|
17
|
+
def __or__(self, other: Where[TEvent]) -> _Or[TEvent]:
|
|
18
|
+
return _Or(self, other)
|
|
19
|
+
|
|
20
|
+
def __invert__(self) -> _Not[TEvent]:
|
|
21
|
+
return _Not(self)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class _And(Where[TEvent]):
|
|
25
|
+
__slots__ = ("_a", "_b")
|
|
26
|
+
|
|
27
|
+
def __init__(self, a: Where[TEvent], b: Where[TEvent]) -> None:
|
|
28
|
+
self._a = a
|
|
29
|
+
self._b = b
|
|
30
|
+
|
|
31
|
+
def __call__(self, event: TEvent) -> bool:
|
|
32
|
+
return self._a(event) and self._b(event)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class _Or(Where[TEvent]):
|
|
36
|
+
__slots__ = ("_a", "_b")
|
|
37
|
+
|
|
38
|
+
def __init__(self, a: Where[TEvent], b: Where[TEvent]) -> None:
|
|
39
|
+
self._a = a
|
|
40
|
+
self._b = b
|
|
41
|
+
|
|
42
|
+
def __call__(self, event: TEvent) -> bool:
|
|
43
|
+
return self._a(event) or self._b(event)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class _Not(Where[TEvent]):
|
|
47
|
+
__slots__ = ("_f",)
|
|
48
|
+
|
|
49
|
+
def __init__(self, f: Where[TEvent]) -> None:
|
|
50
|
+
self._f = f
|
|
51
|
+
|
|
52
|
+
def __call__(self, event: TEvent) -> bool:
|
|
53
|
+
return not self._f(event)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "2.0.1b4"
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import typing as t
|
|
2
|
-
|
|
3
|
-
from pytoniq_core.tl import BlockIdExt
|
|
4
|
-
from pytoniq_core.tlb.block import ExtBlkRef
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class ShardTraversal:
|
|
8
|
-
|
|
9
|
-
@staticmethod
|
|
10
|
-
def shard_key(blk: BlockIdExt) -> t.Tuple[int, int]:
|
|
11
|
-
return blk.workchain, blk.shard
|
|
12
|
-
|
|
13
|
-
@staticmethod
|
|
14
|
-
def simulate_overflow(x: int) -> int:
|
|
15
|
-
return (x + 2**63) % 2**64 - 2**63
|
|
16
|
-
|
|
17
|
-
@staticmethod
|
|
18
|
-
def lower_bit64(num: int) -> int:
|
|
19
|
-
return num & (~num + 1)
|
|
20
|
-
|
|
21
|
-
def get_child_shard(self, shard: int, *, left: bool) -> int:
|
|
22
|
-
x = self.lower_bit64(shard) >> 1
|
|
23
|
-
if left:
|
|
24
|
-
return self.simulate_overflow(shard - x)
|
|
25
|
-
return self.simulate_overflow(shard + x)
|
|
26
|
-
|
|
27
|
-
def get_parent_shard(self, shard: int) -> int:
|
|
28
|
-
x = self.lower_bit64(shard)
|
|
29
|
-
return self.simulate_overflow((shard - x) | (x << 1))
|
|
30
|
-
|
|
31
|
-
async def walk_unseen(
|
|
32
|
-
self,
|
|
33
|
-
*,
|
|
34
|
-
root: BlockIdExt,
|
|
35
|
-
seen_seqno: t.Dict[t.Tuple[int, int], int],
|
|
36
|
-
get_header: t.Callable[[BlockIdExt], t.Awaitable[t.Any]],
|
|
37
|
-
) -> t.List[BlockIdExt]:
|
|
38
|
-
out: t.List[BlockIdExt] = []
|
|
39
|
-
stack: t.List[BlockIdExt] = [root]
|
|
40
|
-
post: t.List[BlockIdExt] = []
|
|
41
|
-
|
|
42
|
-
while stack:
|
|
43
|
-
blk = stack.pop()
|
|
44
|
-
key = self.shard_key(blk)
|
|
45
|
-
if seen_seqno.get(key, -1) >= blk.seqno:
|
|
46
|
-
continue
|
|
47
|
-
|
|
48
|
-
post.append(blk)
|
|
49
|
-
_, header = await get_header(blk)
|
|
50
|
-
prev_ref = header.info.prev_ref
|
|
51
|
-
|
|
52
|
-
if prev_ref.type_ == "prev_blk_info":
|
|
53
|
-
prev: ExtBlkRef = prev_ref.prev
|
|
54
|
-
prev_shard = (
|
|
55
|
-
self.get_parent_shard(blk.shard)
|
|
56
|
-
if header.info.after_split
|
|
57
|
-
else blk.shard
|
|
58
|
-
)
|
|
59
|
-
stack.append(
|
|
60
|
-
BlockIdExt(
|
|
61
|
-
workchain=blk.workchain,
|
|
62
|
-
shard=prev_shard,
|
|
63
|
-
seqno=prev.seqno,
|
|
64
|
-
root_hash=prev.root_hash,
|
|
65
|
-
file_hash=prev.file_hash,
|
|
66
|
-
)
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
else:
|
|
70
|
-
prev1, prev2 = prev_ref.prev1, prev_ref.prev2
|
|
71
|
-
stack.append(
|
|
72
|
-
BlockIdExt(
|
|
73
|
-
workchain=blk.workchain,
|
|
74
|
-
shard=self.get_child_shard(blk.shard, left=True),
|
|
75
|
-
seqno=prev1.seqno,
|
|
76
|
-
root_hash=prev1.root_hash,
|
|
77
|
-
file_hash=prev1.file_hash,
|
|
78
|
-
)
|
|
79
|
-
)
|
|
80
|
-
stack.append(
|
|
81
|
-
BlockIdExt(
|
|
82
|
-
workchain=blk.workchain,
|
|
83
|
-
shard=self.get_child_shard(blk.shard, left=False),
|
|
84
|
-
seqno=prev2.seqno,
|
|
85
|
-
root_hash=prev2.root_hash,
|
|
86
|
-
file_hash=prev2.file_hash,
|
|
87
|
-
)
|
|
88
|
-
)
|
|
89
|
-
|
|
90
|
-
for blk in reversed(post):
|
|
91
|
-
key = self.shard_key(blk)
|
|
92
|
-
if seen_seqno.get(key, -1) >= blk.seqno:
|
|
93
|
-
continue
|
|
94
|
-
out.append(blk)
|
|
95
|
-
|
|
96
|
-
return out
|
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import typing as t
|
|
4
|
-
|
|
5
|
-
from pytoniq_core import Address
|
|
6
|
-
|
|
7
|
-
from tonutils.tools.block_scanner import TransactionEvent
|
|
8
|
-
from tonutils.tools.block_scanner.annotations import TEvent
|
|
9
|
-
from tonutils.types import AddressLike
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class Where(t.Generic[TEvent]):
|
|
13
|
-
__slots__ = ()
|
|
14
|
-
|
|
15
|
-
def __call__(self, event: TEvent) -> bool:
|
|
16
|
-
raise NotImplementedError
|
|
17
|
-
|
|
18
|
-
def __and__(self, other: Where[TEvent]) -> _And[TEvent]:
|
|
19
|
-
return _And(self, other)
|
|
20
|
-
|
|
21
|
-
def __or__(self, other: Where[TEvent]) -> _Or[TEvent]:
|
|
22
|
-
return _Or(self, other)
|
|
23
|
-
|
|
24
|
-
def __invert__(self) -> _Not[TEvent]:
|
|
25
|
-
return _Not(self)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
class _And(Where[TEvent]):
|
|
29
|
-
__slots__ = ("_a", "_b")
|
|
30
|
-
|
|
31
|
-
def __init__(self, a: Where[TEvent], b: Where[TEvent]) -> None:
|
|
32
|
-
self._a = a
|
|
33
|
-
self._b = b
|
|
34
|
-
|
|
35
|
-
def __call__(self, event: TEvent) -> bool:
|
|
36
|
-
return self._a(event) and self._b(event)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
class _Or(Where[TEvent]):
|
|
40
|
-
__slots__ = ("_a", "_b")
|
|
41
|
-
|
|
42
|
-
def __init__(self, a: Where[TEvent], b: Where[TEvent]) -> None:
|
|
43
|
-
self._a = a
|
|
44
|
-
self._b = b
|
|
45
|
-
|
|
46
|
-
def __call__(self, event: TEvent) -> bool:
|
|
47
|
-
return self._a(event) or self._b(event)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
class _Not(Where[TEvent]):
|
|
51
|
-
__slots__ = ("_f",)
|
|
52
|
-
|
|
53
|
-
def __init__(self, f: Where[TEvent]) -> None:
|
|
54
|
-
self._f = f
|
|
55
|
-
|
|
56
|
-
def __call__(self, event: TEvent) -> bool:
|
|
57
|
-
return not self._f(event)
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
class _Opcode(Where[TransactionEvent]):
|
|
61
|
-
__slots__ = ("_ops",)
|
|
62
|
-
|
|
63
|
-
def __init__(self, *ops: int) -> None:
|
|
64
|
-
self._ops = frozenset(ops)
|
|
65
|
-
|
|
66
|
-
def __call__(self, event: TransactionEvent) -> bool:
|
|
67
|
-
msg = event.transaction.in_msg
|
|
68
|
-
if msg is None or msg.body is None:
|
|
69
|
-
return False
|
|
70
|
-
|
|
71
|
-
if len(msg.body.bits) < 32:
|
|
72
|
-
return False
|
|
73
|
-
|
|
74
|
-
op = msg.body.begin_parse().load_uint(32)
|
|
75
|
-
return op in self._ops
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
class _Comment(Where[TransactionEvent]):
|
|
79
|
-
__slots__ = ("_texts", "_any")
|
|
80
|
-
|
|
81
|
-
def __init__(self, *texts: str) -> None:
|
|
82
|
-
self._texts = frozenset(texts)
|
|
83
|
-
self._any = len(texts) == 0
|
|
84
|
-
|
|
85
|
-
def __call__(self, event: TransactionEvent) -> bool:
|
|
86
|
-
msg = event.transaction.in_msg
|
|
87
|
-
if msg is None or msg.body is None:
|
|
88
|
-
return False
|
|
89
|
-
|
|
90
|
-
body = msg.body.begin_parse()
|
|
91
|
-
if len(body.bits) < 32:
|
|
92
|
-
return False
|
|
93
|
-
|
|
94
|
-
op = body.load_uint(32)
|
|
95
|
-
if op != 0:
|
|
96
|
-
return False
|
|
97
|
-
|
|
98
|
-
if self._any:
|
|
99
|
-
return True
|
|
100
|
-
try:
|
|
101
|
-
text = body.load_snake_string()
|
|
102
|
-
except (Exception,):
|
|
103
|
-
return False
|
|
104
|
-
|
|
105
|
-
return text in self._texts
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
class _Sender(Where[TransactionEvent]):
|
|
109
|
-
__slots__ = ("_addrs",)
|
|
110
|
-
|
|
111
|
-
def __init__(self, *addrs: AddressLike) -> None:
|
|
112
|
-
self._addrs = frozenset(Address(a) if isinstance(a, str) else a for a in addrs)
|
|
113
|
-
|
|
114
|
-
def __call__(self, event: TransactionEvent) -> bool:
|
|
115
|
-
msg = event.transaction.in_msg
|
|
116
|
-
if msg is None:
|
|
117
|
-
return False
|
|
118
|
-
|
|
119
|
-
src = msg.info.src
|
|
120
|
-
return src is not None and src in self._addrs
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
class _Destination(Where[TransactionEvent]):
|
|
124
|
-
__slots__ = ("_addrs",)
|
|
125
|
-
|
|
126
|
-
def __init__(self, *addrs: AddressLike) -> None:
|
|
127
|
-
self._addrs = frozenset(Address(a) if isinstance(a, str) else a for a in addrs)
|
|
128
|
-
|
|
129
|
-
def __call__(self, event: TransactionEvent) -> bool:
|
|
130
|
-
msg = event.transaction.in_msg
|
|
131
|
-
if msg is None:
|
|
132
|
-
return False
|
|
133
|
-
|
|
134
|
-
dest = msg.info.dest
|
|
135
|
-
return dest is not None and dest in self._addrs
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
def opcode(*ops: int) -> Where[TransactionEvent]:
|
|
139
|
-
return _Opcode(*ops)
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
def comment(*texts: str) -> Where[TransactionEvent]:
|
|
143
|
-
return _Comment(*texts)
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
def sender(*addresses: AddressLike) -> Where[TransactionEvent]:
|
|
147
|
-
return _Sender(*addresses)
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
def destination(*addresses: AddressLike) -> Where[TransactionEvent]:
|
|
151
|
-
return _Destination(*addresses)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|