From 4086b55adf3efbabe965a956e6ca11cd035581f8 Mon Sep 17 00:00:00 2001 From: andreypfau Date: Sat, 15 Feb 2025 07:10:01 +0900 Subject: [PATCH] 0.4.3 --- bitstring/build.gradle.kts | 1 + bitstring/src/BitString.kt | 3 + bitstring/src/ByteBackedBitString.kt | 7 +- block-tlb/src/AddrStd.kt | 8 + block-tlb/src/StorageUsedShort.kt | 38 +- block-tlb/src/account/Account.kt | 2 + block-tlb/src/config/BurningConfig.kt | 54 ++ block-tlb/src/config/StoragePrices.kt | 89 ++++ build.gradle.kts | 2 +- contract/src/wallet/WalletV4R2Contract.kt | 2 +- examples/src/ConfigContractExample.kt | 24 + examples/src/WalletV4R2Example.kt | 31 ++ .../src/contract/config/ConfigContract.kt | 155 ++++++ examples/src/provider/GlobalConfig.kt | 467 ++++++++++++++++++ examples/src/provider/LiteClient.kt | 5 + hashmap-tlb/src/DictionaryKeyCodec.kt | 13 + tvm/src/cell/Cell.kt | 6 +- 17 files changed, 885 insertions(+), 22 deletions(-) create mode 100644 block-tlb/src/config/BurningConfig.kt create mode 100644 block-tlb/src/config/StoragePrices.kt create mode 100644 examples/src/ConfigContractExample.kt create mode 100644 examples/src/WalletV4R2Example.kt create mode 100644 examples/src/contract/config/ConfigContract.kt diff --git a/bitstring/build.gradle.kts b/bitstring/build.gradle.kts index 8863cef2..9f1e5117 100644 --- a/bitstring/build.gradle.kts +++ b/bitstring/build.gradle.kts @@ -9,6 +9,7 @@ kotlin { dependencies { api(projects.tonKotlinCrypto) implementation(libs.serialization.core) + implementation(libs.kotlinx.io) } } } diff --git a/bitstring/src/BitString.kt b/bitstring/src/BitString.kt index 26697927..980c7dbb 100644 --- a/bitstring/src/BitString.kt +++ b/bitstring/src/BitString.kt @@ -2,6 +2,7 @@ package org.ton.bitstring +import kotlinx.io.bytestring.ByteString import kotlinx.serialization.Serializable import kotlin.contracts.ExperimentalContracts import kotlin.contracts.contract @@ -19,6 +20,8 @@ public inline fun BitString(hex: String): BitString = BitString.parse(hex) public inline fun Iterable.toBitString(): BitString = BitString(this) public inline fun BooleanArray.toBitString(): BitString = BitString(*this) public inline fun ByteArray.toBitString(size: Int = this.size * Byte.SIZE_BITS): BitString = BitString(this, size) +public inline fun ByteString.toBitString(size: Int = this.size * Byte.SIZE_BITS): BitString = + BitString(this.toByteArray(), size) @Serializable(with = FiftHexBitStringSerializer::class) public interface BitString : Iterable, Comparable { diff --git a/bitstring/src/ByteBackedBitString.kt b/bitstring/src/ByteBackedBitString.kt index 589828fc..c557e06e 100644 --- a/bitstring/src/ByteBackedBitString.kt +++ b/bitstring/src/ByteBackedBitString.kt @@ -45,14 +45,15 @@ public open class ByteBackedBitString protected constructor( if (augment && (size % 8 != 0)) { appendAugmentTag(bytes, size) } else { - bytes.copyOf() + bytes.copyOf((size + 7) ushr 3) } override fun toBooleanArray(): BooleanArray = toList().toBooleanArray() - override fun toMutableBitString(): MutableBitString = ByteBackedMutableBitString.of(bytes.copyOf(), size) + override fun toMutableBitString(): MutableBitString = + ByteBackedMutableBitString.of(bytes.copyOf((size + 7) ushr 3), size) - override fun toBitString(): BitString = ByteBackedBitString(size, bytes.copyOf()) + override fun toBitString(): BitString = ByteBackedBitString(size, bytes.copyOf((size + 7) ushr 3)) override fun iterator(): Iterator = BitStringIterator(this) diff --git a/block-tlb/src/AddrStd.kt b/block-tlb/src/AddrStd.kt index 2cf4b6df..04713c57 100644 --- a/block-tlb/src/AddrStd.kt +++ b/block-tlb/src/AddrStd.kt @@ -1,5 +1,6 @@ package org.ton.block +import kotlinx.io.bytestring.ByteString import kotlinx.serialization.SerialName import org.ton.bitstring.BitString import org.ton.bitstring.toBitString @@ -33,6 +34,7 @@ public data class AddrStd( public constructor() : this(0, BitString(256)) public constructor(workchainId: Int, address: BitString) : this(null, workchainId, address) public constructor(workchainId: Int, address: ByteArray) : this(null, workchainId, address) + public constructor(workchainId: Int, address: ByteString) : this(null, workchainId, address.toByteArray()) public constructor(anycast: Anycast?, workchainId: Int, address: ByteArray) : this( anycast.toMaybe(), workchainId, @@ -45,6 +47,12 @@ public data class AddrStd( address.toBitString() ) + public constructor(anycast: Anycast?, workchainId: Int, address: ByteString) : this( + anycast.toMaybe(), + workchainId, + address.toBitString() + ) + init { require(address.size == 256) { "expected address.size == 256, actual: ${address.size}" } } diff --git a/block-tlb/src/StorageUsedShort.kt b/block-tlb/src/StorageUsedShort.kt index 2ad09ce7..a1bb8f82 100644 --- a/block-tlb/src/StorageUsedShort.kt +++ b/block-tlb/src/StorageUsedShort.kt @@ -3,24 +3,34 @@ package org.ton.block import org.ton.cell.CellBuilder import org.ton.cell.CellSlice import org.ton.cell.invoke -import org.ton.tlb.* +import org.ton.kotlin.cell.CellSize import org.ton.tlb.TlbConstructor +import org.ton.tlb.loadTlb import org.ton.tlb.providers.TlbConstructorProvider +import org.ton.tlb.storeTlb +/** + * Cell tree storage stats. + */ public data class StorageUsedShort( - val cells: Long, - val bits: Long -) : TlbObject { - override fun print(printer: TlbPrettyPrinter): TlbPrettyPrinter { - return printer { - type("storage_used_short") { - field("cells", cells) - field("bits", bits) - } - } + /** + * Total number of cells in tree. + */ + val cellCount: Long, + + /** + * Total number of bits in tree. + */ + val bitCount: Long +) { + public constructor(cellSize: CellSize) : this(cellSize.minRefs.toLong(), cellSize.minBits.toLong()) { + require(cellSize.isFixed()) { "Cell size must be fixed" } } - override fun toString(): String = print().toString() + public fun toCellSize(): CellSize = CellSize(bitCount.toInt(), cellCount.toInt()) + + public operator fun plus(other: StorageUsedShort): StorageUsedShort = + StorageUsedShort(cellCount + other.cellCount, bitCount + other.bitCount) public companion object : TlbConstructorProvider by StorageUsedShortTlbConstructor { public val ZERO: StorageUsedShort = StorageUsedShort(0, 0) @@ -36,8 +46,8 @@ private object StorageUsedShortTlbConstructor : TlbConstructor override fun storeTlb( cellBuilder: CellBuilder, value: StorageUsedShort ) = cellBuilder { - storeTlb(varUInteger7Codec, VarUInteger(value.cells)) - storeTlb(varUInteger7Codec, VarUInteger(value.bits)) + storeTlb(varUInteger7Codec, VarUInteger(value.cellCount)) + storeTlb(varUInteger7Codec, VarUInteger(value.bitCount)) } override fun loadTlb( diff --git a/block-tlb/src/account/Account.kt b/block-tlb/src/account/Account.kt index 4d056ccf..d5120fd9 100644 --- a/block-tlb/src/account/Account.kt +++ b/block-tlb/src/account/Account.kt @@ -10,6 +10,8 @@ import org.ton.tlb.TlbCodec /** * Existing account data. + * + * @see [ShardAccount] */ public data class Account( /** diff --git a/block-tlb/src/config/BurningConfig.kt b/block-tlb/src/config/BurningConfig.kt new file mode 100644 index 00000000..9115ad5d --- /dev/null +++ b/block-tlb/src/config/BurningConfig.kt @@ -0,0 +1,54 @@ +@file:Suppress("PackageDirectoryMismatch") + +package org.ton.kotlin.config + +import org.ton.bigint.div +import org.ton.bigint.times +import org.ton.bigint.toBigInt +import org.ton.block.AddrStd +import org.ton.block.Coins +import org.ton.cell.CellBuilder +import org.ton.cell.CellSlice +import org.ton.kotlin.cell.CellContext +import org.ton.tlb.TlbCodec + +public data class BurningConfig( + val blackholeAddress: AddrStd?, + val feeBurnNum: Int, + val feeBurnDenom: Int +) { + init { + require(feeBurnDenom >= 1) { "feeBurnDenom must be at least 1, actual: $feeBurnDenom" } + require(feeBurnNum in 0..feeBurnDenom) { "feeBurnNum must be in 0..$feeBurnDenom, actual: $feeBurnNum" } + } + + public fun calculateBurnedFees(value: Coins): Coins { + if (value == Coins.ZERO) return value + return Coins(value.amount.value.times(feeBurnNum.toBigInt()).div(feeBurnDenom.toBigInt())) + } + + public companion object : TlbCodec by BurningConfigTlbCodec +} + +private object BurningConfigTlbCodec : TlbCodec { + override fun loadTlb(slice: CellSlice, context: CellContext): BurningConfig { + val tag = slice.loadUInt(8).toInt() + require(tag == 0x01) { "Invalid BurningConfig tag: ${tag.toHexString()}" } + val blackholeAddress = if (slice.loadBoolean()) AddrStd(-1, slice.loadByteArray(32)) else null + val feeBurnNum = slice.loadUInt(32).toInt() + val feeBurnDenom = slice.loadUInt(32).toInt() + return BurningConfig(blackholeAddress, feeBurnNum, feeBurnDenom) + } + + override fun storeTlb(builder: CellBuilder, value: BurningConfig) { + builder.storeUInt(0x01, 8) + if (value.blackholeAddress != null) { + builder.storeBoolean(true) + builder.storeBitString(value.blackholeAddress.address) + } else { + builder.storeBoolean(false) + } + builder.storeUInt(value.feeBurnNum, 32) + builder.storeUInt(value.feeBurnDenom, 32) + } +} \ No newline at end of file diff --git a/block-tlb/src/config/StoragePrices.kt b/block-tlb/src/config/StoragePrices.kt new file mode 100644 index 00000000..a9e4fb53 --- /dev/null +++ b/block-tlb/src/config/StoragePrices.kt @@ -0,0 +1,89 @@ +package org.ton.block.config + +import org.ton.bigint.* +import org.ton.block.Coins +import org.ton.block.StorageUsedShort +import org.ton.cell.CellBuilder +import org.ton.cell.CellSlice +import org.ton.kotlin.cell.CellContext +import org.ton.tlb.TlbCodec + +/** + * Storage prices for some interval. + */ +public data class StoragePrices( + /** + * Unix timestamp in seconds since which this prices are used. + */ + val validSince: Long, + + /** + * Bit price in base workchain. + */ + val bitPrice: Long, + + /** + * Cell price in base workchain. + */ + val cellPrice: Long, + + /** + * Bit price in masterchain. + */ + val mcBitPrice: Long, + + /** + * Cell price in masterchain. + */ + val mcCellPrice: Long, +) { + /** + * Computes the amount of fees for storing [stats] data for [delta] seconds. + */ + public fun computeStorageFee( + isMasterchain: Boolean, + delta: Long, + stats: StorageUsedShort + ): Coins { + var result = if (isMasterchain) { + (stats.cellCount.toBigInt().times(mcCellPrice.toBigInt())).plus( + (stats.bitCount.toBigInt().times(mcBitPrice.toBigInt())) + ) + } else { + (stats.cellCount.toBigInt().times(cellPrice.toBigInt())).plus( + (stats.bitCount.toBigInt().times(bitPrice.toBigInt())) + ) + } + result = result.times(delta.toBigInt()) + val r = result.and(0xFFFF.toBigInt()).toLong() != 0L + result = result.shr(16) + if (r) { + result = result.plus(1.toBigInt()) + } + return Coins(result) + } + + public companion object : TlbCodec by StoragePricesTlbCodec +} + +private object StoragePricesTlbCodec : TlbCodec { + override fun loadTlb(slice: CellSlice, context: CellContext): StoragePrices { + val tag = slice.loadUInt(8).toInt() + require(tag == 0xCC) { "Invalid StorageUsedShort tag: ${tag.toHexString()}" } + val validSince = slice.loadUInt(32).toLong() + val bitPrice = slice.loadULong().toLong() + val cellPrice = slice.loadULong().toLong() + val mcBitPrice = slice.loadULong().toLong() + val mcCellPrice = slice.loadULong().toLong() + return StoragePrices(validSince, bitPrice, cellPrice, mcBitPrice, mcCellPrice) + } + + override fun storeTlb(builder: CellBuilder, value: StoragePrices, context: CellContext) { + builder.storeUInt(0xCC, 8) + builder.storeUInt(value.validSince, 32) + builder.storeULong(value.bitPrice.toULong(), 64) + builder.storeULong(value.cellPrice.toULong(), 64) + builder.storeULong(value.mcBitPrice.toULong(), 64) + builder.storeULong(value.mcCellPrice.toULong(), 64) + } +} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index e2727b15..3be556e4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -10,7 +10,7 @@ plugins { allprojects { group = "org.ton" - version = "0.4.2" + version = "0.4.3" repositories { mavenCentral() diff --git a/contract/src/wallet/WalletV4R2Contract.kt b/contract/src/wallet/WalletV4R2Contract.kt index a8f0bf0b..f9175029 100644 --- a/contract/src/wallet/WalletV4R2Contract.kt +++ b/contract/src/wallet/WalletV4R2Contract.kt @@ -52,7 +52,7 @@ public class WalletV4R2Contract( walletId, privateKey.publicKey(), ) - ).value else null + ).load() else null val message = transferMessage( address = address, stateInit = stateInit, diff --git a/examples/src/ConfigContractExample.kt b/examples/src/ConfigContractExample.kt new file mode 100644 index 00000000..083a44c9 --- /dev/null +++ b/examples/src/ConfigContractExample.kt @@ -0,0 +1,24 @@ +package org.ton.kotlin.examples + +import org.ton.block.AccountActive +import org.ton.kotlin.examples.contract.config.ConfigContract +import org.ton.kotlin.examples.contract.config.ConfigData +import org.ton.kotlin.examples.provider.LiteClientProvider +import org.ton.kotlin.examples.provider.liteClientMainnet + +private val provider = LiteClientProvider(liteClientMainnet()) + +suspend fun main() { + val configContract = ConfigContract(provider) + + val account = configContract.getState()?.loadAccount() ?: error("cant load account not found") + val data = (account.state as AccountActive).value.data.value?.cell ?: error("cant load data") + val configData = ConfigData.loadTlb(data.beginParse()) + println("seqno: ${configData.seqno}") + println("public key: ${configData.publicKey}") + println("voteDict: ${configData.voteDict}") + for (entry in configData.voteDict) { + println("proposal: ${entry.key}") + println(entry.value) + } +} \ No newline at end of file diff --git a/examples/src/WalletV4R2Example.kt b/examples/src/WalletV4R2Example.kt new file mode 100644 index 00000000..4d613b61 --- /dev/null +++ b/examples/src/WalletV4R2Example.kt @@ -0,0 +1,31 @@ +package org.ton.kotlin.examples + +import org.ton.api.pk.PrivateKeyEd25519 +import org.ton.block.AddrStd +import org.ton.block.Coins +import org.ton.block.CurrencyCollection +import org.ton.contract.wallet.WalletTransferBuilder +import org.ton.contract.wallet.WalletV4R2Contract +import org.ton.kotlin.account.balance +import org.ton.kotlin.examples.faucet.TestnetFaucet +import org.ton.kotlin.examples.provider.LiteClientProvider +import org.ton.kotlin.examples.provider.liteClientTestnet +import kotlin.random.Random + +private val provider = LiteClientProvider(liteClientTestnet()) + +suspend fun main() { + val key = PrivateKeyEd25519(Random(42)) + val wallet = WalletV4R2Contract(provider.liteClient, WalletV4R2Contract.address(key)) + if (provider.getAccount(wallet.address)?.loadAccount().balance == CurrencyCollection.ZERO) { + TestnetFaucet(provider).topUpContract(wallet.address, Coins.of(1)) + } + val transfer = WalletTransferBuilder().apply { + destination = AddrStd("0QCaLIqf03-qGREcf-Novb6H3UOXmIB1cLXxAJDxqrwe3rbP") + currencyCollection = CurrencyCollection(Coins.ofNano(1)) + bounceable = false + } + + val res = wallet.transfer(key, transfer.build()) + println(res) +} \ No newline at end of file diff --git a/examples/src/contract/config/ConfigContract.kt b/examples/src/contract/config/ConfigContract.kt new file mode 100644 index 00000000..720ce483 --- /dev/null +++ b/examples/src/contract/config/ConfigContract.kt @@ -0,0 +1,155 @@ +package org.ton.kotlin.examples.contract.config + +import kotlinx.io.bytestring.ByteString +import org.ton.api.tonnode.TonNodeBlockIdExt +import org.ton.block.AddrStd +import org.ton.cell.Cell +import org.ton.cell.CellBuilder +import org.ton.cell.CellSlice +import org.ton.kotlin.account.ShardAccount +import org.ton.kotlin.cell.CellContext +import org.ton.kotlin.dict.Dictionary +import org.ton.kotlin.dict.DictionaryKeyCodec +import org.ton.kotlin.examples.provider.Provider +import org.ton.tlb.CellRef +import org.ton.tlb.TlbCodec + +class ConfigContract( + val provider: Provider +) { + @OptIn(ExperimentalStdlibApi::class) + val address = AddrStd(-1, "5555555555555555555555555555555555555555555555555555555555555555".hexToByteArray()) + + suspend fun getState(blockId: TonNodeBlockIdExt? = null): ShardAccount? { + return provider.getAccount(address, blockId) + } +} + +data class ConfigData( + val cfgDict: Cell, + val seqno: Int, + val publicKey: ByteString, + val voteDict: Dictionary, +) { + companion object : TlbCodec by ConfigDataTlbCodec +} + +private object ConfigDataTlbCodec : TlbCodec { + override fun loadTlb(slice: CellSlice, context: CellContext): ConfigData { + val cfgDict = slice.loadRef() + val seqno = slice.loadUInt(32).toInt() + val publicKey = slice.loadByteString(32) + val voteDict = Dictionary(slice.loadNullableRef(), DictionaryKeyCodec.BYTE_STRING_32, ConfigProposalStatus) + return ConfigData(cfgDict, seqno, publicKey, voteDict) + } +} + +/** + * ```tlb + * cfg_proposal_status#ce expires:uint32 proposal:^ConfigProposal is_critical:Bool + * voters:(HashmapE 16 True) remaining_weight:int64 validator_set_id:uint256 + * rounds_remaining:uint8 wins:uint8 losses:uint8 = ConfigProposalStatus; + * ``` + */ +data class ConfigProposalStatus( + val expiresAt: Long, + val proposal: CellRef, + val isCritical: Boolean, + val voters: Dictionary, + val remainingWeight: Long, + val validatorSetId: ByteString, + val roundsRemaining: Int, + val wins: Int, + val losses: Int +) { + companion object : TlbCodec by ConfigProposalStatusTlbCodec +} + +private object ConfigProposalStatusTlbCodec : TlbCodec { + val key = DictionaryKeyCodec.int(16) + val trueValue = object : TlbCodec { + override fun loadTlb(slice: CellSlice, context: CellContext) = Unit + override fun storeTlb(builder: CellBuilder, value: Unit, context: CellContext) { + } + } + + override fun storeTlb(builder: CellBuilder, value: ConfigProposalStatus, context: CellContext) { + builder.storeUInt(0xCE, 8) + builder.storeUInt(value.expiresAt, 32) + builder.storeRef(value.proposal.cell) + builder.storeBoolean(value.isCritical) + builder.storeNullableRef(value.voters.cell) + builder.storeLong(value.remainingWeight, 64) + builder.storeByteString(value.validatorSetId) + builder.storeUInt(value.remainingWeight, 8) + builder.storeUInt(value.wins, 8) + builder.storeUInt(value.losses, 8) + } + + @OptIn(ExperimentalStdlibApi::class) + override fun loadTlb(slice: CellSlice, context: CellContext): ConfigProposalStatus { + val tag = slice.loadUInt(8).toInt() + require(tag == 0xCE) { "Invalid ConfigProposalStatus tag: ${tag.toHexString()}" } + val expiresAt = slice.loadUInt(32).toLong() + val proposal = CellRef(slice.loadRef(), ConfigProposal) + val isCritical = slice.loadBoolean() + val voters = Dictionary(slice.loadNullableRef(), key, trueValue) + val remainingWeight = slice.loadLong(64) + val validatorSetId = slice.loadByteString(32) + val roundsRemaining = slice.loadUInt(8).toInt() + val wins = slice.loadUInt(8).toInt() + val losses = slice.loadUInt(8).toInt() + return ConfigProposalStatus( + expiresAt, + proposal, + isCritical, + voters, + remainingWeight, + validatorSetId, + roundsRemaining, + wins, + losses + ) + } +} + +/** + * ```tlb + * cfg_proposal#f3 param_id:int32 param_value:(Maybe ^Cell) if_hash_equal:(Maybe uint256) + * ``` + */ +data class ConfigProposal( + val paramId: Int, + val paramValue: Cell?, + val ifHashEqual: ByteString?, +) { + companion object : TlbCodec by ConfigProposalTlbCodec +} + +private object ConfigProposalTlbCodec : TlbCodec { + @OptIn(ExperimentalStdlibApi::class) + override fun loadTlb(slice: CellSlice, context: CellContext): ConfigProposal { + val tag = slice.loadUInt(8).toInt() + require(tag == 0xF3) { "Invalid ConfigProposal tag: ${tag.toHexString()}" } + val paramId = slice.loadInt(32).toInt() + val paramValue = slice.loadNullableRef() + val ifHashEqual = if (slice.loadBoolean()) { + slice.loadByteString(32) + } else { + null + } + return ConfigProposal(paramId, paramValue, ifHashEqual) + } + + override fun storeTlb(builder: CellBuilder, value: ConfigProposal, context: CellContext) { + builder.storeUInt(0xf3, 8) + builder.storeInt(value.paramId, 32) + builder.storeNullableRef(value.paramValue) + if (value.ifHashEqual != null) { + builder.storeBoolean(true) + builder.storeByteString(value.ifHashEqual) + } else { + builder.storeBoolean(false) + } + } +} \ No newline at end of file diff --git a/examples/src/provider/GlobalConfig.kt b/examples/src/provider/GlobalConfig.kt index fc142d30..31ec5b83 100644 --- a/examples/src/provider/GlobalConfig.kt +++ b/examples/src/provider/GlobalConfig.kt @@ -332,8 +332,475 @@ val TESTNET_GLOBAL_CONFIG_JSON = """ } """.trimIndent() +val MAINNET_GLOBAL_CONFIG_JSON = """ +{ + "@type": "config.global", + "dht": { + "@type": "dht.config.global", + "k": 6, + "a": 3, + "static_nodes": { + "@type": "dht.nodes", + "nodes": [ + { + "@type": "dht.node", + "id": { + "@type": "pub.ed25519", + "key": "6PGkPQSbyFp12esf1NqmDOaLoFA8i9+Mp5+cAx5wtTU=" + }, + "addr_list": { + "@type": "adnl.addressList", + "addrs": [ + { + "@type": "adnl.address.udp", + "ip": -1185526007, + "port": 22096 + } + ], + "version": 0, + "reinit_date": 0, + "priority": 0, + "expire_at": 0 + }, + "version": -1, + "signature": "L4N1+dzXLlkmT5iPnvsmsixzXU0L6kPKApqMdcrGP5d9ssMhn69SzHFK+yIzvG6zQ9oRb4TnqPBaKShjjj2OBg==" + }, + { + "@type": "dht.node", + "id": { + "@type": "pub.ed25519", + "key": "4R0C/zU56k+x2HGMsLWjX2rP/SpoTPIHSSAmidGlsb8=" + }, + "addr_list": { + "@type": "adnl.addressList", + "addrs": [ + { + "@type": "adnl.address.udp", + "ip": -1952265919, + "port": 14395 + } + ], + "version": 0, + "reinit_date": 0, + "priority": 0, + "expire_at": 0 + }, + "version": -1, + "signature": "0uwWyCFn2KjPnnlbSFYXLZdwIakaSgI9WyRo87J3iCGwb5TvJSztgA224A9kNAXeutOrXMIPYv1b8Zt8ImsrCg==" + }, + { + "@type": "dht.node", + "id": { + "@type": "pub.ed25519", + "key": "/YDNd+IwRUgL0mq21oC0L3RxrS8gTu0nciSPUrhqR78=" + }, + "addr_list": { + "@type": "adnl.addressList", + "addrs": [ + { + "@type": "adnl.address.udp", + "ip": -1402455171, + "port": 14432 + } + ], + "version": 0, + "reinit_date": 0, + "priority": 0, + "expire_at": 0 + }, + "version": -1, + "signature": "6+oVk6HDtIFbwYi9khCc8B+fTFceBUo1PWZDVTkb4l84tscvr5QpzAkdK7sS5xGzxM7V7YYQ6gUQPrsP9xcLAw==" + }, + { + "@type": "dht.node", + "id": { + "@type": "pub.ed25519", + "key": "DA0H568bb+LoO2LGY80PgPee59jTPCqqSJJzt1SH+KE=" + }, + "addr_list": { + "@type": "adnl.addressList", + "addrs": [ + { + "@type": "adnl.address.udp", + "ip": -1402397332, + "port": 14583 + } + ], + "version": 0, + "reinit_date": 0, + "priority": 0, + "expire_at": 0 + }, + "version": -1, + "signature": "cL79gDTrixhaM9AlkCdZWccCts7ieQYQBmPxb/R7d7zHw3bEHL8Le96CFJoB1KHu8C85iDpFK8qlrGl1Yt/ZDg==" + }, + { + "@type": "dht.node", + "id": { + "@type": "pub.ed25519", + "key": "MJr8xja0xpu9DoisFXBrkNHNx1XozR7HHw9fJdSyEdo=" + }, + "addr_list": { + "@type": "adnl.addressList", + "addrs": [ + { + "@type": "adnl.address.udp", + "ip": -2018147130, + "port": 6302 + } + ], + "version": 0, + "reinit_date": 0, + "priority": 0, + "expire_at": 0 + }, + "version": -1, + "signature": "XcR5JaWcf4QMdI8urLSc1zwv5+9nCuItSE1EDa0dSwYF15R/BtJoKU5YHA4/T8SiO18aVPQk2SL1pbhevuMrAQ==" + }, + { + "@type": "dht.node", + "id": { + "@type": "pub.ed25519", + "key": "Fhldu4zlnb20/TUj9TXElZkiEmbndIiE/DXrbGKu+0c=" + }, + "addr_list": { + "@type": "adnl.addressList", + "addrs": [ + { + "@type": "adnl.address.udp", + "ip": -2018147075, + "port": 6302 + } + ], + "version": 0, + "reinit_date": 0, + "priority": 0, + "expire_at": 0 + }, + "version": -1, + "signature": "nUGB77UAkd2+ZAL5PgInb3TvtuLLXJEJ2icjAUKLv4qIGB3c/O9k/v0NKwSzhsMP0ljeTGbcIoMDw24qf3goCg==" + }, + { + "@type": "dht.node", + "id": { + "@type": "pub.ed25519", + "key": "gzUNJnBJhdpooYCE8juKZo2y4tYDIQfoCvFm0yBr7y0=" + }, + "addr_list": { + "@type": "adnl.addressList", + "addrs": [ + { + "@type": "adnl.address.udp", + "ip": 89013260, + "port": 54390 + } + ], + "version": 0, + "reinit_date": 0, + "priority": 0, + "expire_at": 0 + }, + "version": -1, + "signature": "LCrCkjmkMn6AZHW2I+oRm1gHK7CyBPfcb6LwsltskCPpNECyBl1GxZTX45n0xZtLgyBd/bOqMPBfawpQwWt1BA==" + }, + { + "@type": "dht.node", + "id": { + "@type": "pub.ed25519", + "key": "jXiLaOQz1HPayilWgBWhV9xJhUIqfU95t+KFKQPIpXg=" + }, + "addr_list": { + "@type": "adnl.addressList", + "addrs": [ + { + "@type": "adnl.address.udp", + "ip": 94452896, + "port": 12485 + } + ], + "version": 0, + "reinit_date": 0, + "priority": 0, + "expire_at": 0 + }, + "version": -1, + "signature": "fKSZh9nXMx+YblkQXn3I/bndTD0JZ1yAtK/tXPIGruNglpe9sWMXR+8fy3YogPhLJMdjNiMom1ya+tWG7qvBAQ==" + }, + { + "@type": "dht.node", + "id": { + "@type": "pub.ed25519", + "key": "vhFPq+tgjJi+4ZbEOHBo4qjpqhBdSCzNZBdgXyj3NK8=" + }, + "addr_list": { + "@type": "adnl.addressList", + "addrs": [ + { + "@type": "adnl.address.udp", + "ip": 85383775, + "port": 36752 + } + ], + "version": 0, + "reinit_date": 0, + "priority": 0, + "expire_at": 0 + }, + "version": -1, + "signature": "kBwAIgJVkz8AIOGoZcZcXWgNmWq8MSBWB2VhS8Pd+f9LLPIeeFxlDTtwAe8Kj7NkHDSDC+bPXLGQZvPv0+wHCg==" + }, + { + "@type": "dht.node", + "id": { + "@type": "pub.ed25519", + "key": "sbsuMcdyYFSRQ0sG86/n+ZQ5FX3zOWm1aCVuHwXdgs0=" + }, + "addr_list": { + "@type": "adnl.addressList", + "addrs": [ + { + "@type": "adnl.address.udp", + "ip": 759132846, + "port": 50187 + } + ], + "version": 0, + "reinit_date": 0, + "priority": 0, + "expire_at": 0 + }, + "version": -1, + "signature": "9FJwbFw3IECRFkb9bA54YaexjDmlNBArimWkh+BvW88mjm3K2i5V2uaBPS3GubvXWOwdHLE2lzQBobgZRGMyCg==" + }, + { + "@type": "dht.node", + "id": { + "@type": "pub.ed25519", + "key": "aeMgdMdkkbkfAS4+n4BEGgtqhkf2/zXrVWWECOJ/h3A=" + }, + "addr_list": { + "@type": "adnl.addressList", + "addrs": [ + { + "@type": "adnl.address.udp", + "ip": -1481887565, + "port": 25975 + } + ], + "version": 0, + "reinit_date": 0, + "priority": 0, + "expire_at": 0 + }, + "version": -1, + "signature": "z5ogivZWpQchkS4UR4wB7i2pfOpMwX9Nd/USxinL9LvJPa+/Aw3F1AytR9FX0BqDftxIYvblBYAB5JyAmlj+AA==" + }, + { + "@type": "dht.node", + "id": { + "@type": "pub.ed25519", + "key": "rNzhnAlmtRn9rTzW6o2568S6bbOXly7ddO1olDws5wM=" + }, + "addr_list": { + "@type": "adnl.addressList", + "addrs": [ + { + "@type": "adnl.address.udp", + "ip": -2134428422, + "port": 45943 + } + ], + "version": 0, + "reinit_date": 0, + "priority": 0, + "expire_at": 0 + }, + "version": -1, + "signature": "sn/+ZfkfCSw2bHnEnv04AXX/Goyw7+StHBPQOdPr+wvdbaJ761D7hyiMNdQGbuZv2Ep2cXJpiwylnZItrwdUDg==" + } + ] + } + }, + "liteservers": [ + { + "ip": 84478511, + "port": 19949, + "id": { + "@type": "pub.ed25519", + "key": "n4VDnSCUuSpjnCyUk9e3QOOd6o0ItSWYbTnW3Wnn8wk=" + } + }, + { + "ip": 84478479, + "port": 48014, + "id": { + "@type": "pub.ed25519", + "key": "3XO67K/qi+gu3T9v8G2hx1yNmWZhccL3O7SoosFo8G0=" + } + }, + { + "ip": -2018135749, + "port": 53312, + "id": { + "@type": "pub.ed25519", + "key": "aF91CuUHuuOv9rm2W5+O/4h38M3sRm40DtSdRxQhmtQ=" + } + }, + { + "ip": -2018145068, + "port": 13206, + "id": { + "@type": "pub.ed25519", + "key": "K0t3+IWLOXHYMvMcrGZDPs+pn58a17LFbnXoQkKc2xw=" + } + }, + { + "ip": -2018145059, + "port": 46995, + "id": { + "@type": "pub.ed25519", + "key": "wQE0MVhXNWUXpWiW5Bk8cAirIh5NNG3cZM1/fSVKIts=" + } + }, + { + "ip": 1091931625, + "port": 30131, + "id": { + "@type": "pub.ed25519", + "key": "wrQaeIFispPfHndEBc0s0fx7GSp8UFFvebnytQQfc6A=" + } + }, + { + "ip": 1091931590, + "port": 47160, + "id": { + "@type": "pub.ed25519", + "key": "vOe1Xqt/1AQ2Z56Pr+1Rnw+f0NmAA7rNCZFIHeChB7o=" + } + }, + { + "ip": 1091931623, + "port": 17728, + "id": { + "@type": "pub.ed25519", + "key": "BYSVpL7aPk0kU5CtlsIae/8mf2B/NrBi7DKmepcjX6Q=" + } + }, + { + "ip": 1091931589, + "port": 13570, + "id": { + "@type": "pub.ed25519", + "key": "iVQH71cymoNgnrhOT35tl/Y7k86X5iVuu5Vf68KmifQ=" + } + }, + { + "ip": -1539021362, + "port": 52995, + "id": { + "@type": "pub.ed25519", + "key": "QnGFe9kihW+TKacEvvxFWqVXeRxCB6ChjjhNTrL7+/k=" + } + }, + { + "ip": -1539021936, + "port": 20334, + "id": { + "@type": "pub.ed25519", + "key": "gyLh12v4hBRtyBygvvbbO2HqEtgl+ojpeRJKt4gkMq0=" + } + }, + { + "ip": -1136338705, + "port": 19925, + "id": { + "@type": "pub.ed25519", + "key": "ucho5bEkufbKN1JR1BGHpkObq602whJn3Q3UwhtgSo4=" + } + }, + { + "ip": 868465979, + "port": 19434, + "id": { + "@type": "pub.ed25519", + "key": "J5CwYXuCZWVPgiFPW+NY2roBwDWpRRtANHSTYTRSVtI=" + } + }, + { + "ip": 868466060, + "port": 23067, + "id": { + "@type": "pub.ed25519", + "key": "vX8d0i31zB0prVuZK8fBkt37WnEpuEHrb7PElk4FJ1o=" + } + }, + { + "ip": -2018147130, + "port": 53560, + "id": { + "@type": "pub.ed25519", + "key": "NlYhh/xf4uQpE+7EzgorPHqIaqildznrpajJTRRH2HU=" + } + }, + { + "ip": -2018147075, + "port": 46529, + "id": { + "@type": "pub.ed25519", + "key": "jLO6yoooqUQqg4/1QXflpv2qGCoXmzZCR+bOsYJ2hxw=" + } + }, + { + "ip": 908566172, + "port": 51565, + "id": { + "@type": "pub.ed25519", + "key": "TDg+ILLlRugRB4Kpg3wXjPcoc+d+Eeb7kuVe16CS9z8=" + } + }, + { + "ip": -1185526007, + "port": 4701, + "id": { + "@type": "pub.ed25519", + "key": "G6cNAr6wXBBByWDzddEWP5xMFsAcp6y13fXA8Q7EJlM=" + } + } + ], + "validator": { + "@type": "validator.config.global", + "zero_state": { + "workchain": -1, + "shard": -9223372036854775808, + "seqno": 0, + "root_hash": "F6OpKZKqvqeFp6CQmFomXNMfMj2EnaUSOXN+Mh+wVWk=", + "file_hash": "XplPz01CXAps5qeSWUtxcyBfdAo5zVb1N979KLSKD24=" + }, + "init_block": { + "root_hash": "VpWyfNOLm8Rqt6CZZ9dZGqJRO3NyrlHHYN1k1oLbJ6g=", + "seqno": 34835953, + "file_hash": "8o12KX54BtJM8RERD1J97Qe1ZWk61LIIyXydlBnixK8=", + "workchain": -1, + "shard": -9223372036854775808 + }, + "hardforks": [ + { + "file_hash": "t/9VBPODF7Zdh4nsnA49dprO69nQNMqYL+zk5bCjV/8=", + "seqno": 8536841, + "root_hash": "08Kpc9XxrMKC6BF/FeNHPS3MEL1/Vi/fQU/C9ELUrkc=", + "workchain": -1, + "shard": -9223372036854775808 + } + ] + } +} +""".trimIndent() + private val JSON = Json { ignoreUnknownKeys = true } val TESTNET_GLOBAL_CONFIG: LiteClientConfigGlobal = JSON.decodeFromString(TESTNET_GLOBAL_CONFIG_JSON) + +val MAINNET_GLOBAL_CONFIG: LiteClientConfigGlobal = + JSON.decodeFromString(MAINNET_GLOBAL_CONFIG_JSON) \ No newline at end of file diff --git a/examples/src/provider/LiteClient.kt b/examples/src/provider/LiteClient.kt index 3205257c..edfc7354 100644 --- a/examples/src/provider/LiteClient.kt +++ b/examples/src/provider/LiteClient.kt @@ -7,3 +7,8 @@ fun liteClientTestnet() = LiteClient( liteClientConfigGlobal = TESTNET_GLOBAL_CONFIG, coroutineContext = Dispatchers.Default ) + +fun liteClientMainnet() = LiteClient( + liteClientConfigGlobal = MAINNET_GLOBAL_CONFIG, + coroutineContext = Dispatchers.Default +) \ No newline at end of file diff --git a/hashmap-tlb/src/DictionaryKeyCodec.kt b/hashmap-tlb/src/DictionaryKeyCodec.kt index a537bb37..ff1461ec 100644 --- a/hashmap-tlb/src/DictionaryKeyCodec.kt +++ b/hashmap-tlb/src/DictionaryKeyCodec.kt @@ -2,6 +2,7 @@ package org.ton.kotlin.dict +import kotlinx.io.bytestring.ByteString import org.ton.bitstring.BitString import org.ton.cell.CellSlice import org.ton.cell.buildCell @@ -24,6 +25,18 @@ public interface DictionaryKeyCodec : DictionaryKeyLoader, DictionaryKeySt } } + public val BYTE_STRING_32: DictionaryKeyCodec = object : DictionaryKeyCodec { + override val keySize: Int get() = 256 + + override fun decodeKey(value: BitString): ByteString { + return ByteString(*value.toByteArray()) + } + + override fun encodeKey(value: ByteString): BitString { + return BitString(value.toByteArray()) + } + } + public fun long(keySize: Int = Long.SIZE_BITS): DictionaryKeyCodec = object : DictionaryKeyCodec { override val keySize: Int = keySize diff --git a/tvm/src/cell/Cell.kt b/tvm/src/cell/Cell.kt index c47e81b8..7c5d5754 100644 --- a/tvm/src/cell/Cell.kt +++ b/tvm/src/cell/Cell.kt @@ -83,11 +83,11 @@ public interface Cell { appendable.append(' ') } appendable.append(cell.bits.toString()) - appendable.append(", hash:") - appendable.append(cell.hash().toHexString()) +// appendable.append(", hash:") +// appendable.append(cell.hash().toHexString()) cell.refs.forEach { reference -> appendable.append('\n') - toString(reference, appendable, "$indent ") + toString(reference, appendable, "$indent ") } }