diff --git a/README.md b/README.md index d8c589f..3d419ce 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Swift Client for Free Ton SDK [![SPM](https://img.shields.io/badge/swift-package%20manager-green)](https://swift.org/package-manager/) -[![SPM](https://img.shields.io/badge/SDK%20VERSION-1.23.0-orange)](https://github.com/tonlabs/TON-SDK) +[![SPM](https://img.shields.io/badge/SDK%20VERSION-1.25.0-orange)](https://github.com/tonlabs/TON-SDK) Swift is a strongly typed language that has long been used not only for iOS development. Apple is actively promoting it to new platforms and today it can be used for almost any task. Thanks to this, this implementation provides the work of TonSDK on many platforms at once, including the native one for mobile phones. Let me remind you that swift can also be built for android. diff --git a/Sources/TonClientSwift/Abi/Abi.swift b/Sources/TonClientSwift/Abi/Abi.swift index f91638e..782095c 100644 --- a/Sources/TonClientSwift/Abi/Abi.swift +++ b/Sources/TonClientSwift/Abi/Abi.swift @@ -122,11 +122,11 @@ public final class TSDKAbiModule { /// Decodes account data using provided data BOC and ABI. /// Note: this feature requires ABI 2.1 or higher. - public func decode_account_data(_ payload: TSDKParamsOfDecodeAccountData, _ handler: @escaping (TSDKBindingResponse) throws -> Void + public func decode_account_data(_ payload: TSDKParamsOfDecodeAccountData, _ handler: @escaping (TSDKBindingResponse) throws -> Void ) { let method: String = "decode_account_data" binding.requestLibraryAsync(methodName(module, method), payload) { (requestId, params, responseType, finished) in - var response: TSDKBindingResponse = .init() + var response: TSDKBindingResponse = .init() response.update(requestId, params, responseType, finished) try handler(response) } @@ -154,4 +154,20 @@ public final class TSDKAbiModule { } } + /// Decodes BOC into JSON as a set of provided parameters. + /// Solidity functions use ABI types for [builder encoding](https://github.com/tonlabs/TON-Solidity-Compiler/blob/master/API.md#tvmbuilderstore). + /// The simplest way to decode such a BOC is to use ABI decoding. + /// ABI has it own rules for fields layout in cells so manually encodedBOC can not be described in terms of ABI rules. + /// To solve this problem we introduce a new ABI type `Ref()`which allows to store `ParamType` ABI parameter in cell reference and, thus,decode manually encoded BOCs. This type is available only in `decode_boc` functionand will not be available in ABI messages encoding until it is included into some ABI revision. + /// Such BOC descriptions covers most users needs. If someone wants to decode some BOC whichcan not be described by these rules (i.e. BOC with TLB containing constructors of flagsdefining some parsing conditions) then they can decode the fields up to fork condition,check the parsed data manually, expand the parsing schema and then decode the whole BOCwith the full schema. + public func decode_boc(_ payload: TSDKParamsOfDecodeBoc, _ handler: @escaping (TSDKBindingResponse) throws -> Void + ) { + let method: String = "decode_boc" + binding.requestLibraryAsync(methodName(module, method), payload) { (requestId, params, responseType, finished) in + var response: TSDKBindingResponse = .init() + response.update(requestId, params, responseType, finished) + try handler(response) + } + } + } diff --git a/Sources/TonClientSwift/Abi/AbiTypes.swift b/Sources/TonClientSwift/Abi/AbiTypes.swift index 0c0edfd..e305847 100644 --- a/Sources/TonClientSwift/Abi/AbiTypes.swift +++ b/Sources/TonClientSwift/Abi/AbiTypes.swift @@ -546,7 +546,7 @@ public struct TSDKParamsOfDecodeAccountData: Codable { } } -public struct TSDKResultOfDecodeData: Codable { +public struct TSDKResultOfDecodeAccountData: Codable { /// Decoded data as a JSON structure. public var data: AnyValue @@ -612,3 +612,26 @@ public struct TSDKResultOfDecodeInitialData: Codable { } } +public struct TSDKParamsOfDecodeBoc: Codable { + /// Parameters to decode from BOC + public var params: [TSDKAbiParam] + /// Data BOC or BOC handle + public var boc: String + public var allow_partial: Bool + + public init(params: [TSDKAbiParam], boc: String, allow_partial: Bool) { + self.params = params + self.boc = boc + self.allow_partial = allow_partial + } +} + +public struct TSDKResultOfDecodeBoc: Codable { + /// Decoded data as a JSON structure. + public var data: AnyValue + + public init(data: AnyValue) { + self.data = data + } +} + diff --git a/Sources/TonClientSwift/Boc/Boc.swift b/Sources/TonClientSwift/Boc/Boc.swift index 175e4c7..cd763ff 100644 --- a/Sources/TonClientSwift/Boc/Boc.swift +++ b/Sources/TonClientSwift/Boc/Boc.swift @@ -89,6 +89,17 @@ public final class TSDKBocModule { } } + /// Calculates BOC depth + public func get_boc_depth(_ payload: TSDKParamsOfGetBocDepth, _ handler: @escaping (TSDKBindingResponse) throws -> Void + ) { + let method: String = "get_boc_depth" + binding.requestLibraryAsync(methodName(module, method), payload) { (requestId, params, responseType, finished) in + var response: TSDKBindingResponse = .init() + response.update(requestId, params, responseType, finished) + try handler(response) + } + } + /// Extracts code from TVC contract image public func get_code_from_tvc(_ payload: TSDKParamsOfGetCodeFromTvc, _ handler: @escaping (TSDKBindingResponse) throws -> Void ) { diff --git a/Sources/TonClientSwift/Boc/BocTypes.swift b/Sources/TonClientSwift/Boc/BocTypes.swift index 1d524ca..2317b2b 100644 --- a/Sources/TonClientSwift/Boc/BocTypes.swift +++ b/Sources/TonClientSwift/Boc/BocTypes.swift @@ -82,7 +82,7 @@ public struct TSDKResultOfGetBlockchainConfig: Codable { } public struct TSDKParamsOfGetBocHash: Codable { - /// BOC encoded as base64 + /// BOC encoded as base64 or BOC handle public var boc: String public init(boc: String) { @@ -99,6 +99,24 @@ public struct TSDKResultOfGetBocHash: Codable { } } +public struct TSDKParamsOfGetBocDepth: Codable { + /// BOC encoded as base64 or BOC handle + public var boc: String + + public init(boc: String) { + self.boc = boc + } +} + +public struct TSDKResultOfGetBocDepth: Codable { + /// BOC root cell depth + public var depth: UInt32 + + public init(depth: UInt32) { + self.depth = depth + } +} + public struct TSDKParamsOfGetCodeFromTvc: Codable { /// Contract TVC image or image BOC handle public var tvc: String @@ -277,8 +295,16 @@ public struct TSDKParamsOfDecodeTvc: Codable { public struct TSDKResultOfDecodeTvc: Codable { /// Contract code BOC encoded as base64 or BOC handle public var code: String? + /// Contract code hash + public var code_hash: String? + /// Contract code depth + public var code_depth: UInt32? /// Contract data BOC encoded as base64 or BOC handle public var data: String? + /// Contract data hash + public var data_hash: String? + /// Contract data depth + public var data_depth: UInt32? /// Contract library BOC encoded as base64 or BOC handle public var library: String? /// `special.tick` field. @@ -289,14 +315,21 @@ public struct TSDKResultOfDecodeTvc: Codable { public var tock: Bool? /// Is present and non-zero only in instances of large smart contracts public var split_depth: UInt32? + /// Compiler version, for example 'sol 0.49.0' + public var compiler_version: String? - public init(code: String? = nil, data: String? = nil, library: String? = nil, tick: Bool? = nil, tock: Bool? = nil, split_depth: UInt32? = nil) { + public init(code: String? = nil, code_hash: String? = nil, code_depth: UInt32? = nil, data: String? = nil, data_hash: String? = nil, data_depth: UInt32? = nil, library: String? = nil, tick: Bool? = nil, tock: Bool? = nil, split_depth: UInt32? = nil, compiler_version: String? = nil) { self.code = code + self.code_hash = code_hash + self.code_depth = code_depth self.data = data + self.data_hash = data_hash + self.data_depth = data_depth self.library = library self.tick = tick self.tock = tock self.split_depth = split_depth + self.compiler_version = compiler_version } } diff --git a/Sources/TonClientSwift/Client/Client.swift b/Sources/TonClientSwift/Client/Client.swift index 317383e..b23fd04 100644 --- a/Sources/TonClientSwift/Client/Client.swift +++ b/Sources/TonClientSwift/Client/Client.swift @@ -11,6 +11,7 @@ public final class TSDKClientModule { public var tvm: TSDKTvmModule public var net: TSDKNetModule public var debot: TSDKDebotModule + public var proofs: TSDKProofsModule public init(config: TSDKClientConfig) throws { self.config = config @@ -23,6 +24,7 @@ public final class TSDKClientModule { self.tvm = TSDKTvmModule(binding: binding) self.net = TSDKNetModule(binding: binding) self.debot = TSDKDebotModule(binding: binding) + self.proofs = TSDKProofsModule(binding: binding) } /// Returns Core Library API reference diff --git a/Sources/TonClientSwift/Client/ClientTypes.swift b/Sources/TonClientSwift/Client/ClientTypes.swift index d35f6fd..36f1d72 100644 --- a/Sources/TonClientSwift/Client/ClientTypes.swift +++ b/Sources/TonClientSwift/Client/ClientTypes.swift @@ -33,6 +33,7 @@ public enum TSDKClientErrorCode: Int, Codable { case CanNotParseNumber = 32 case InternalError = 33 case InvalidHandle = 34 + case LocalStorageError = 35 } public enum TSDKAppRequestResultEnumTypes: String, Codable { @@ -57,12 +58,17 @@ public struct TSDKClientConfig: Codable { public var crypto: TSDKCryptoConfig? public var abi: TSDKAbiConfig? public var boc: TSDKBocConfig? + public var proofs: TSDKProofsConfig? + /// For file based storage is a folder name where SDK will store its data. For browser based is a browser async storage key prefix. Default (recommended) value is "~/.tonclient" for native environments and ".tonclient" for web-browser. + public var local_storage_path: String? - public init(network: TSDKNetworkConfig? = nil, crypto: TSDKCryptoConfig? = nil, abi: TSDKAbiConfig? = nil, boc: TSDKBocConfig? = nil) { + public init(network: TSDKNetworkConfig? = nil, crypto: TSDKCryptoConfig? = nil, abi: TSDKAbiConfig? = nil, boc: TSDKBocConfig? = nil, proofs: TSDKProofsConfig? = nil, local_storage_path: String? = nil) { self.network = network self.crypto = crypto self.abi = abi self.boc = boc + self.proofs = proofs + self.local_storage_path = local_storage_path } } @@ -101,7 +107,7 @@ public struct TSDKNetworkConfig: Codable { /// If the latency (time-lag) is less then `NetworkConfig.max_latency`then library selects another endpoint. /// Must be specified in milliseconds. Default is 60000 (1 min). public var latency_detection_interval: UInt32? - /// Maximum value for the endpoint's blockchain data syncronization latency (time-lag). Library periodically checks the current endpoint for blockchain data syncronization latency. If the latency (time-lag) is less then `NetworkConfig.max_latency` then library selects another endpoint. + /// Maximum value for the endpoint's blockchain data syncronization latency (time-lag). Library periodically checks the current endpoint for blockchain data synchronization latency. If the latency (time-lag) is less then `NetworkConfig.max_latency` then library selects another endpoint. /// Must be specified in milliseconds. Default is 60000 (1 min). public var max_latency: UInt32? /// Default timeout for http requests. @@ -170,6 +176,16 @@ public struct TSDKBocConfig: Codable { } } +public struct TSDKProofsConfig: Codable { + /// Cache proofs in the local storage. + /// Default is `true`. If this value is set to `true`, downloaded proofs and master-chain BOCs are saved into thepersistent local storage (e.g. file system for native environments or browser's IndexedDBfor the web); otherwise all the data is cached only in memory in current client's contextand will be lost after destruction of the client. + public var cache_in_local_storage: Bool? + + public init(cache_in_local_storage: Bool? = nil) { + self.cache_in_local_storage = cache_in_local_storage + } +} + public struct TSDKBuildInfoDependency: Codable { /// Dependency name. /// Usually it is a crate name. diff --git a/Sources/TonClientSwift/Proofs/Proofs.swift b/Sources/TonClientSwift/Proofs/Proofs.swift new file mode 100644 index 0000000..50d3422 --- /dev/null +++ b/Sources/TonClientSwift/Proofs/Proofs.swift @@ -0,0 +1,54 @@ +public final class TSDKProofsModule { + + private var binding: TSDKBindingModule + public let module: String = "proofs" + + public init(binding: TSDKBindingModule) { + self.binding = binding + } + + /// Proves that a given block's data, which is queried from TONOS API, can be trusted. + /// This function checks block proofs and compares given data with the proven. + /// If the given data differs from the proven, the exception will be thrown. + /// The input param is a single block's JSON object, which was queried from DApp server usingfunctions such as `net.query`, `net.query_collection` or `net.wait_for_collection`. + /// If block's BOC is not provided in the JSON, it will be queried from DApp server(in this case it is required to provide at least `id` of block). + /// Please note, that joins (like `signatures` in `Block`) are separated entities and not supported,so function will throw an exception in a case if JSON being checked has such entities in it. + /// If `cache_in_local_storage` in config is set to `true` (default), downloaded proofs andmaster-chain BOCs are saved into the persistent local storage (e.g. file system for nativeenvironments or browser's IndexedDB for the web); otherwise all the data is cached only inmemory in current client's context and will be lost after destruction of the client. + /// **Why Proofs are needed**Proofs are needed to ensure that the data downloaded from a DApp server is real blockchaindata. Checking proofs can protect from the malicious DApp server which can potentially providefake data, or also from "Man in the Middle" attacks class. + /// **What Proofs are**Simply, proof is a list of signatures of validators', which have signed this particular master-block. + /// The very first validator set's public keys are included in the zero-state. Whe know a root hashof the zero-state, because it is stored in the network configuration file, it is our authorityroot. For proving zero-state it is enough to calculate and compare its root hash. + /// In each new validator cycle the validator set is changed. The new one is stored in a key-block,which is signed by the validator set, which we already trust, the next validator set will bestored to the new key-block and signed by the current validator set, and so on. + /// In order to prove any block in the master-chain we need to check, that it has been signed bya trusted validator set. So we need to check all key-blocks' proofs, started from the zero-stateand until the block, which we want to prove. But it can take a lot of time and traffic todownload and prove all key-blocks on a client. For solving this, special trusted blocks are usedin TON-SDK. + /// The trusted block is the authority root, as well, as the zero-state. Each trusted block is the`id` (e.g. `root_hash`) of the already proven key-block. There can be plenty of trustedblocks, so there can be a lot of authority roots. The hashes of trusted blocks for MainNetand DevNet are hardcoded in SDK in a separated binary file (trusted_key_blocks.bin) and canbe updated for each release. + /// In future SDK releases, one will also be able to provide their hashes of trusted blocks forother networks, besides for MainNet and DevNet. + /// By using trusted key-blocks, in order to prove any block, we can prove chain of key-blocks tothe closest previous trusted key-block, not only to the zero-state. + /// But shard-blocks don't have proofs on DApp server. In this case, in order to prove any shard-block data, we search for a corresponding master-block, which contains the root hash of thisshard-block, or some shard block which is linked to that block in shard-chain. After provingthis master-block, we traverse through each link and calculate and compare hashes with links,one-by-one. After that we can ensure that this shard-block has also been proven. + public func proof_block_data(_ payload: TSDKParamsOfProofBlockData, _ handler: @escaping (TSDKBindingResponse) throws -> Void + ) { + let method: String = "proof_block_data" + binding.requestLibraryAsync(methodName(module, method), payload) { (requestId, params, responseType, finished) in + var response: TSDKBindingResponse = .init() + response.update(requestId, params, responseType, finished) + try handler(response) + } + } + + /// Proves that a given transaction's data, which is queried from TONOS API, can be trusted. + /// This function requests the corresponding block, checks block proofs, ensures that given transactionexists in the proven block and compares given data with the proven. + /// If the given data differs from the proven, the exception will be thrown. + /// The input parameter is a single transaction's JSON object (see params description),which was queried from TONOS API using functions such as `net.query`, `net.query_collection`or `net.wait_for_collection`. + /// If transaction's BOC and/or `block_id` are not provided in the JSON, they will be queried fromTONOS API (in this case it is required to provide at least `id` of transaction). + /// Please note, that joins (like `account`, `in_message`, `out_messages`, etc. in `Transaction`entity) are separated entities and not supported, so function will throw an exception in a caseif JSON being checked has such entities in it. + /// If `cache_in_local_storage` in config is set to `true` (default), downloaded proofs andmaster-chain BOCs are saved into the persistent local storage (e.g. file system for nativeenvironments or browser's IndexedDB for the web); otherwise all the data is cached only inmemory in current client's context and will be lost after destruction of the client. + /// For more information about proofs checking, see description of `proof_block_data` function. + public func proof_transaction_data(_ payload: TSDKParamsOfProofTransactionData, _ handler: @escaping (TSDKBindingResponse) throws -> Void + ) { + let method: String = "proof_transaction_data" + binding.requestLibraryAsync(methodName(module, method), payload) { (requestId, params, responseType, finished) in + var response: TSDKBindingResponse = .init() + response.update(requestId, params, responseType, finished) + try handler(response) + } + } + +} diff --git a/Sources/TonClientSwift/Proofs/ProofsTypes.swift b/Sources/TonClientSwift/Proofs/ProofsTypes.swift new file mode 100644 index 0000000..070b43d --- /dev/null +++ b/Sources/TonClientSwift/Proofs/ProofsTypes.swift @@ -0,0 +1,25 @@ +public enum TSDKProofsErrorCode: Int, Codable { + case InvalidData = 901 + case ProofCheckFailed = 902 + case InternalError = 903 + case DataDiffersFromProven = 904 +} + +public struct TSDKParamsOfProofBlockData: Codable { + /// Single block's data, retrieved from TONOS API, that needs proof. Required fields are `id` and/or top-level `boc` (for block identification), others are optional. + public var block: AnyValue + + public init(block: AnyValue) { + self.block = block + } +} + +public struct TSDKParamsOfProofTransactionData: Codable { + /// Single transaction's data as queried from DApp server, without modifications. The required fields are `id` and/or top-level `boc`, others are optional. In order to reduce network requests count, it is recommended to provide `block_id` and `boc` of transaction. + public var transaction: AnyValue + + public init(transaction: AnyValue) { + self.transaction = transaction + } +} +