Coder Social home page Coder Social logo

ton-community / ton-kotlin Goto Github PK

View Code? Open in Web Editor NEW
86.0 5.0 25.0 3.42 MB

Kotlin/Multiplatform SDK for The Open Network

License: Apache License 2.0

Kotlin 100.00%
blockchain ton telegram kotlin kotlin-language cryptocurrency kotlin-library kotlin-multiplatform

ton-kotlin's Introduction

Kotlin/Multiplatform SDK for The Open Network

Maven Central Kotlin License Telegram Based on TON

Modules

Core components

  • org.ton:ton-kotlin-tvm:0.3.1 - TVM Primitives (Cells, BOC, etc.)
  • org.ton:ton-kotlin-crypto:0.3.1 - Crypto primitives for TON (ED25519, SHA, etc.)
  • org.ton:ton-kotlin-adnl:0.3.1 - ADNL (Abstract Datagram Network Layer) TON Network implementation

API Interfaces

  • org.ton:ton-kotlin-contract:0.3.1 - Smart-contracts API interface
  • org.ton:ton-kotlin-liteclient:0.3.1 - Lite-client API implementation

TL-B (TL-Binary)

  • org.ton:ton-kotlin-tlb:0.3.1 - TON TL-B (TL-Binary) serialization/deserialization
  • org.ton:ton-kotlin-block-tlb:0.3.1 - Pre-generated TL-B schemas for TON Blockchain
  • org.ton:ton-kotlin-hashmap-tlb:0.3.1 - Pre-generated TL-B schemas for TON Hashmap (also known as Dictionary)

Documentation

https://github.com/andreypfau/ton-kotlin/wiki/TON-Kotlin-documentation

ton-kotlin's People

Contributors

90k2 avatar andreypfau avatar antonmeep avatar dependabot[bot] avatar joltd avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

ton-kotlin's Issues

MsgAddressIntStd.toString() emits invalid values in certain scenarios

MsgAddressIntStd.parse("EQCxhaSrUuwwsMMUnMu8jaipSrkhXwzXaWQfhhO1s2REdQAB").toString(userFriendly = true)
// EQCxhaSrUuwwsMMUnMu8jaipSrkhXwzXaWQfhhO1s2REdQE=

Probable cause is messed up CRC16 implementations in very rare cases (I encountered just 105 cases in over 25k addresses)

Enable Android platform support

To use the library in Android projects, 'android()' platform support must be enabled. To do so, it need to apply Android Gradle Plugin for the configuration phase, apply android library plugin com.android.library for each multiplatofrm module, setup android configuration with fresh verisons of the android SDK (compileSdk = 33, targetSdk = 33, minSdk = 21 or older), create androidMain sourcesets and place manifest androidMain/AndroidManifest.xml to each sourceset with different packages.

And as I beleive, there are some problems with the current project configuration for multiplatform. jvmMain and androidMain - is different sourcests for KMM (because some differents with API in Java, as I understand). So, it will need to either copypaste the code of actual implementations, or rework structure of sourcesets.
And it is impossible to enable two plugins at the same time in the same module - com.android.library and java. At now java plugin applied in ton-kotlin-fift module.

Some links on the topic:

CellType.LIBRARY_REFERENCE -> TODO() lead to deadlock probably

TESTNET get block 0:11959898 is block coroutine by CellType.LIBRARY_REFERENCE -> TODO()

or this block seqno

liteClient.getBlock(TonNodeBlockId(0, Long.MIN_VALUE, 11996799))

lead to exception

kotlin.NotImplementedError: An operation is not implemented. at org.ton.cell.CellBuilderImpl.build(CellBuilder.kt:266) at org.ton.cell.CellBuilder$DefaultImpls.endCell(CellBuilder.kt:29) at org.ton.cell.CellBuilderImpl.endCell(CellBuilder.kt:150) at org.ton.boc.BagOfCellsUtilsKt$createCell$2.invokeSuspend(BagOfCellsUtils.kt:320) at org.ton.boc.BagOfCellsUtilsKt$createCell$2.invoke(BagOfCellsUtils.kt) at org.ton.boc.BagOfCellsUtilsKt$createCell$2.invoke(BagOfCellsUtils.kt) at

apparently cause of :

image

`LiteClient.getTransactions` throws an error (supposedly) for old addresses

I am using ton-kotlin 0.3.1 installed like that

implementation 'org.ton:ton-kotlin-tvm:0.3.1'
implementation 'org.ton:ton-kotlin-block-tlb:0.3.1'
implementation 'org.ton:ton-kotlin-liteclient:0.3.1'

Here is a toy example of using this function

val address = "UQBZf6LoZG0KSONnGs1ljXgVe_KhxOspnT1NAwV0rWMy1mw9"
val liteClient = LiteClient(Dispatchers.IO, config)
runBlocking {
    val result = liteClient.getAccountState(AddrStd(address))
    liteClient.getTransactions(result.address, result.lastTransactionId!!, 2)
}

For addresses
-https://dev.tonviewer.com/EQBZf6LoZG0KSONnGs1ljXgVe_KhxOspnT1NAwV0rWMy1jH4
-https://dev.tonviewer.com/EQBWl0HakAranxsLaDPcQGJtExFYZ8g7NR4D-JdvaV4cAKfZ

getTransactions throws following error:

Caused by: org.ton.lite.api.exception.LiteServerUnknownException: cannot locate transaction in block with specified logical time

For address
-https://dev.tonviewer.com/EQDaokYMTQSpFFjbe9Pht2AH6AwD2PhnOGmSFsJH5WsNhVtp

It throws:

Caused by: org.ton.lite.api.exception.LiteServerUnknownException: cannot load block (0,8000000000000000,42712115):B715FD8023F2E2430ED88D4A112FE58F80A59DECCFA09AAE4F35EC2F2A9CA599:5016E35CC8264C8DD98925C40753D42C1EFFE598B7CC2BA06D41B2F142E08DF3 with specified transaction: not in db

However for addresses
-https://dev.tonviewer.com/EQCtiv7PrMJImWiF2L5oJCgPnzp-VML2CAt5cbn1VsKAxLiE
-https://dev.tonviewer.com/EQCTd6HSN6kMQfYU5fel7BykpGh7ARzWunj2zmB9fhgGA2Y6

It works without exceptions

It seems, that "bad" addresses only differ in last transaction date.

What could be the reason for such behaviour, and can it be fixed?

Inconsistent naming

Some entities use camelCase:

public data class MerkleUpdate<X>(
    val oldHash: BitString,
    val newHash: BitString,
    val old: CellRef<X>,
    val new: CellRef<X>
)

Some use snake_case:

data class BlockInfo(
    val version: UInt,
    val not_master: Boolean,
    val after_merge: Boolean,
    val before_split: Boolean,
    val after_split: Boolean,
    ...

Some use both?

data class Block(
    val global_id: Int,
    val infoCell: CellRef<BlockInfo>,
    val valueFlowCell: CellRef<ValueFlow>,
    val stateUpdateCell: CellRef<MerkleUpdate<ShardState>>,
    val extraCell: CellRef<BlockExtra>
)

[0.2.6] Can't get Block's stateUpdate

import io.ktor.util.*
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import org.ton.api.liteserver.LiteServerDesc
import org.ton.api.pub.PublicKeyEd25519
import org.ton.lite.client.LiteClient

runBlocking {
    val liteClient = LiteClient(
        Dispatchers.IO + CoroutineName("liteClient"),
        LiteServerDesc(
            ip = 1091931625,
            port = 30131,
            id =  PublicKeyEd25519("wrQaeIFispPfHndEBc0s0fx7GSp8UFFvebnytQQfc6A=".decodeBase64Bytes())
        )
    )

    val id = liteClient.getLastBlockId()
    val block = liteClient.getBlock(id)
    block?.stateUpdate?.oldHash
}

Results in

Exception in thread "main" java.lang.IllegalStateException: bitsPosition: 512 != bits.length: 552
	at org.ton.cell.CellSliceImpl.endParse(CellSlice.kt:96)
	at org.ton.cell.Cell$DefaultImpls.parse(Cell.kt:62)
	at org.ton.cell.CellImpl.parse(CellImpl.kt:13)
	at org.ton.tlb.CellRefImpl$value$2.invoke(CellRef.kt:37)
	at kotlin.SafePublicationLazyImpl.getValue(LazyJVM.kt:107)
	at org.ton.tlb.CellRefImpl.getValue(CellRef.kt:36)
	at org.ton.tlb.CellRef$DefaultImpls.getValue(CellRef.kt:18)
	at org.ton.tlb.CellRefImpl.getValue(CellRef.kt:32)
	at org.ton.block.Block.getStateUpdate(Block.kt:20)
	at org.jetbrains.kotlin.idea.scratch.generated.ScratchFileRunnerGenerated$ScratchFileRunnerGenerated$generated_get_instance_res0$1$1.invokeSuspend(tmp.kt:26)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.internal.ScopeCoroutine.afterResume(Scopes.kt:33)
	at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:102)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
	at kotlinx.coroutines.UndispatchedCoroutine.afterResume(CoroutineContext.kt:233)
	at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:102)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
	at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:284)
	at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85)
	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
	at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
	at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
	at org.jetbrains.kotlin.idea.scratch.generated.ScratchFileRunnerGenerated$ScratchFileRunnerGenerated.generated_get_instance_res0(tmp.kt:14)
	at org.jetbrains.kotlin.idea.scratch.generated.ScratchFileRunnerGenerated.main(tmp.kt:33)

Can't parse block: (-1:8000000000000000:24533340)

Setup:

@Test
    fun `load block`() {
        val lite = LiteClient(
            LiteClientConfigGlobal(
                liteservers = listOf(
                    LiteServerDesc(id = PublicKeyEd25519(base64("aF91CuUHuuOv9rm2W5+O/4h38M3sRm40DtSdRxQhmtQ=")), ip = -2018135749, port = 53312)
                ),
                validator = ValidatorConfigGlobal()
            )
        )

        val blockId = 24533340
        runBlocking {
            lite.lookupBlock(
                TonNodeBlockId(workchain = -1, shard = Long.MIN_VALUE, seqno = blockId)
            )?.let {
                println(lite.getBlock(it))
            }
        }
    }

stacktrace:

Can't parse block: (-1:8000000000000000:24533340):44B2F2B204734A082282E63A9C2CFB46C16AE5497E588FAF0634131B2E98F374:F221C4081B538D6C1C6C9C8803BE2FA8C6E2F90FC99983FF5A9E2562B80686A3
java.lang.RuntimeException: Can't parse block: (-1:8000000000000000:24533340):44B2F2B204734A082282E63A9C2CFB46C16AE5497E588FAF0634131B2E98F374:F221C4081B538D6C1C6C9C8803BE2FA8C6E2F90FC99983FF5A9E2562B80686A3
	at org.ton.lite.client.LiteClient.getBlock(LiteClient.kt:293)
	at org.ton.lite.client.LiteClient$getBlock$3.invokeSuspend(LiteClient.kt)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.internal.ScopeCoroutine.afterResume(Scopes.kt:33)
	at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:102)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
	at kotlinx.coroutines.UndispatchedCoroutine.afterResume(CoroutineContext.kt:202)
	at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:102)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
	at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:279)
	at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85)
	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
	at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
	at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
	at com.project.TonModuleTest.load block(TonModuleTest.kt:253)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
	at java.base/java.lang.reflect.Method.invoke(Method.java:577)
	at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:725)
	at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
	at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
	at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:214)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:210)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:66)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:96)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:75)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:99)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:79)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:75)
	at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:61)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
	at java.base/java.lang.reflect.Method.invoke(Method.java:577)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
	at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
	at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
	at jdk.proxy1/jdk.proxy1.$Proxy2.stop(Unknown Source)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:193)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
	at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:133)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71)
	at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
	at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
Caused by: java.lang.IndexOutOfBoundsException: Empty list doesn't contain element at index 0.
	at kotlin.collections.EmptyList.get(Collections.kt:36)
	at kotlin.collections.EmptyList.get(Collections.kt:24)
	at org.ton.cell.CellSliceImpl.preloadRef(CellSlice.kt:104)
	at org.ton.cell.CellSliceImpl.loadRef(CellSlice.kt:97)
	at org.ton.hashmap.HashMapNodeTlbCombinator$HashMapNodeForkTlbConstructor.loadTlb(HashMapNode.kt:124)
	at org.ton.hashmap.HashMapNodeTlbCombinator.loadTlb(HashMapNode.kt:43)
	at org.ton.hashmap.HashMapNodeTlbCombinator.loadTlb(HashMapNode.kt:21)
	at org.ton.hashmap.HashMapEdgeTlbConstructor.loadTlb(HashMapEdge.kt:79)
	at org.ton.hashmap.HashMapEdgeTlbConstructor.loadTlb(HashMapEdge.kt:46)
	at org.ton.hashmap.HashMapNodeTlbCombinator$HashMapNodeForkTlbConstructor.loadTlb(HashMapNode.kt:125)
	at org.ton.hashmap.HashMapNodeTlbCombinator.loadTlb(HashMapNode.kt:43)
	at org.ton.hashmap.HashMapNodeTlbCombinator.loadTlb(HashMapNode.kt:21)
	at org.ton.hashmap.HashMapEdgeTlbConstructor.loadTlb(HashMapEdge.kt:79)
	at org.ton.hashmap.HashMapEdgeTlbConstructor.loadTlb(HashMapEdge.kt:46)
	at org.ton.hashmap.HashMapNodeTlbCombinator$HashMapNodeForkTlbConstructor.loadTlb(HashMapNode.kt:125)
	at org.ton.hashmap.HashMapNodeTlbCombinator.loadTlb(HashMapNode.kt:43)
	at org.ton.hashmap.HashMapNodeTlbCombinator.loadTlb(HashMapNode.kt:21)
	at org.ton.hashmap.HashMapEdgeTlbConstructor.loadTlb(HashMapEdge.kt:79)
	at org.ton.hashmap.HashMapEdgeTlbConstructor.loadTlb(HashMapEdge.kt:46)
	at org.ton.hashmap.HashMapNodeTlbCombinator$HashMapNodeForkTlbConstructor.loadTlb(HashMapNode.kt:125)
	at org.ton.hashmap.HashMapNodeTlbCombinator.loadTlb(HashMapNode.kt:43)
	at org.ton.hashmap.HashMapNodeTlbCombinator.loadTlb(HashMapNode.kt:21)
	at org.ton.hashmap.HashMapEdgeTlbConstructor.loadTlb(HashMapEdge.kt:79)
	at org.ton.hashmap.HashMapEdgeTlbConstructor.loadTlb(HashMapEdge.kt:46)
	at org.ton.block.ConfigParamsTlbConstructor.loadTlb(ConfigParams.kt:60)
	at org.ton.block.ConfigParams$Companion.loadTlb(ConfigParams.kt)
	at org.ton.block.ConfigParams$Companion.loadTlb(ConfigParams.kt:22)
	at org.ton.block.McBlockExtraTlbConstructor.loadTlb(McBlockExtra.kt:108)
	at org.ton.block.McBlockExtraTlbConstructor.loadTlb(McBlockExtra.kt:28)
	at org.ton.tlb.TlbCombinator.loadTlb(TlbCombinator.kt:34)
	at org.ton.block.McBlockExtra$Companion.loadTlb(McBlockExtra.kt)
	at org.ton.block.McBlockExtra$Companion.loadTlb(McBlockExtra.kt:25)
	at org.ton.tlb.constructor.CellReferencedTlbConstructor.loadTlb(CellTlbConstructor.kt:63)
	at org.ton.block.MaybeTlbCombinator$JustConstructor.loadTlb(Maybe.kt:117)
	at org.ton.block.MaybeTlbCombinator$JustConstructor.loadTlb(Maybe.kt:84)
	at org.ton.tlb.TlbCombinator.loadTlb(TlbCombinator.kt:34)
	at org.ton.block.BlockExtraTlbConstructor.loadTlb(BlockExtra.kt:113)
	at org.ton.block.BlockExtraTlbConstructor.loadTlb(BlockExtra.kt:32)
	at org.ton.tlb.TlbCombinator.loadTlb(TlbCombinator.kt:34)
	at org.ton.block.BlockExtra$Companion.loadTlb(BlockExtra.kt)
	at org.ton.block.BlockExtra$Companion.loadTlb(BlockExtra.kt:29)
	at org.ton.block.Block$TlbConstructor.loadTlb(Block.kt:104)
	at org.ton.block.Block$TlbConstructor.loadTlb(Block.kt:26)
	at org.ton.tlb.TlbCombinator.loadTlb(TlbCombinator.kt:34)
	at org.ton.lite.client.LiteClient$getBlock$$inlined$parse$1.invoke(TlbCodec.kt:53)
	at org.ton.lite.client.LiteClient$getBlock$$inlined$parse$1.invoke(TlbCodec.kt:34)
	at org.ton.cell.Cell$DefaultImpls.parse(Cell.kt:56)
	at org.ton.cell.CellImpl.parse(CellImpl.kt:12)
	at org.ton.lite.client.LiteClient.getBlock(LiteClient.kt:536)
	... 95 more

upd. same for 24702356 for example

BOC hasCrc32c anomaly in LiteServer

Sometimes Lite-Server returns:
Exception in thread "main" LiteServerError(code=0, message=bag-of-cells CRC32C mismatch: expected 0xf161a74f, found 0x0000d09d)

here logs:

send boc: b5ee9c724101020100ae0001cf880083a2c647078b8a66f9765a64401ce06b507a077e2e426e1c0eb9d0dfc45bc7ac062741de2dc749d0ca21d3d2577edb734c223e2c94a414c25de9aef583e82f565a6281c86a6c919dca4b96b1fb68a0fb2836b7d3363d7364bf1cbd9a54f4ace050000000081c0100826200798f62e648d4d112f54116e56686a5d9033f7e135f66a8e63a77cad088fd441f21dcd65000000000000000000000000000000000000048656c6c6f20544f4e765c2f2f
[simpleR3] [INFO] Transfer: Message(info={"src":{"@type":"addr_none"},"dest":{"@type":"addr_std","anycast":{"@type":"nothing"},"workchain_id":0,"address":"41D1632383C5C5337CBB2D32200E7035A83D03BF1721370E075CE86FE22DE3D6"}}, init=org.ton.block.Nothing@0, body=Left(value=x{E5B15AB7DB54023B14F8942C573AE6E9A85B7E7DAC66403839DC626DE69E0A042BD5EAA73CF3590630691B6D77EA755E79D1E0F4B41E6603D3DCC3D4C9EB44060000000103}
    x{6200798F62E648D4D112F54116E56686A5D9033F7E135F66A8E63A77CAD088FD441F0808000000000000000000000000000000000048656C6C6F20544F4E}))
[TON SimpleWalletR3] [DEBUG] Send query: df068c79c482d40a69bcb5ee9c724101020100ab0001cf880083a2c647078b8a66f9765a64401ce06b507a077e2e426e1c0eb9d0dfc45bc7ac072d8ad5bedaa011d8a7c4a162b9d7374d42dbf3ed633201c1cee3136f34f050215eaf5539e79ac8318348db6bbf53aaf3ce8f07a5a0f3301e9ee61ea64f5a2030000000081c01007c6200798f62e648d4d112f54116e56686a5d9033f7e135f66a8e63a77cad088fd441f0808000000000000000000000000000000000048656c6c6f20544f4e6afb9dd00000000000000000
raw adnl: 340100000bbf4c66219e794881c7a03852f51a1d3acfd5bec71cbdf0d96b8a401fb3c1807af98bb416603ac15f1e56dd40ead2d19257d91c25b30ff642afe07571f945a1bfd786ecccdf068c79c482d40a69bcb5ee9c724101020100ab0001cf880083a2c647078b8a66f9765a64401ce06b507a077e2e426e1c0eb9d0dfc45bc7ac072d8ad5bedaa011d8a7c4a162b9d7374d42dbf3ed633201c1cee3136f34f050215eaf5539e79ac8318348db6bbf53aaf3ce8f07a5a0f3301e9ee61ea64f5a2030000000081c01007c6200798f62e648d4d112f54116e56686a5d9033f7e135f66a8e63a77cad088fd441f0808000000000000000000000000000000000048656c6c6f20544f4e6afb9dd0000000000000000000000083d390029418d7bc35961c7715185044739aef10188e9b117adf5ada26840e35
Exception in thread "main" LiteServerError(code=0, message=bag-of-cells CRC32C mismatch: expected 0xf161a74f, found 0x0000d09d)

As we can see boc has crc32c: 765c2f2f but for some reason LiteServer expects f161a74f and founds 0000d09d, but 0000d09d
it is not found anywhere, neither in the BOC itself, not in AdnlQuery, not in the raw TCP packet itself (before AES encryption). Where he gets this meaning remains a mystery

IllegalArgumentException if wallet in AccountUninit state

WalletV4R2Contract
Method getSeqno() failed with Exception if wallet is not AccountActive

    public suspend fun transfer(...): Unit = coroutineScope {
        val seqno = getSeqno()
        val walletId = getSubWalletId()
        val message = createTransferMessage(
            address = address,
            stateInit = if (state !is AccountActive) createStateInit(privateKey.publicKey(), walletId) else null,
            ...
        )
        sendExternalMessage(liteApi, message)
    }

ton4j is Missing example(s)

If I use the officially recommended Kotlin, I haven't seen any examples of Java usage on Github.
Can you provide relevant examples, such as how to implement wallet transfers in Java; how to deploy, call transactions, and query basic jetton and NFT contracts in Java; and how to implement historical block transaction queries in Java?

Working with jettons

How can we handle jettons using ton-kotlin SDK? We need functionality for receiving jettons as well as sending

Can't deserialize block data

! Testnet block sample !

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.ActiveProfiles
import org.ton.api.liteclient.config.LiteClientConfigGlobal
import org.ton.api.liteserver.LiteServerDesc
import org.ton.api.pub.PublicKeyEd25519
import org.ton.api.tonnode.TonNodeBlockId
import org.ton.api.validator.config.ValidatorConfigGlobal
import org.ton.crypto.base64
import org.ton.lite.client.LiteClient

class Test {

    @Test
    fun `get block`() {
        val liteClient = LiteClient(
                coroutineContext = Dispatchers.Default,
                liteClientConfigGlobal = LiteClientConfigGlobal(
                        liteServers = listOf(
                                LiteServerDesc(id = PublicKeyEd25519(base64("p2tSiaeSqX978BxE5zLxuTQM06WVDErf5/15QToxMYA=")), ip = 1097649206, port = 29296)
                        ),
                        validator = ValidatorConfigGlobal()
                )
        )
        val blockId = 8573000
        runBlocking {
            liteClient.lookupBlock(
                    TonNodeBlockId(workchain = 0, shard = Long.MIN_VALUE, seqno = blockId)
            )?.let {
                println(liteClient.getBlock(it))
            }
        }
    }
}

Exception

Can't deserialize block data
java.lang.RuntimeException: Can't deserialize block data
	at org.ton.lite.client.LiteClient.getBlock(LiteClient.kt:316)
	at org.ton.lite.client.LiteClient$getBlock$3.invokeSuspend(LiteClient.kt)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.internal.ScopeCoroutine.afterResume(Scopes.kt:33)
	at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:102)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
	at kotlinx.coroutines.UndispatchedCoroutine.afterResume(CoroutineContext.kt:233)
	at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:102)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
	at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:284)
	at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85)
	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
	at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
	at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
	at java.base/java.lang.reflect.Method.invoke(Method.java:577)
	at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:725)
	at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
	at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
	at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:214)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:210)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:66)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:96)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:75)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:99)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:79)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:75)
	at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:61)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
	at java.base/java.lang.reflect.Method.invoke(Method.java:577)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
	at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
	at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
	at jdk.proxy1/jdk.proxy1.$Proxy2.stop(Unknown Source)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:193)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
	at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:133)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71)
	at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
	at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
Caused by: java.lang.IllegalArgumentException: Can't load BoC: b5ee9c72e20201090001000023d90000002400d000ee0184021e02ba0356037c038e03fa046604b204cc05a805c605e4068a073007d607f0080a08e60956097409920a380ade0b840bda0c280c7e0c9a0cb60cd20cee0d0a0d240d3e0d580d720d8c0da40dbc0dd40dec0e040ea60f220fd20fea103610541070108c10a810c410e010fa1114112e11481162117c119611b0125412d21328134613641380139c13b813d413f0140c14261440145a1472148a14a214ba14d2157415f2160e165b16a716c2170f172a1777179217df17fa18471860187a18c718e0192d1946199319ac19f91a101a5d1aa91ac01b0d1b241b711b881ba01bed1c391cda1d561da31e521e9f1eeb1f371f4e1f9b1faa1fb81fbe1fc41fca1fe820352050209d20b821052120216d218821d52221223c228922a222bc23092355236e23bb23d42421243a248724a024ba25072520256d25b9265c26da2727277c27c927e628332850286c28b928d42921293c298929d529f02a3d2a582aa52ac02b0d2b262b732b8c2bd92bf22c0a2c572c6e2cbb2cd22d1f2d362d832d9a2de72e332ed42f522f9f308330ce31ba32063252326832c832de333c335233b034023410346634b834cc34e135343588359c35b135c435d935ee360436b83768381c38e338f03977399a39a43a463aba3acc3ade3b643c2b3c383cbe3cda3dc53dce3e4c3eee3f624029403240b840da419541b8425a42ba438343904417443e4507451045cb469c473e47b2041011ef55aafffffffd000100020003000402a09bc7a9870000000084010082d048000000000000000000000000000000000063edda8b0000080272253d800000080272253d88698ecf8d0001828c006be3a4006bdf29c400000003000000000000002e000500060211b8e48dfb43e0b02e64000700080a8a04c8009bc4fe2bd607fa56fc245e84e1b89b9f705d00352b4e3b5969a354d478ecc4d43fcbe7ed75ef648df4d2d0e7582d2e2f1df1907a01739dd0a93d04ffb629013401340009000a03894a33f6fd4535e4e1ac55c87a91f52d47f416a5763ef0eac90b69ce88ec0200c13a8bf0c98d0d9e924e34de4798fd404c4cc830c1950fe0a8d9727e1c93f364fbafdecb8e40001c001d001e0098000008027206b904006be3a4a76c8ba3e3b21306f741927f4b8ce73cef91e68a0b5a7b160f1caa02b30b7952b737ed4d257ab0a147620f38aa727714406eccb7cb42b701f0d1a08f043455ce0098000008027215fb410082d04709a5521b9cb8350395c3bcdf822a2fe2c35edeec8b1bbc3656e59b500cfe3739ad743c59c36c61ec8def10af88bc2972848793de0955ac755fe72114cc6873fe002172a904ec9c01dee395482763a7f2840008000d0010ee6b280008235b9023afe2fffffffd000000000000000000000000000082d0470000000063edda89000008027215fb41006be3a320000b000c000d235b9023afe2fffffffd000000000000000000000000000082d0480000000063edda8b0000080272253d88006be3a42000130014001528480101f30b06b6a3b2260a5b952707f954a1a7165851ffe4c0d5707cd53effbca61ffe0001211181caa413b270077b90000e00d70000000000000000ffffffffffffffff72a904ec9c01dee38f4b1c35df4cf490000080271f776c4006be3a3d5c55355269bd17274bc7a3167e7665779f86783dfd67094185bc2a3c66c7d1e22afde791ce057a3887b192af3d008cefab7e19f5917a9754419be5c47c7a1188221100e55209d93803bdc8000f0010221100e16d27ca5a1dde68001100123211247bd2d1d483b2e453d23502008f5189f79fd8c93ea3867cc3e5aa56b09cf1c020b6b4593f2956db1e565c53bc5ec720e086eb28ebbc0832b2ea9062dd151dc2012f001500e3e4e20edde5df6800a40044321148a1966d37c6c255c493feb88c3f1202cf526a06fa210936c6a0b28e642001727bebed6a58f2978dbd4a5fafa716dd95a2e385e44a641edf980de3de34b8bea0002d001400e0abb5142b9a6e08001f00573211b2532cc937709ce4c0f8f0a2b8e1f3e708c36afe6a726a29ac634f12628f7f9b5395337880f532f6b25c824c28a2c5f7539ff0389e039f37e76612b4cb12c1840130001100e0c172b62e8370680033008401110000000000000000500016211181caa413b1d3f94210001700d70000000000000000ffffffffffffffff72a904ec74fe50838f4b1c54e4ce679000008027206b904006be3a4a76c8ba3e3b21306f741927f4b8ce73cef91e68a0b5a7b160f1caa02b30b7952b737ed4d257ab0a147620f38aa727714406eccb7cb42b701f0d1a08f043455ce8006bb040000000000000000035f1d20000040139129ec3603932bfd909cd78a0be2c8c5f6f943b6ab7f128b9cb0b7c7c7ebfb83f8129fec0221100e55209d8e9fca10800180019221100e16d27ca0c179048001a001b3211d7a6fed42f84cfd33e447f025ce223216386f2525e175a52de669a74960cd98fff55b68c272de282ba3344726f7da9f5b69b2a4a53787c869b1685cdaa7acf4c012f001500e3e4e20edde510c800a400a532112e119037b46f3f0f4963d7cdf80d9dd915bd0fdb58203cc800dddd5ee305a744e72787934e086d6850b022915078c1da3cc5c1e21a91a3cfdd38fd5b7a55965b002d001600e0abb513b9cae1e80056005732118de9461107632c35df051f58808b044b53fb228e7d6bc0e2a78ebc1890aba22f002aee640a63872f65d7afcf7a812577629f175ec05e22f4a6648df6435e92bf0130001100e0c172b6524cae680083008411094bbdc87ed1d7b4072be5d1a94e05015922186886d32d6d2edcb4d241b0ce759500089951cfd82000cd110109714d85cf477cbf5fdd6c9b0cfd120977930a208e49c83b6c1d4f80e608be4a00098200d41109923b0134e0a01f7ed4d168c93da244790dbf5aefa3c6c1c87794cfc098a613440008a0122ff75a00df220f00c31889c9d995e800580020220f00c180d97bcfec68005a0021220f00c0dcda51e52268005c0022220f00c07daa4eae48a8005e0023220f00c0263fc454612800600024220d00b804e77394c800250063220d00a474609a5dc800260065220d00a24376fc50e800270067220d00a13f3f60bf0800280069220d00a0f5dc8ea4680029006b220b009c3630e988006c002a220b00925dabcb48006e002b220b00914d9679680070002c220b00825e59a228002d0073220b0082050578280074002e2199bb9f337d59dc7dfe997d3400b1453a206b2fa0ea8809b93ac952376dfa43160207dff238b5bd5f7d9a1b8d4e8fc2b6f418e25bfc440c9ed90589b2339bc0b8100c047b84c00002009b1ee9f160002f226fc001f075f337d59dc7dfe997d3400b1453a206b2fa0ea8809b93ac952376dfa43162a8952dc31f6eccc800002009b1ee9f1903eff91c534000770030249b801f22b65c8aaf992d58ca89d846a05639a5da1ee4c437761e8965fba8a4b7679050000000000000000000000000000000000000000000000000000000000000001000000030000000100000001f0079007a007b0031220bc110051340a0007d00322848010196eb9b226c1d2ee6651a1afb0823602a3b171011dda4395eb97283c921a066e10002221100e0721303db34dec800340086220f00c2cadd7e622ca800350088220f00c1cfa987a562c80036008a220f00c18f771ce325e80037008c220f00c1054a5d4f1be8008d0038220f00c0fe1272319e68008f0039220d00aa0dc55cc348003a0092220d00a98f4c9435280093003b220d00a3ab66df5d080095003c220d00a0e3fc6bd4680097003d220d00a0e28a4d55a80099003e220d00a0e134a135c8003f009c220d00a0e0ce4421c80040009e220d40283479e46872009f0041219bbba92f7406a52f09178232ee54bf88812be7fa83f0fc62da39e529bb6d47be0283464c961031aa86767e8a6080fdd0f69ff750f6863aa38eba4e9fb08dc33ccb68295e2c7200000200993a72842000422271c0041bc692f7406a52f09178232ee54bf88812be7fa83f0fc62da39e529bb6d47be22c8590431f6ec3180000200993a7284541a3264b08134000a20043005100000ab329a9a317f2023e9c5b6ca86e5091813ea22f532a50c6b673d7ddb07551948339c401532240221100e0456dd4d196568800a60045221100e0240f59b86521c8004600a9220f00c573de328ed028004700ab220f00c20434e05faca8004800ad220f00c1b397a6845b6800ae0049220f00c053ecb3a799c800b0004a220f00c049a9a71dac2800b2004b220f00c026f6914c5ee800b4004c220d00a743e5c74c4800b6004d220d00a0c4954b248800b8004e220d00a02ad0170408004f00bb220b0091bc39b348005000bd220b008180182088005100bf220b00816f9e7188005200c1220b008116ff5588005300c3220b0080368ae40800c400542199bbb0cab616faab89f4906e67d452d2783d92a0af560aceb6afc4014427ed020200a607e82375baeced4ca9c9830f9e87b88cf6617ee807728d0ca0f01db0a9cb38d592804000020099144ce32000552271c00e3f070cab616faab89f4906e67d452d2783d92a0af560aceb6afc4014427ed023a4c0511b031f6ec258000020099144ce39005303f4134000c700c8220f00c31889580a09c800580059284801016e23475ddc689ed168b3cdc20d356c2a039a0ebee3aaf71b28bb273d9fe5c512002c284801017cd83ec60a213400f7099fe5376030836cab8362bbdcf3f2e140d19717b63636002a220f00c180d90a006048005a005b284801010412f075a27a23fed2003b7f0918ff26a0798b486903d7aaac0c384924be85c00028220f00c0dcd9e0159648005c005d284801018384b6d13f51491c3dc5a92c6f55414e76921f16f096661b42cfa7ef2d13dc2c0028220f00c07da9dcdebc88005e005f284801015ee4d78b4a6696a8bd6f99fd4e9b5a8a0ef82fd6db96baee430e8ee940d2d8150027220f00c0263f5284d5080060006128480101d1221c2913cb043978e912874352c0deab04c19a71b663eebbe85246fc7ff3360027220d00b80475a408a800620063220d00a473eecad1a800640065284801010674cc3bcdcc002d5ee04ba3756620270b26b3e043def64b86a4be33f072b325001f220d00a243052cc4c8006600672848010174fd43549702febb84f3e4a320fe7026c18c32972e7b8150c58a3840fbfc27a00021220d00a13ecd9132e80068006928480101a90fb24e96236c4030e39c5f6adc4715ee4fb6bd15e110ca9d1e837a001a13c9001f220d00a0f56abf1848006a006b284801013d743a71525170eb814adf95340930873740a3c86ee90d2c09adf7342d14a1110014220b009bc4615d68006c006d2848010157f07701f4509e75e9a2546890d3536303a773a6afd2bdbba72acae8d4c42b7c001128480101ac21ff927b0dc2c6e2ec4e3101c0b457d40d98ef72ae38a6773d848b8f4345720010220b0091ebdc3f28006e006f28480101b764714e2fbf134ddebc215a410e5f8d32a997b1c445f5fc864010d2836b1fa4000c220b0090dbc6ed480070007128480101dfb67ff894ff36fddf4795f17fa2cd7f197f382a387574a1beed3a5e3f3b43ce0009220b0081ec8a160800720073220b00819335ec080074007528480101f13e00df6616bec9f650f13b8310ade3117d52488092b212411f21fcd92ee9d0000828480101c478a93d7a917cee39b6ad61168c616fd1b538b4ec817068e87d08331ff1aff200082199bb9f337d59dc7dfe997d3400b1453a206b2fa0ea8809b93ac952376dfa4316020618b408191a55eca1882e8b62e4a475c6ef7ef3d9f8ff7b6a2c5d1efa80577ef5c66a78c00002009c894f61600076226fc001f075f337d59dc7dfe997d3400b1453a206b2fa0ea8809b93ac952376dfa43162a8952dc31f6ed45800002009c894f61d030c5a04134000770078284801014bf53e3a47e0dcb199e3849b745718e6b33fff9f6523cace70daae10f6a115630009249b801f22b65c8aaf992d58ca89d846a05639a5da1ee4c437761e8965fba8a4b7679050000000000000000000000000000000000000000000000000000000000000001000000030000000100000001f0079007a007b007c2848010154f6037c842f362d2c5102120b97b94390eae40b3c2726999967bb1cf7d26350000b28480101a35ace2e8fdff03dd81d9ce084be6ce010046bd8fd1456b1a14a618f1b0bb1b90004284801010cb5ce55ed6896b617aa449398f77e0395bd030f81833470ced7910d71a36cb60003220bc110051340a0007d007e284801018a0acfa527a2e96e6e79babf342ed62751427c78a2ed73f5a01857d19de7bac800010203cfd8007f0080020120008100820002b600016b0001f2221100e0721303fefe1cc80085008628480101343750ef6a7acf47ecc19531554a7d9afb0d6df2fc0f53b368265b52fdf23d28002b220f00c2cadda22b6aa800870088284801013e2d7775fda54d1095c49a98ef7836b3dbe76d9bca5d51fa323a0d5e9d44e7e40080220f00c1cfa9ab6ea0c80089008a28480101b6df09e5a409d757b340241c896b9ee377b2b6e0d4809970af6cb6c84e109ac3002a220f00c18f7740ac63e8008b008c284801011999165f9da81c4bcbad14afe1f8ec5155fa1e793cf2dd11c9f32625511bf7aa012c220f00c1054a811859e8008d008e28480101a11667b0b8c7d78bb04d8b8534b0e9cb56cb929744c20ac206e8e14675e079bd002628480101683a0d980a16927420446dc33c10c1f3f64c0c42bd3cfeccd51d7ad0e1da912e0022220f00c0fe1295fadc68008f009028480101160ac93bf4c5be172cbc72d34f3605a0e22babaee211ddcc86832d98b74d0831001e220d00aa0de926014800910092220d00a98f705d732800930094284801013420ba80ac0760b54a0c20f19ed1a5f7960d23054bdc711c853c428f75fb3a13001e284801014124c80a37ce52d61d081e41acc8ab834628b81c1a21e8d22db0c9831b66d8470015220d00a3ab8aa89b0800950096284801011201565c4abc2f800a71b3836b56dc2cb6654e81631bb137c308c29e7c6d66250013220d00a0e4203512680097009828480101a6189e4320d9fb202f136b9f7c00e9e4c1b96b42fea8d17eda5609a27f9c3ac70010220d00a0e2ae1693a80099009a28480101e3d43824222efe258fe7a3007b763253e87910df967972f3d6b8c5b18b86b601000c220d00a0e1586a73c8009b009c220d00a0e0f20d5fc8009d009e284801010a54a29bd266e572d263c4ad4ec142613624a8bf273196388b11d6789cad461b000a220d40283482d6b7f2009f00a0284801017b7992cd966b921e9fea5ee844bdb18ad10ab935f7c37348946c5f29e5c40e64000128480101dec9429b6cc4b3cf635937bf956ef6d21c101bec9ac708005c6da82e6b5265260009219bbba92f7406a52f09178232ee54bf88812be7fa83f0fc62da39e529bb6d47be028346dbbb081708a2ca67b280b72b7744548e3836cb9dd0a698fdb5a51554a501034b6e407dc00002009c894f61e000a12271c0041bc692f7406a52f09178232ee54bf88812be7fa83f0fc62da39e529bb6d47be22c8590431f6ed45800002009c894f62141a36ddd84134000a200a328480101feb5ff6820e2ff0d9483e7e0d62c817d846789fb4ae580c878866d959dabd5c00007005100000ab429a9a317f2023e9c5b6ca86e5091813ea22f532a50c6b673d7ddb07551948339c40153224028480101a983060414584642c9edc138111fc94ecdb71c624286f181702ed4d279d48b12002e221100e0456dd4d19587e800a600a7284801016e8688607ba9e3246905b7211943212e9ffd2a60d1b00e22787b087dbe59bed7002e221100e0240f59b864532800a800a9220f00c573de328e018800aa00ab28480101e6ddafca79cf720c6540a3be6a9eb43158fd491fa2ad165e99eab7a04dc9c7d9002d220f00c20434e05ede0800ac00ad28480101d06c03c570d0be832260500e38bf3c4476783684738d639c7a452400488426cf012b220f00c1b397a6838cc800ae00af284801018a4edbc743d47034301709135a4f0c65690f0d846d8b828c1c4b160270445518002928480101d068fea827db36b838d6f80d73cfa92acd7a73b17b69ed2437c47c776e77c59b0024220f00c053ecb3a6cb2800b000b12848010101d63fc57d90606ced39ffc48fe7fe0087325ba8d84a68ca924267005d1bc02a0024220f00c049a9a71cdd8800b200b3284801017a1384b46068e26b77733a4f306a25471afe598be69e4bc74b581ba1768ff6660024220f00c026f6914b904800b400b528480101017aec50d47444736eb36eea822de7aeae1aefbfaf6c16f08aceba977b86a5590022220d00a743e5c67da800b600b728480101bd247b2186d992bdf7f25a7ad5940ad64ed996b580e603c63b159d59e24a5382001e220d00a0c4954a55e800b800b928480101e75c9228f15fb15b26a06df81aa9594f30b86ed0ada068fc1db7a3a5d4ea369d0012220d00a02ad016356800ba00bb220b0091bc38e4a800bc00bd28480101fcf44587902c51beee4862f6b6f97c594db123680aa9cbfc64171a855f1895a90012220b0081801751e800be00bf284801017ab950ec3a2acab17a3f7d92489a755a1e12b22525a556fea33b3ff22b1523d1000d220b00816f9da2e800c000c1284801016a9bba355c9c583f02e488d819c7290b652c9c3ea0114d69a796c37ae00d4c5b000a220b008116fe86e800c200c328480101741a8ef418161f47321ecc1a551905aecb8f92c669a7e64a5841913ad2eac752000c220b0080368a156800c400c5284801013d5adb82ba0ebbd14baabcc9a6014371ca27da097e521afa25b4e32af2821b1f000a28480101543ee7dd9187b578c8acb09005a539f180c9e0ac841e19cdef1c339e4690b23f00082199bbb0cab616faab89f4906e67d452d2783d92a0af560aceb6afc4014427ed020200a604adbdfd5286d1e50e3b551b7f6382c6ad4b0974dacc9c37417278b47aec950018ea800002009c894f60e000c62271c00e3f070cab616faab89f4906e67d452d2783d92a0af560aceb6afc4014427ed023a4c0511b031f6ed45800002009c894f61500530256d34000c700c828480101e898e7ab7f3acd6b6041f323449cf25f79cf7787bf0f034abf195e514a268d48001022d7801f22b65c8aaf992d58ca89d846a05639a5da1ee4c437761e8965fba8a4b767904000000000000000000000000000000000000000000000000000000000000000204000000000000000180102d54b4b91b48a13e69a2219aeaa55d7cb4465d7ae5148071f634ec0b4ffcbd500c900ca28480101075bfe88f459ebe321541f977b825ef72e62ffce5dae883d11a46c89cb34bab4001322df80111ead3fcdce487c896fdaafeda88ec987a697b5e30d58a7914b37f74390b839100106f1a4bdd01a94bc245e08cbb952fe2204af9fea0fc3f18b68e794a6edb51efa0000000600000000000000000000000000000000000000000000000000000000000000000000000240000000a000cb00cc284801019dfa0bf465e64775c20410b7361eb96b2558e89d355f4bd75a71c1e8530472d50001284801017237e3a4111c87d7fdf381f1b28a07fcf2c6eb9b1d1092e5b2cdd6ef6fbaf9f2000302090ca8e7ec1000ce00cf0253bfc45b1b2a94e3a5fd0357edc092e26a9368c726ab7ca1a7e9e338d2c42a5ef60d18516180198516184000d800ff02090c80372c1000d000d10252bf8d215f22a136dbc5da9d8a92dabe5bca1906445d8ac8aab2567cf7ce97ba3cf03138fda033138fda00dc00e402090c31f7c41000d200d30251bf40e4caff642735e282f8b2317dbe50edaadfc4a2e72c2df1f1fafee0fe04a7fa618fbe206618fbe300de00f70245bf4f3b9675139bbc98deb9c607adf136c710faf78d9d807977c5332af686b99384001000f200ee02010100d500d60345bfc45b1b2a94e3a5fd0357edc092e26a9368c726ab7ca1a7e9e338d2c42a5ef60d014000d800ee00d712013afe12276f1c76fbf18fa7b2cb09d82cee2e2f43e65770b846e310ccf555fe4c00070100d900da02076614586100d800ff010c4606030a2c3001030344bf8d215f22a136dbc5da9d8a92dabe5bca1906445d8ac8aab2567cf7ce97ba3cf00200dc00ff00db0344bf8072657fb2139af1417c5918bedf2876d56fe251739616f8f8fd7f707f0253fd0200de00e400dd020766271fb500dc00e4010c460603138fda010502076618fbe300de00f7010c4606030c7df100fb0209100917fbad00e000e102091004c427e100e200e302a8bff1f838655b0b7d55c4fa483733ea29693c1ec95057ab05675b57e200a213f6812008a7a79978fc1c32ad85beaae27d241b99f514b49e0f64a82bd582b3adabf1005109fb40a8000001004e44a7b0680229e9e600ff010102a3bf9f075f337d59dc7dfe997d3400b1453a206b2fa0ea8809b93ac952376dfa43163d21f4328f83af99beacee3eff4cbe9a0058a29d103597d0754404dc9d64a91bb6fd218b50000002009c894f614f487d0d00e400e603a3bf81bc692f7406a52f09178232ee54bf88812be7fa83f0fc62da39e529bb6d47be35eeab52a0de3497ba035297848bc119772a5fc44095f3fd41f87e316d1cf294ddb6a3df4f400002009c894f606bdd56a800eb00ec00ed03b571f075f337d59dc7dfe997d3400b1453a206b2fa0ea8809b93ac952376dfa43160000080272253d85d6f57df6686e353a3f0adbd063896ff110327b641626c8ce6f02e0403011ee13000008026c7ba7c563edda8b000347a43e86800e500e600e70201e0010500e8008272c1ca49b77e05cb74b586f811af79f8cd857dcd76bd4658c9ce4d8ee7aa85a1af99bfd3aec401a8819ab497c15948b90934944dc9f7db8fd72bf8f3b522f0582c0217047b09013358aa18797bed1100e900ea0101df00fb009e46862c04eae400000000000000017700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006fc9895e704c18fbbc0000000000020000000000025a53fd54dd36a4d0d750acce3e5ed7b935c607eaaebbea4a3c65a8d33a04475440901a0c010964d3f2e75000ee0109f1878f8c2000f7008272d5bb680b3041dff56a9e7d9dc11becd88d0dd57ff767f3753533ec9962c8f4f8ba1924caa1f8af8ab2322d1b4a4b883150ec3a4d677546d6a366ba714ce8664f03b5741bc692f7406a52f09178232ee54bf88812be7fa83f0fc62da39e529bb6d47be0000080272253d81c6aa19d9fa298203f743da7fdd43da18ea8e3ae93a7ec2370cf32da0a578b1c80000080264e9ca1063edda8b0003469f973a800ef00f000f10201e000f200f3008272d5bb680b3041dff56a9e7d9dc11becd88d0dd57ff767f3753533ec9962c8f4f8996c8498471bf244f798348384ce93c85311d95b655ce7677ccb77be1b19802c020f0c63461993cf044000f500f601e188008378d25ee80d4a5e122f0465dca97f110257cff507e1f8c5b473ca5376da8f7c0793ee806a67602ab0c777ef81efdc55b37003ae9dde0489875cf2c291c16a18162c1eed62833f6f7050f87619d8d2822fe5265204e36a2736203cc5e8ea59b0494d4d18bb1f6ed63000005598000c00f40101df0103007a620071f838655b0b7d55c4fa483733ea29693c1ec95057ab05675b57e200a213f681202faf08000000000000000000000000000000000000436c61696d009d419d8313880000000000000000110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020006fc987a1204c14584000000000000200000000000225a47ea2e18d826e18d44ee0b6070689059be2f46de52e4b1d8eb3d94fe774ae4050184c03b5741bc692f7406a52f09178232ee54bf88812be7fa83f0fc62da39e529bb6d47be0000080272253d8787c3ca3b853f9f1d1625fc95e465285405c4c814f81d9b63b16abc2a8be6973a0000080272253d8163edda8b0001461e3e30800f800f900fa0101a000fb008272996c8498471bf244f798348384ce93c85311d95b655ce7677ccb77be1b19802cba1924caa1f8af8ab2322d1b4a4b883150ec3a4d677546d6a366ba714ce8664f0215040901df50755861e3e31100fd00fe01b168003e0ebe66fab3b8fbfd32fa6801628a7440d65f41d51013727592a46edbf4862d00106f1a4bdd01a94bc245e08cbb952fe2204af9fea0fc3f18b68e794a6edb51ef901df507540618fbe200001004e44a7b0cc7dbb516c000fc002000000000436c61696d20526577617264009e407bec07ab4400000000000000001d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005bc00000000000000000000000012d452da449e50b8cf7dd27861f146122afe1b546bb8b70fc8216f0c614139f8e0403b77e3f070cab616faab89f4906e67d452d2783d92a0af560aceb6afc4014427ed020000080272253d838dd6ebb3b532a7260c3e7a1ee233d985fba01dca343283c076c2a72ce3564a01000008026451338c63edda8b0003480229e9e680100010101020201e001030104008272cc22777aaa80cfc0aa89ffcae6ac217c2b529b1dcbfad6d877abf1cf6a73e0b0bc553edcf31e8f8bfb1f3c10289924b6b7c1ed0b4e503d84de9e358bef79c14a021b04819d49017d784018802164d4110107010800c368008378d25ee80d4a5e122f0465dca97f110257cff507e1f8c5b473ca5376da8f7d0038fc1c32ad85beaae27d241b99f514b49e0f64a82bd582b3adabf1005109fb409017d784000614586000001004e44a7b04c7dbb5160000000021b630b4b6c00101df010501b16801c7e0e1956c2df55713e920dccfa8a5a4f07b25415eac159d6d5f8802884fda050007c1d7ccdf56771f7fa65f4d002c514e881acbe83aa2026e4eb2548ddb7e90c59013358aa006271fb400001004e44a7b08c7dbb516c0010600cdee3b3cee0000000000000000000000000000000000000000000000000000000000000001400e3f070cab616faab89f4906e67d452d2783d92a0af560aceb6afc4014427ed028008378d25ee80d4a5e122f0465dca97f110257cff507e1f8c5b473ca5376da8f7d009e488c8c061a8000000000000000019900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006fc98eabdc4c271f7800000000000200000000000248285ea0fd47f7bb381418103f5af3db5b687ba1984b385cacb8ac70da82232a40902fc48e6f7e77
	at org.ton.boc.BagOfCells$Companion.of(BagOfCells.kt:47)
	at org.ton.boc.BagOfCellsKt.BagOfCells(BagOfCells.kt:8)
	at org.ton.lite.client.LiteClient.getBlock(LiteClient.kt:314)
	... 95 more
Caused by: java.lang.IllegalStateException: First hash mismatch in merkle update cell:
merkle hash: c8009bc4fe2bd607fa56fc245e84e1b89b9f705d00352b4e3b5969a354d478ec
 child hash: a0dd25b4d971457d11c22f3baeeac15d4ec44db3ead091ea7dcf166f4fec28cb
       data: 04c8009bc4fe2bd607fa56fc245e84e1b89b9f705d00352b4e3b5969a354d478ecc4d43fcbe7ed75ef648df4d2d0e7582d2e2f1df1907a01739dd0a93d04ffb62901340134
	at org.ton.cell.CellImpl$Companion.of(CellImpl.kt:144)
	at org.ton.cell.Cell$Companion.of(Cell.kt:86)
	at org.ton.boc.BagOfCellsUtilsKt.createCell(BagOfCellsUtils.kt:317)
	at org.ton.boc.BagOfCellsUtilsKt.createCell(BagOfCellsUtils.kt:156)
	at org.ton.boc.BagOfCellsUtilsKt.readBagOfCell(BagOfCellsUtils.kt:127)
	at org.ton.boc.BagOfCells$Companion.read(BagOfCells.kt:52)
	at org.ton.boc.BagOfCells$Companion.of(BagOfCells.kt:45)
	... 97 more


Caused by: java.lang.IllegalStateException: bag-of-cells error: reference #0 of cell #0 is to non-existent cell #10312, only 1172 cells are defined

Exception in thread "main" java.lang.IllegalArgumentException: Can't load BoC: https://gist.github.com/andreypfau/10263901d0bd943a6f012a487855da50
	at org.ton.boc.BagOfCells$Companion.of(BagOfCells.kt:39)
	at org.ton.boc.BagOfCellsKt.BagOfCells(BagOfCells.kt:7)
	at org.ton.lite.api.liteserver.LiteServerBlockData.dataBagOfCells(LiteServerBlockData.kt:21)
	at org.ton.lite.api.liteserver.LiteServerBlockData.toBlock(LiteServerBlockData.kt:23)
	at TestKt.transactionsRealtime(test.kt:58)
	at TestKt$transactionsRealtime$1.invokeSuspend(test.kt)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlin.coroutines.SafeContinuation.resumeWith(SafeContinuationJvm.kt:41)
	at org.ton.adnl.AdnlTcpClient$sendQuery$2$1.invokeSuspend(AdnlTcpClient.kt:72)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
	at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:749)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)
Caused by: java.lang.IllegalStateException: bag-of-cells error: reference #0 of cell #0 is to non-existent cell #10312, only 1172 cells are defined
	at org.ton.boc.BagOfCellsUtilsKt.readBagOfCell(BagOfCellsUtils.kt:110)
	at org.ton.boc.BagOfCells$Companion.of(BagOfCells.kt:37)
	... 14 more
	```

0.2.4 getBlock() Broken

The following example code:

import io.ktor.util.*
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import org.ton.api.liteserver.LiteServerDesc
import org.ton.api.pub.PublicKeyEd25519
import org.ton.lite.client.LiteClient

runBlocking {
    val liteClient = LiteClient(
        Dispatchers.IO + CoroutineName("liteClient"),
        LiteServerDesc(
            ip = 1091931625,
            port = 30131,
            id =  PublicKeyEd25519("wrQaeIFispPfHndEBc0s0fx7GSp8UFFvebnytQQfc6A=".decodeBase64Bytes())
    ))
    
    val id = liteClient.getLastBlockId()
    val block = liteClient.getBlock(id)
}

Results in an exception:

java.lang.IllegalStateException: file hash mismatch for block (-1:8000000000000000:26309171):78E54296F03D8AAFF58CD8766879F7C299538286481E012E47339C0E06A2AA84:3CA71A2055E6F4438169C8F86D0072BF074FF56776A548B4082ADC66CCD8BFF9, expected: PKcaIFXm9EOBacj4bQByvwdP9Wd2pUi0CCrcZszYv/k= , actual: 3A31F4AF1B18B312E78A8554678BA1CE9E22D66B2D35E1D505FAFB7D6A7D146C
  at org.ton.lite.client.LiteClient.getBlock(LiteClient.kt:285) ~[ton-kotlin-liteclient-jvm-0.2.4.jar:na]
  at org.ton.lite.client.LiteClient$getBlock$3.invokeSuspend(LiteClient.kt) ~[ton-kotlin-liteclient-jvm-0.2.4.jar:na]

IllegalArgumentException when send LiteServerGetTransactions request

Get IllegalArgumentException at TlConstructor.decodeBoxed(TlConstructor.kt:28) when I try to send LiteServerGetTransactions request using LiteApi.

Stack trase:

java.lang.IllegalArgumentException: Invalid ID. expected: 9dd72eb9 (-1188112483) actual: 0bc6266f (1864812043)
	at org.ton.tl.TlConstructor.decodeBoxed(TlConstructor.kt:28)
	at org.ton.tl.TlDecoder$DefaultImpls.decodeBoxed(TlDecoder.kt:11)
	at org.ton.tl.TlCodec$DefaultImpls.decodeBoxed(TlCodec.kt:5)
	at org.ton.tl.TlConstructor.decodeBoxed(TlConstructor.kt:7)
	at org.ton.lite.api.liteserver.LiteServerTransactionList$Companion.decodeBoxed(LiteServerTransactionList.kt)
	at org.ton.lite.api.liteserver.LiteServerTransactionList$Companion.decodeBoxed(LiteServerTransactionList.kt:30)
	at org.ton.lite.api.LiteApiClient$DefaultImpls.sendQuery(LiteApiClient.kt:37)

Reproducer:

suspend fun transactionList(address: String = "EQBJpDx2dFtSAkHr8AMB4by3KeON_5GA0fC_rBYkWljFLqbg") {
    val liteClient = LiteClient(
        coroutineContext = currentCoroutineContext(),
        LiteServerDesc(
            id = PublicKeyEd25519(base64("n4VDnSCUuSpjnCyUk9e3QOOd6o0ItSWYbTnW3Wnn8wk=")),
            ip = 84478511,
            port = 19949
        )
    )
    val accountId = LiteServerAccountId(AddrStd(address))

    // Hardcoded data from the SharedAccount
    val lastTrLt = 35163722000006L
    val lastTrHash = BitString("4D40CB31E89AB79AC54A6ED840EEDC586E876034F548CDC39E99C0DA18E2475B").toByteArray()

    // throws IllegalArgumentException
    val transactionList = liteClient.liteApi(
        LiteServerGetTransactions(
            count = 1,
            account = accountId,
            lt = lastTrLt,
            hash = lastTrHash
        )
    )
}

Add `readRemaining` to CellSlice

fun CellSlice.readRemainingBits(): BitString {
    return BitString((this.bitsPosition until this.bits.size).map { this.loadBit() })
}

IndexOutOfBoundsException when iterating AugDictionaryRoot

Trying to get the last transaction hash value from LiteServerAccountState proof field. And get the exception java.lang.IndexOutOfBoundsException: Empty list doesn't contain element at index 0. while iterating AugDictionary. Code example:

suspend fun printLastLtHash(address: String = "EQBnRcXDUmnXeAtPcsmGUQRhpvAyca4H0tHJ7edkj_pxNgBB") {
    val liteClient = LiteClient(
        coroutineContext = currentCoroutineContext(),
        LiteServerDesc(
            id = PublicKeyEd25519(base64("n4VDnSCUuSpjnCyUk9e3QOOd6o0ItSWYbTnW3Wnn8wk=")),
            ip = 84478511,
            port = 19949
        )
    )
    val accountId = LiteServerAccountId(AddrStd(address))

    val lastBlock = liteClient.getLastBlockId()
    val accountState = liteClient.liteApi(
        LiteServerGetAccountState(
            id = lastBlock,
            account = accountId
        )
    )

    BagOfCells(accountState.proof).first().beginParse().run {
        val slice = loadRef().beginParse()
        val shardState = slice.loadTlb(ShardState)

        when (shardState) {
            is SplitState -> TODO()
            is ShardStateUnsplit -> {
                val dictionaryRoot = shardState.accounts.value.x as AugDictionaryRoot<ShardAccount, DepthBalanceInfo>

                // Here we will get "java.lang.IndexOutOfBoundsException: Empty list doesn't contain element at index 0."
                // at loading some AugDictionaryNodeFork
                dictionaryRoot.root.value.nodes().forEach(::println)
            }
        }
    }
}

Part of the stacktrace:

java.lang.IndexOutOfBoundsException: Empty list doesn't contain element at index 0.
	at kotlin.collections.EmptyList.get(Collections.kt:36)
	at kotlin.collections.EmptyList.get(Collections.kt:24)
	at org.ton.cell.CellSliceImpl.preloadRef(CellSlice.kt:110)
	at org.ton.cell.CellSliceImpl.loadRef(CellSlice.kt:103)
	at org.ton.hashmap.AugDictionaryNodeForkTlbConstructor.loadTlb(AugDictionaryNodeFork.kt:80)
	at org.ton.hashmap.AugDictionaryNodeForkTlbConstructor.loadTlb(AugDictionaryNodeFork.kt:40)
	at org.ton.hashmap.AugDictionaryEdgeTlbConstructor.loadTlb(AugDictionaryEdge.kt:81)
	at org.ton.hashmap.AugDictionaryEdgeTlbConstructor.loadTlb(AugDictionaryEdge.kt:42)
        ...

Is this a TLB parsing problem or is the sample code just incorrect?

The goal is to get ShardAccount in the middle of the dictionary with cell:

value:(account_descr
  account:(!pruned_branch cell:x{0101CEFC031D0FDB88C6D2146567C...}) last_trans_hash:48E0F3D18A857233C3BB07D... last_trans_lt:350803850...)))

Library version: 0.2.14

Coins.toString() incorrect results

If Coins variable stores at least 1 TON, function will return incorrect result, for example the following:

val nanoAmount: Long = 1_000_000_000
val coins = Coins.ofNano(nanoAmount)
coins.toString()

results in 1.999999999

Let's look at the toString() code:

override fun toString(): String = buildString {
    val full = amount.value / NANOCOINS
    val decimal = amount.value - full
    append(full)
    append(".")
    append(decimal.toString().padStart(9, '0'))
}

In the line val decimal = amount.value - full the value in TON (full) is subtracted from the value in nanoTON (amount.value), which makes decimal part incorrect. In my opinion, changing to val decimal = amount.value - full * NANOCOINS, or val decimal = amount.value % NANOCOINS would fix the problem.

Cell representation computation is obscure

At the moment Cell.representation() is implemented similarly to tonweb, however it is not clear what this implementation is based on.
According to the almighty tblkch.pdf, 1.1.3, CellRepr consists of following:

  1. Two descriptor bytes
  2. Data bytes, with optional padding when length of the cell is not divisible by 8
  3. References to other cells, where each reference is the Sha256 hash of that referenced cell

However, tonweb and ton-kotlin at the moment also add reference cells' maxDepth encoded as 16-bit integer between 2. and 3.; it might be somehow connected to 5.3.6 but I am not quite sure.

To clear things up, it is necessary to either try and find better description in tblkch.pdf or a piece of code that implements this behaviour in the main TON repository.

Arithmetic operations on Coins type

Coins should be more useful as a type to represent monetary values. It should be possible to work with them similarly to generic number types such as integer, while preserving correctness and being very strict to prevent possible mistakes.

It might look something like:

val a = Coins.ofNano(10_000_000) // 0.01 TON
val b = Coins.of(1.0) // Operating on floating-point values is a bad practice for working with money
                                 // so wrap it in safe Coins type. This is now 1_000_000_000 nanotons

a + b // 1_010_000_000 nanotons, 1.01 TON
a - b // 990_000_000 nanotons, 0.99 TON

I also propose to forbid any operations with generic numeric types, as it will be more error-prone. Unlike .of() and .ofNano() functions, it cannot be explicitly specified what a certain value represents until you wrap it in Coins class:

Coins.of(1.0) + 1   // 1_000_000_001 nanotons, because 1 is an integer value representing 1 nanoton
Coins.of(1.0) + 1.0 // 2_000_000_000 nanotons, because 1.0 is a floating point value representing 1 ton

As you can see, a simple typo may result in a 9 order of magnitude difference, which can become a very expensive mistake to make.
Unlike operating on floating points values for monetary purposes, a mistake that lies clearly on the shoulders of the programmer, this problem would indicate poor design on the ton-kotlin's side and its API being not strict enough.

With this, I suggest only allowing operations on Coins type itself, with no possibility of mixing in other numeric types without explicitly wrapping it with Coins.of() or Coins.ofNano().

Also, operations such as multiplication and division must be carefully thought through.

Can't parse seqno from SeqnoContract

Using SeqnoContract#seqno() or SeqnoContract#seqno(TonNodeBlockIdExt) throws an IllegalArgumentException

Stacktrace:

java.lang.IllegalArgumentException: Can't load BoC: 
	at org.ton.boc.BagOfCells$Companion.of(BagOfCells.kt:39)
	at org.ton.boc.BagOfCellsKt.BagOfCells(BagOfCells.kt:7)
	at org.ton.lite.api.liteserver.LiteServerRunMethodResult.resultBagOfCells(LiteServerRunMethodResult.kt:45)
	at org.ton.lite.api.liteserver.LiteServerRunMethodResult.resultStack(LiteServerRunMethodResult.kt:48)
	at org.ton.lite.api.liteserver.LiteServerRunMethodResult.resultValues(LiteServerRunMethodResult.kt:53)
	at org.ton.lite.api.liteserver.LiteServerRunMethodResult.iterator(LiteServerRunMethodResult.kt:56)
	at kotlin.collections.CollectionsKt___CollectionsKt.first(_Collections.kt:199)
	at org.ton.contract.wallet.SeqnoContract$DefaultImpls.seqno(SeqnoContract.kt:14)
	at org.ton.contract.wallet.SeqnoContract$seqno$2.invokeSuspend(SeqnoContract.kt)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.internal.ScopeCoroutine.afterResume(Scopes.kt:33)
	at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:102)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
	at kotlinx.coroutines.UndispatchedCoroutine.afterResume(CoroutineContext.kt:202)
	at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:102)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
	at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:279)
	at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85)
	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
	at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
	at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
	at java.base/java.lang.reflect.Method.invoke(Method.java:577)
	at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:725)
	at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
	at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
	at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:214)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:210)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:66)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:96)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:75)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:99)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:79)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:75)
	at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:61)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
	at java.base/java.lang.reflect.Method.invoke(Method.java:577)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
	at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
	at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
	at jdk.proxy1/jdk.proxy1.$Proxy2.stop(Unknown Source)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:193)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
	at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:133)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71)
	at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
	at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
Caused by: java.io.EOFException: Premature end of stream: expected 4 bytes
	at io.ktor.utils.io.core.StringsKt.prematureEndOfStream(Strings.kt:453)
	at io.ktor.utils.io.core.InputPrimitivesKt.readIntFallback(InputPrimitives.kt:86)
	at io.ktor.utils.io.core.InputPrimitivesKt.readInt(InputPrimitives.kt:17)
	at org.ton.boc.BagOfCellsUtilsKt.readBagOfCell(BagOfCellsUtils.kt:13)
	at org.ton.boc.BagOfCells$Companion.of(BagOfCells.kt:37)
	... 102 more

Moreover, code

suspend fun seqno(blockIdExt: TonNodeBlockIdExt): Int {
        val liteServerAccountId = LiteServerAccountId(address())
        val result = liteApi.runSmcMethod(4, blockIdExt, liteServerAccountId, "seqno")
        return (result.first() as VmStackTinyInt).value.toInt()
    }

works perfectly before any interactions with result from liteApi.runSmcMethod(4, blockIdExt, liteServerAccountId, "seqno")

java.util.Base64 breaks Android 25- compability

At least in file base64.kt - https://github.com/andreypfau/ton-kotlin/blob/main/ton-kotlin-crypto/src/jvmMain/kotlin/org/ton/crypto/base64.kt class java.util.Base64 is used.
Unfortunately, this class was only added in API level 26 - https://developer.android.com/reference/java/util/Base64.
This makes impossible for apps, based on this library, to run on Android API level 25 or lower.
Possible solution could be to use android.util.Base64 instead.

According to Android studio, 11,8% of current android devices have API level 25 or lower.

ย SmartContracts suggestions

  • add into org.ton.contract.SmartContract#runGetMethod optional lastBlock? = null argument for multiple same-type requests in context of same block
  • allow to override code of any SmartContract

Kotlin/Native support

Being a Kotlin Multiplatform project, no platform other than Kotlin/JVM is currently supported.

Ability to target native platforms with ton-kotlin is important for multiple reasons:

  • (Fixed in fcac450) Currently only JVM versions 11 and up are supported, meaning that to run an application based on ton-kotlin users will need to install additional JDK (java 1.8 is still the standard when it comes to desktops). Removing this dependency altogether will make shipping of end user apps much easier
  • Short-lived command line utilities will benefit from decreased start-up times compared to running on JVM
  • Possibility of invoking ton-kotlin from other languages through a compatible C API. Current standard native ton library is tonlibjson, which spots obnoxious json-based interface, leaks memory like you wouldn't believe, and is a general eyesore. With ton-kotlin built for native platforms, SDKs for other languages and frameworks can be set up as bindings to it.

I can't set derivation path to this library

Hi i want to set derivation path to my wallet to create a trust wallet like address but i can't .
this is my code.

val mnemonic =
"sample"
val seed = Mnemonic.toSeed(
mnemonic.split(" "),
)
val privateKey =
PrivateKeyEd25519(seed)

val contract =
WalletV4R2Contract(liteClient, WalletV4R2Contract.address(privateKey))

`ArrayIndexOutOfBoundsException: Index 36 out of bounds for length 36` while call `CellBuilder.bits.toBooleanArray()`

CellBuilder has size 1023 and bits too, but for some reason BitString has only 36 bytearray, instead 128

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 36 out of bounds for length 36
	at org.ton.bitstring.ByteBackedBitString$Companion.get(ByteBackedBitString.kt:174)
	at org.ton.bitstring.ByteBackedBitString.get(ByteBackedBitString.kt)
	at org.ton.bitstring.ByteBackedBitString.getOrNull(ByteBackedBitString.kt:15)
	at org.ton.bitstring.ByteBackedBitString.get(ByteBackedBitString.kt:12)
	at org.ton.bitstring.ByteBackedBitString$BitStringIterator.next(ByteBackedBitString.kt:105)
	at org.ton.bitstring.ByteBackedBitString$BitStringIterator.next(ByteBackedBitString.kt:99)
	at kotlin.collections.CollectionsKt___CollectionsKt.toBooleanArray(_Collections.kt:1086)
	at org.ton.bitstring.ByteBackedBitString.toBooleanArray(ByteBackedBitString.kt:31)

HashMapE.fromMap conversion error

Sample:


import org.junit.jupiter.api.Test
import org.ton.bitstring.BitString
import org.ton.block.*
import org.ton.cell.Cell
import org.ton.cell.CellBuilder
import org.ton.contract.wallet.WalletTransfer
import org.ton.hashmap.HashMapE
import org.ton.tlb.CellRef
import org.ton.tlb.constructor.AnyTlbConstructor
import org.ton.tlb.constructor.tlbCodec
import org.ton.tlb.storeTlb
import org.ton.tlb.storeRef


class HashMapTest {

    fun createIntMsg(gift: WalletTransfer): MessageRelaxed<Cell> {
        val info = CommonMsgInfoRelaxed.IntMsgInfoRelaxed(
                ihrDisabled = true,
                bounce = gift.bounceable,
                bounced = false,
                src = AddrNone,
                dest = gift.destination,
                value = gift.coins,
                ihrFee = Coins(),
                fwdFee = Coins(),
                createdLt = 0u,
                createdAt = 0u
        )
        val init = Maybe.of(gift.stateInit?.let {
            Either.of<StateInit, CellRef<StateInit>>(null, CellRef(it))
        })
        val body = if (gift.body == null) {
            Either.of<Cell, CellRef<Cell>>(Cell.of(), null)
        } else {
            Either.of<Cell, CellRef<Cell>>(null, CellRef(gift.body!!))
        }

        return MessageRelaxed(
                info = info,
                init = init,
                body = body,
        )
    }

    @Test
    fun hmapTest() {
        val targets = listOf(
                AddrStd("kQC7ryRspjsMrdnuuA98uGyLMUVd_dDWjroYLBHoIz9ovl7e")
        )
        val transfers = targets.map {
            WalletTransfer {
                destination = it
                coins = Coins.of(1)
                bounceable = true
                body = null
                stateInit = null
            }
        }
        val transfersBody = transfers.mapIndexed { index, walletTransfer ->
            BitString(index) to CellBuilder.createCell {
                storeUInt(walletTransfer.sendMode, 8)
                storeRef(MessageRelaxed.tlbCodec(AnyTlbConstructor), CellRef(createIntMsg(walletTransfer)))
            }
        }.toMap()

        CellBuilder.createCell {
            storeTlb(HashMapE.tlbCodec(16, Cell.tlbCodec()), HashMapE.fromMap(transfersBody))
        }
    }
}

StackTrace

class org.ton.hashmap.HmnLeaf cannot be cast to class org.ton.hashmap.HmnFork (org.ton.hashmap.HmnLeaf and org.ton.hashmap.HmnFork are in unnamed module of loader 'app')
java.lang.ClassCastException: class org.ton.hashmap.HmnLeaf cannot be cast to class org.ton.hashmap.HmnFork (org.ton.hashmap.HmnLeaf and org.ton.hashmap.HmnFork are in unnamed module of loader 'app')
	at org.ton.hashmap.HashMapNodeForkTlbConstructor.storeTlb(HmnFork.kt:52)
	at org.ton.hashmap.HashMapEdgeTlbConstructor.storeTlb(HmEdge.kt:122)
	at org.ton.hashmap.HashMapEdgeTlbConstructor.storeTlb(HmEdge.kt:92)
	at org.ton.tlb.CellRefValue$toCell$2.invoke(CellRef.kt:76)
	at org.ton.tlb.CellRefValue$toCell$2.invoke(CellRef.kt:75)
	at org.ton.cell.CellBuilder$Companion.createCell(CellBuilder.kt:97)
	at org.ton.tlb.CellRefValue.toCell(CellRef.kt:75)
	at org.ton.tlb.CellRefTlbConstructor.storeTlb(CellRef.kt:87)
	at org.ton.tlb.CellRefTlbConstructor.storeTlb(CellRef.kt:83)
	at org.ton.hashmap.RootHashMapETlbConstructor.storeTlb(HmeRoot.kt:73)
	at org.ton.hashmap.RootHashMapETlbConstructor.storeTlb(HmeRoot.kt:44)
	at org.ton.tlb.TlbCombinator.storeTlb(TlbCombinator.kt:59)

[0.2.7] liteClient.getAccount : Premature end of stream: expected 4 bytes for UNINIT account

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Test
import org.ton.api.liteclient.config.LiteClientConfigGlobal
import org.ton.api.liteserver.LiteServerDesc
import org.ton.api.pub.PublicKeyEd25519
import org.ton.block.AccountActive
import org.ton.block.AccountInfo
import org.ton.block.AccountNone
import org.ton.block.AccountUninit
import org.ton.crypto.base64
import org.ton.lite.client.LiteClient
import kotlin.test.assertNotNull
import kotlin.test.assertTrue


class LiteClientTest2 {

    private fun getClient() = LiteClient(
        liteClientConfigGlobal = LiteClientConfigGlobal(
            liteServers = listOf(
                LiteServerDesc(id = PublicKeyEd25519(base64("n4VDnSCUuSpjnCyUk9e3QOOd6o0ItSWYbTnW3Wnn8wk=")), ip = 84478511, port = 19949)
            )
        ),
        coroutineContext = Dispatchers.Default
    )

    
    @Test
    fun getAccount() {
        val liteClient = getClient()
        runBlocking {
            val data = liteClient.getAccount("EQAs87W4yJHlF8mt29ocA4agnMrLsOP69jC1HPyBUjJay-7l")

            // assume that active account is OK
            assertTrue(data is AccountInfo)
            assertNotNull(data.storage.state)
            assertTrue(data.storage.state is AccountActive)

            val data2 = liteClient.getAccount("EQDTwjlbMcG4gLgw_fmf-swoLmvaGuppGbn--6HWTUCAui0Y")

            // assume that uninitialized account is OK
//            assertTrue(data2 is AccountNone)
        }
    }

Result:

Can't load BoC: 
java.lang.IllegalArgumentException: Can't load BoC: 
	at org.ton.boc.BagOfCells$Companion.of(BagOfCells.kt:47)
	at org.ton.boc.BagOfCellsKt.BagOfCells(BagOfCells.kt:8)
	at org.ton.lite.client.LiteClient.getAccount(LiteClient.kt:427)
	at org.ton.lite.client.LiteClient$getAccount$4.invokeSuspend(LiteClient.kt)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.internal.ScopeCoroutine.afterResume(Scopes.kt:33)
	at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:102)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
	at kotlinx.coroutines.UndispatchedCoroutine.afterResume(CoroutineContext.kt:202)
	at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:102)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
	at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:279)
	at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85)
	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
	at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
	at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
	at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
	at com.demo.LiteClientTest2.getAccount(LiteClientTest.kt:34)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
	at java.base/java.lang.reflect.Method.invoke(Method.java:577)
	at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:725)
	at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
	at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
	at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:214)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:210)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:66)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:96)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:75)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:99)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:79)
	at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:75)
	at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:61)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
	at java.base/java.lang.reflect.Method.invoke(Method.java:577)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
	at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
	at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
	at jdk.proxy1/jdk.proxy1.$Proxy2.stop(Unknown Source)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:193)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
	at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:133)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71)
	at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
	at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
Caused by: java.io.EOFException: Premature end of stream: expected 4 bytes
	at io.ktor.utils.io.core.StringsKt.prematureEndOfStream(Strings.kt:453)
	at io.ktor.utils.io.core.InputPrimitivesKt.readIntFallback(InputPrimitives.kt:86)
	at io.ktor.utils.io.core.InputPrimitivesKt.readInt(InputPrimitives.kt:17)
	at org.ton.boc.BagOfCellsUtilsKt.readBagOfCell(BagOfCellsUtils.kt:12)
	at org.ton.boc.BagOfCells$Companion.read(BagOfCells.kt:52)
	at org.ton.boc.BagOfCells$Companion.of(BagOfCells.kt:45)
	... 97 more

Add kotlin-map to ton-maps and back convertion support

usecase: batch mint nfts required dict of { index: Cell }

reference: tonlib ts

Our efforts with @AntonMeep (it is seems exactly like tonwhales ts logic but doesn't work unfortunatelly)

fun <T> HashMapNodeOf(it: Map<BitString, T>, n: Int): HashMapNode<T> {
    require(it.isNotEmpty())

    return if (it.size == 1) {
        HashMapNodeLeaf(it.values.first())
    } else {
        HashMapNodeFork(
            // Left has implicit '0' appended to the label
            left = HashMapEdgeOf(it.filterKeys { !it.first() }.mapKeys { it.key.drop(1).toBitString() }, n - 1),
            // Right has implicit '1' appended to the label
            right = HashMapEdgeOf(it.filterKeys { it.first() }.mapKeys { it.key.drop(1).toBitString() }, n - 1),
        )
    }
}

fun <T> HashMapEdgeOf(it: Map<BitString, T>, n: Int): HashMapEdge<T> {
    require(it.isNotEmpty())
    val keys = it.keys
    val commonPrefix = it.keys
        .reduce { acc, bitString ->
            acc.zip(bitString)
                .takeWhile { it.first == it.second }
                .map { it.first }
                .toBitString()
        }
    val label = HashMapLabel.of(commonPrefix)
    return HashMapEdge(
        label = label,
        node = HashMapNodeOf(it.mapKeys { it.key.drop(commonPrefix.size).toBitString() }, n - commonPrefix.size)
    )
}

fun pad(src: BitString, size: Int): BitString {
    var t = src
    while (t.size < size)
        t = BitString.of(false).plus(t)
    return t
}

fun <T> HashMapEOf(src: Map<BitString, T>, n: Int): HashMapE<T> = if (src.isEmpty()) {
    HashMapE.of()
} else {

    RootHashMapE(HashMapEdgeOf(src.keys.map {
        pad(it, n) to src.get(it)!!
    }.toMap(), n))

//    RootHashMapE(HashMapEdgeOf(src, n))
}

`org.ton.block.VarUInteger` addition always throws an exception

On almost any addition (say VarUInteger(1)+VarUInteger(1)) an exception is thrown from plus in line 45

https://github.com/andreypfau/ton-kotlin/blob/b1edc4b134e89ccf252149f27c85fd530377cebe/ton-kotlin-block/src/commonMain/kotlin/org/ton/block/VarUInteger.kt#L41-L47

This happens, because VarUInteger, when constructed, holds number's length in bytes:
https://github.com/andreypfau/ton-kotlin/blob/b1edc4b134e89ccf252149f27c85fd530377cebe/ton-kotlin-block/src/commonMain/kotlin/org/ton/block/VarUInteger.kt#L26-L31

Therefore, in line 45, maxLen contains number of bytes, but actualLen is clearly number of bits, and very often number of bits is larger, causing this exception. It is also related to other mathematical operators with VarUInteger
Moreover, to me it seems that bit length anyway can increase when adding two numbers of the same bit length

`ArrayIndexOutOfBoundsException: Index 36 out of bounds for length 36` while call `CellBuilder.bits.toBooleanArray()`

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 36 out of bounds for length 36
at org.ton.bitstring.ByteBackedBitString$Companion.get(ByteBackedBitString.kt:174)
at org.ton.bitstring.ByteBackedBitString.get(ByteBackedBitString.kt)
at org.ton.bitstring.ByteBackedBitString.getOrNull(ByteBackedBitString.kt:15)
at org.ton.bitstring.ByteBackedBitString.get(ByteBackedBitString.kt:12)
at org.ton.bitstring.ByteBackedBitString$BitStringIterator.next(ByteBackedBitString.kt:105)
at org.ton.bitstring.ByteBackedBitString$BitStringIterator.next(ByteBackedBitString.kt:99)
at kotlin.collections.CollectionsKt___CollectionsKt.toBooleanArray(_Collections.kt:1086)
at org.ton.bitstring.ByteBackedBitString.toBooleanArray(ByteBackedBitString.kt:31)

First depth mismatch in Testnet

!!! TESTNET !!!

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.ActiveProfiles
import org.ton.api.liteclient.config.LiteClientConfigGlobal
import org.ton.api.liteserver.LiteServerDesc
import org.ton.api.pub.PublicKeyEd25519
import org.ton.api.tonnode.TonNodeBlockId
import org.ton.api.validator.config.ValidatorConfigGlobal
import org.ton.crypto.base64
import org.ton.lite.client.LiteClient

class Test {

    @Test
    fun `get block`() {
        val liteClient = LiteClient(
                coroutineContext = Dispatchers.Default,
                liteClientConfigGlobal = LiteClientConfigGlobal(
                        liteServers = listOf(
                                LiteServerDesc(id = PublicKeyEd25519(base64("p2tSiaeSqX978BxE5zLxuTQM06WVDErf5/15QToxMYA=")), ip = 1097649206, port = 29296)
                        )
                )
        )
        runBlocking {
            liteClient.getLastBlockId().let {
                liteClient.getBlock(it)
            }
        }
    }
}

We are getting next exception and coroutine does not closing

Exception in thread "DefaultDispatcher-worker-7 @coroutine#13" java.lang.IllegalStateException: First depth mismatch in merkle update cell:
merkle depth: -64
 child depth: 32
       data: 041f689bbd35a85569a7002be10a7879e618c8681ef017d8482a81e3cd9533619395432bd79c5e7e3515aae485948c0355d91c15f7b4dbd3fb5113dafc79513ef101c001c0
	at org.ton.cell.CellImpl$Companion.of(CellImpl.kt:163)
	at org.ton.cell.Cell$Companion.of(Cell.kt:118)
	at org.ton.boc.BagOfCellsUtilsKt$createCell$2.invokeSuspend(BagOfCellsUtils.kt:338)
	(Coroutine boundary)
	at org.ton.boc.BagOfCellsUtilsKt$readBagOfCell$3$1$1.invokeSuspend(BagOfCellsUtils.kt:137)
	Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [CoroutineId(11), "coroutine#11":StandaloneCoroutine{Cancelling}@139c1847, Dispatchers.Default]
Caused by: java.lang.IllegalStateException: First depth mismatch in merkle update cell:
merkle depth: -64
 child depth: 32
       data: 041f689bbd35a85569a7002be10a7879e618c8681ef017d8482a81e3cd9533619395432bd79c5e7e3515aae485948c0355d91c15f7b4dbd3fb5113dafc79513ef101c001c0
	at org.ton.cell.CellImpl$Companion.of(CellImpl.kt:163)
	at org.ton.cell.Cell$Companion.of(Cell.kt:118)
	at org.ton.boc.BagOfCellsUtilsKt$createCell$2.invokeSuspend(BagOfCellsUtils.kt:338)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
	at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)

Generic interfaces to standard smart-contracts

At the moment, contract definitions in ton-kotlin.contract module module are nothing to write home about.

  • Lite client instance is a part of contract definition. Just a bad design decision. This needs to be fixed in a way that won't force specific paradigm, dependency injection or something else, upon the end user.
  • Contracts are bundled together by what they are (a simple wallet, wallet V2, etc) and not what they can do (use seqno, have public keys, send multiple messages at once). This makes it rather awkward to work with specific cases, for example wallet V3 supports sending up to 4 transfers at once, but because V2 doesn't, all of the wallets are dumbed down to "1 message - 1 transfer". Another example are highload wallets, especially V2, which unlike all of the other wallets does not use seqno to protect from replay attacks. This needs to be addressed.
  • To create an instance of a wallet, it requires the user to supply a private key. This is wrong, since even without knowing the private key users should be able to query for example wallets seqno. Another use case would be to compute wallets address based on known public key.
    Also, security is also important, where for example wallet application wouldn't have access to private key until the user authorizes using fingerprint scanner.
    This also applies to contracts other than wallets, for example NFT tokens, Jetton wallets, etc. In these cases there's no actual private key involved. For example, anyone can get nft item's information, but to transfer it, a wallet (or some other contract) with the address equal to items owner address is required.

This issue requires careful design of such an API, in a way where it would be extensible, yet simple and clear.

For reference, take a look at my failed attempt at this #26, where I attempted to solve it without giving much thought to it. Another example would be this package in an open source tool of mine, where I attempted to implement fluent DSL-based API for wallet v3r2. However, adapting it to all of the kinds of contracts we are dealing with wouldn't be an easy task

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.