From 3b55b1359b6c41c09493eb44525c4335999bc3c8 Mon Sep 17 00:00:00 2001 From: Kirill Kirilenko Date: Thu, 20 Jul 2023 15:30:52 +0400 Subject: [PATCH 01/11] remove all ; --- Source/TonSwift/Address/Address.swift | 4 ++-- Source/TonSwift/Address/AnyAddress.swift | 18 +++++++++--------- Source/TonSwift/Address/FriendlyAddress.swift | 2 +- Source/TonSwift/Cells/Cell.swift | 12 ++++++------ Source/TonSwift/Cells/Slice.swift | 12 ++++++------ Source/TonSwift/Cells/SnakeEncoding.swift | 4 ++-- Source/TonSwift/Contracts/Coins.swift | 2 +- Source/TonSwift/Contracts/StateInit.swift | 14 +++++++------- Tests/TonSwiftTests/Cells/BocTest.swift | 2 +- Tests/TonSwiftTests/Cells/DictionaryTest.swift | 2 +- .../TonSwiftTests/Contracts/SendModeTest.swift | 16 ++++++++-------- .../Contracts/StateInitTest.swift | 2 +- 12 files changed, 45 insertions(+), 45 deletions(-) diff --git a/Source/TonSwift/Address/Address.swift b/Source/TonSwift/Address/Address.swift index 344c50b..b236f44 100644 --- a/Source/TonSwift/Address/Address.swift +++ b/Source/TonSwift/Address/Address.swift @@ -26,7 +26,7 @@ public struct Address: Hashable, Codable { /// Initializes address from the raw format `:` (decimal workchain, hex-encoded hash part) public static func parse(raw: String) throws -> Address { - let parts = raw.split(separator: ":"); + let parts = raw.split(separator: ":") guard parts.count == 2 else { throw TonError.custom("Raw address is malformed: should be in the form `:`") } @@ -96,7 +96,7 @@ extension Address: CellCodable, StaticSize { } // No Anycast supported - let anycastPrefix = try s.loadUint(bits: 1); + let anycastPrefix = try s.loadUint(bits: 1) if anycastPrefix != 0 { throw TonError.custom("Invalid address: anycast not supported") } diff --git a/Source/TonSwift/Address/AnyAddress.swift b/Source/TonSwift/Address/AnyAddress.swift index 90a35fb..5978ce9 100644 --- a/Source/TonSwift/Address/AnyAddress.swift +++ b/Source/TonSwift/Address/AnyAddress.swift @@ -47,8 +47,8 @@ enum AnyAddress { /// Converts to an optional internal address. Throws error if it is an external address. public func asInternal() throws -> Address? { switch self { - case .none: return nil; - case .internalAddr(let addr): return addr; + case .none: return nil + case .internalAddr(let addr): return addr case .externalAddr(_): throw TonError.custom("Expected internal address") } } @@ -56,9 +56,9 @@ enum AnyAddress { /// Converts to an external address. Throws error if it is an internal address. public func asExternal() throws -> ExternalAddress? { switch self { - case .none: return nil; + case .none: return nil case .internalAddr(_): throw TonError.custom("Expected external address") - case .externalAddr(let addr): return addr; + case .externalAddr(let addr): return addr } } } @@ -82,14 +82,14 @@ extension AnyAddress: CellCodable { let type = try slice.preloadUint(bits: 2) switch type { case 0: - try slice.skip(2); - return .none; + try slice.skip(2) + return .none case 1: - return .externalAddr(try slice.loadType()); + return .externalAddr(try slice.loadType()) case 2,3: - return .internalAddr(try slice.loadType()); + return .internalAddr(try slice.loadType()) default: - throw TonError.custom("Unreachable error"); + throw TonError.custom("Unreachable error") } } } diff --git a/Source/TonSwift/Address/FriendlyAddress.swift b/Source/TonSwift/Address/FriendlyAddress.swift index c4c0ca2..3a5ae9e 100644 --- a/Source/TonSwift/Address/FriendlyAddress.swift +++ b/Source/TonSwift/Address/FriendlyAddress.swift @@ -1,7 +1,7 @@ import Foundation /// By default, addresses are bounceable for safety of TON transfers. -public let BounceableDefault = true; +public let BounceableDefault = true let bounceableTag: UInt8 = 0x11 let nonBounceableTag: UInt8 = 0x51 diff --git a/Source/TonSwift/Cells/Cell.swift b/Source/TonSwift/Cells/Cell.swift index be248d3..fa16ecb 100644 --- a/Source/TonSwift/Cells/Cell.swift +++ b/Source/TonSwift/Cells/Cell.swift @@ -65,7 +65,7 @@ public struct Cell: Hashable { self.basic = BasicCell(type: .ordinary, bits: bits, refs: refs) } - let precomputed = try self.basic.precompute(); + let precomputed = try self.basic.precompute() self.mask = precomputed.mask self._depths = precomputed.depths self._hashes = precomputed.hashes @@ -138,7 +138,7 @@ public struct Cell: Hashable { /// Same as `toSlice`. public func beginParse(allowExotic: Bool = false) throws -> Slice { if isExotic && !allowExotic { - throw TonError.custom("Exotic cells cannot be parsed"); + throw TonError.custom("Exotic cells cannot be parsed") } return Slice(cell: self) @@ -218,7 +218,7 @@ fileprivate struct BasicCell: Hashable { let reader = Slice(bits: bits) let typeInt = try reader.preloadUint(bits: 8) - let type: CellType; + let type: CellType switch typeInt { case 1: type = try resolvePruned(bits: bits, refs: refs).type @@ -417,7 +417,7 @@ func exoticPruned(bits: Bitstring, refs: [Cell]) throws -> ExoticPruned { } if refs.count != 0 { - throw TonError.custom("Pruned Branch cell can't has refs, got \(refs.count)"); + throw TonError.custom("Pruned Branch cell can't has refs, got \(refs.count)") } // Resolve cell @@ -432,13 +432,13 @@ func exoticPruned(bits: Bitstring, refs: [Cell]) throws -> ExoticPruned { let level = UInt32(try reader.loadUint(bits: 8)) mask = LevelMask(mask: level) if mask.level < 1 || mask.level > 3 { - throw TonError.custom("Pruned Branch cell level must be >= 1 and <= 3, got \(mask.level)/\(mask.value)"); + throw TonError.custom("Pruned Branch cell level must be >= 1 and <= 3, got \(mask.level)/\(mask.value)") } // Read pruned let size = 8 + 8 + (mask.apply(level: mask.level - 1).hashCount * (256 /* Hash */ + 16 /* Depth */)) if bits.length != size { - throw TonError.custom("Pruned branch cell must have exactly size bits, got \(bits.length)"); + throw TonError.custom("Pruned branch cell must have exactly size bits, got \(bits.length)") } } diff --git a/Source/TonSwift/Cells/Slice.swift b/Source/TonSwift/Cells/Slice.swift index ff4bad0..69db649 100644 --- a/Source/TonSwift/Cells/Slice.swift +++ b/Source/TonSwift/Cells/Slice.swift @@ -127,12 +127,12 @@ public class Slice { /// If parsing succeeded, the slice is advanced. /// If parsing failed, the slice remains unchanged. public func tryLoad(_ closure: (Slice) throws -> T) throws -> T { - let tmpslice = self.clone(); - let result = try closure(tmpslice); - self.bitstring = tmpslice.bitstring; - self.offset = tmpslice.offset; - self.refs = tmpslice.refs; - return result; + let tmpslice = self.clone() + let result = try closure(tmpslice) + self.bitstring = tmpslice.bitstring + self.offset = tmpslice.offset + self.refs = tmpslice.refs + return result } diff --git a/Source/TonSwift/Cells/SnakeEncoding.swift b/Source/TonSwift/Cells/SnakeEncoding.swift index abe3593..a0aceda 100644 --- a/Source/TonSwift/Cells/SnakeEncoding.swift +++ b/Source/TonSwift/Cells/SnakeEncoding.swift @@ -7,7 +7,7 @@ extension Slice { guard let str = String(data: try self.loadSnakeData(), encoding: .utf8) else { throw TonError.custom("Cannot read slice to string") } - return str; + return str } /// Loads snake-encoded Data. Fails if the binary string is malformed. @@ -68,7 +68,7 @@ extension Builder { extension String { /// Encodes a String into a Cell public func toTonCell() throws -> Cell { - return try Builder().writeSnakeData(Data(self.utf8)).endCell(); + return try Builder().writeSnakeData(Data(self.utf8)).endCell() } } diff --git a/Source/TonSwift/Contracts/Coins.swift b/Source/TonSwift/Contracts/Coins.swift index d5b5f1f..0235557 100644 --- a/Source/TonSwift/Contracts/Coins.swift +++ b/Source/TonSwift/Contracts/Coins.swift @@ -12,7 +12,7 @@ public struct Coins { } extension Coins: RawRepresentable { - public typealias RawValue = BigUInt; + public typealias RawValue = BigUInt public init?(rawValue: BigUInt) { self.amount = rawValue diff --git a/Source/TonSwift/Contracts/StateInit.swift b/Source/TonSwift/Contracts/StateInit.swift index 2459599..4d42f52 100644 --- a/Source/TonSwift/Contracts/StateInit.swift +++ b/Source/TonSwift/Contracts/StateInit.swift @@ -18,11 +18,11 @@ public struct StateInit: CellCodable { code: Cell? = nil, data: Cell? = nil, libraries: [UInt256: SimpleLibrary] = [:]) { - self.splitDepth = splitDepth; - self.special = special; - self.code = code; - self.data = data; - self.libraries = libraries; + self.splitDepth = splitDepth + self.special = special + self.code = code + self.data = data + self.libraries = libraries } public func storeTo(builder: Builder) throws { @@ -48,11 +48,11 @@ public struct StateInit: CellCodable { static public func loadFrom(slice: Slice) throws -> StateInit { let splitDepth: UInt32? = try slice.loadMaybe { s in UInt32(try s.loadUint(bits: 5)) - }; + } let special: TickTock? = try slice.loadMaybe { s in try TickTock.loadFrom(slice: slice) - }; + } let code = try slice.loadMaybeRef() let data = try slice.loadMaybeRef() diff --git a/Tests/TonSwiftTests/Cells/BocTest.swift b/Tests/TonSwiftTests/Cells/BocTest.swift index 4fc2f2c..4facc3b 100644 --- a/Tests/TonSwiftTests/Cells/BocTest.swift +++ b/Tests/TonSwiftTests/Cells/BocTest.swift @@ -11,7 +11,7 @@ final class SerializationTest: XCTestCase { "B5EE9C724101010100630000C2FF0020DD2082014C97BA218201339CBAB19C71B0ED44D0D31FD70BFFE304E0A4F2608308D71820D31FD31F01F823BBF263ED44D0D31FD3FFD15131BAF2A103F901541042F910F2A2F800029320D74A96D307D402FB00E8D1A4C8CB1FCBFFC9ED54044CD7A1", "B5EE9C724101010100620000C0FF0020DD2082014C97BA9730ED44D0D70B1FE0A4F2608308D71820D31FD31FD31FF82313BBF263ED44D0D31FD31FD3FFD15132BAF2A15144BAF2A204F901541055F910F2A3F8009320D74A96D307D402FB00E8D101A4C8CB1FCB1FCBFFC9ED543FBE6EE0", "B5EE9C724101010100710000DEFF0020DD2082014C97BA218201339CBAB19F71B0ED44D0D31FD31F31D70BFFE304E0A4F2608308D71820D31FD31FD31FF82313BBF263ED44D0D31FD31FD3FFD15132BAF2A15144BAF2A204F901541055F910F2A3F8009320D74A96D307D402FB00E8D101A4C8CB1FCB1FCBFFC9ED5410BD6DAD" - ]; + ] func testSerialization() throws { // should parse wallet code diff --git a/Tests/TonSwiftTests/Cells/DictionaryTest.swift b/Tests/TonSwiftTests/Cells/DictionaryTest.swift index 01c1a75..27dec5c 100644 --- a/Tests/TonSwiftTests/Cells/DictionaryTest.swift +++ b/Tests/TonSwiftTests/Cells/DictionaryTest.swift @@ -41,7 +41,7 @@ final class DictionaryTest: XCTestCase { .store(ref: try builderFrom("1011111011111101111100100001")) .endCell() - let dict: [UInt16: UInt16] = try root.beginParse().loadDictRoot(); + let dict: [UInt16: UInt16] = try root.beginParse().loadDictRoot() XCTAssertEqual(dict[13], 169) XCTAssertEqual(dict[17], 289) XCTAssertEqual(dict[239], 57121) diff --git a/Tests/TonSwiftTests/Contracts/SendModeTest.swift b/Tests/TonSwiftTests/Contracts/SendModeTest.swift index 5112fc2..9663ad6 100644 --- a/Tests/TonSwiftTests/Contracts/SendModeTest.swift +++ b/Tests/TonSwiftTests/Contracts/SendModeTest.swift @@ -4,13 +4,13 @@ import XCTest final class SendModeTest: XCTestCase { func testSendModeEncoding() throws { - XCTAssertEqual(SendMode().rawValue, 0); - XCTAssertEqual(SendMode.walletDefault().rawValue, 3); - XCTAssertEqual(SendMode(value: .sendRemainingBalance).rawValue, 128); - XCTAssertEqual(SendMode(value: .sendRemainingBalanceAndDestroy).rawValue, 128+32); - XCTAssertEqual(SendMode(value: .addInboundValue).rawValue, 64); - XCTAssertEqual(SendMode(payMsgFees: true).rawValue, 1); - XCTAssertEqual(SendMode(payMsgFees: true, ignoreErrors: true).rawValue, 3); - XCTAssertEqual(SendMode(payMsgFees: true, ignoreErrors: true, value: .sendRemainingBalanceAndDestroy).rawValue, 3+128+32); + XCTAssertEqual(SendMode().rawValue, 0) + XCTAssertEqual(SendMode.walletDefault().rawValue, 3) + XCTAssertEqual(SendMode(value: .sendRemainingBalance).rawValue, 128) + XCTAssertEqual(SendMode(value: .sendRemainingBalanceAndDestroy).rawValue, 128+32) + XCTAssertEqual(SendMode(value: .addInboundValue).rawValue, 64) + XCTAssertEqual(SendMode(payMsgFees: true).rawValue, 1) + XCTAssertEqual(SendMode(payMsgFees: true, ignoreErrors: true).rawValue, 3) + XCTAssertEqual(SendMode(payMsgFees: true, ignoreErrors: true, value: .sendRemainingBalanceAndDestroy).rawValue, 3+128+32) } } diff --git a/Tests/TonSwiftTests/Contracts/StateInitTest.swift b/Tests/TonSwiftTests/Contracts/StateInitTest.swift index 15153a5..4ce8671 100644 --- a/Tests/TonSwiftTests/Contracts/StateInitTest.swift +++ b/Tests/TonSwiftTests/Contracts/StateInitTest.swift @@ -13,7 +13,7 @@ final class StateInitTest: XCTestCase { .endCell() .toBoc(idx: false, crc32: true) - let enc = boc.base64EncodedString(); + let enc = boc.base64EncodedString() //XCTAssertEqual(boc.base64EncodedString(), "te6cckEBAwEACwACATQCAQACAgACAX/38hg=") // TBD: figure out why there is random failure in the above test XCTAssertTrue( From 500d1eeb5f648bb01f3a1f894426004ad971a8de Mon Sep 17 00:00:00 2001 From: Kirill Kirilenko Date: Thu, 20 Jul 2023 15:50:12 +0400 Subject: [PATCH 02/11] remove useless return's --- Source/TonSwift/Address/ADNLAddress.swift | 8 +-- Source/TonSwift/Address/Address.swift | 24 +++---- Source/TonSwift/Address/ExternalAddress.swift | 7 +- Source/TonSwift/Address/FriendlyAddress.swift | 4 +- Source/TonSwift/Cells/Bitstring.swift | 6 +- Source/TonSwift/Cells/Boc.swift | 2 +- Source/TonSwift/Cells/Builder.swift | 30 ++++---- Source/TonSwift/Cells/Cell.swift | 34 ++++----- Source/TonSwift/Cells/Dictionary.swift | 6 +- Source/TonSwift/Cells/Integers.swift | 30 ++++---- Source/TonSwift/Cells/Serialization.swift | 12 ++-- Source/TonSwift/Cells/Slice.swift | 70 ++++++------------- Source/TonSwift/Cells/SnakeEncoding.swift | 4 +- Source/TonSwift/Contracts/Coins.swift | 15 ++-- Source/TonSwift/Contracts/Message.swift | 2 +- .../TonSwift/Contracts/MessageRelaxed.swift | 31 ++++++-- Source/TonSwift/Contracts/SendMode.swift | 2 +- Source/TonSwift/Contracts/StateInit.swift | 4 +- Source/TonSwift/Keys/PublicKey.swift | 2 +- Source/TonSwift/Mnemonic/Mnemonic.swift | 4 +- Source/TonSwift/TVM/TupleReader.swift | 4 +- Source/TonSwift/Util/BitsCount.swift | 2 +- Source/TonSwift/Util/Data+Bytes.swift | 2 +- .../TonSwiftTests/Cells/DictionaryTest.swift | 4 +- .../Wallets/WalletContractV1Test.swift | 2 +- .../Wallets/WalletContractV2Test.swift | 2 +- .../Wallets/WalletContractV3Test.swift | 2 +- .../Wallets/WalletContractV4Test.swift | 2 +- 28 files changed, 150 insertions(+), 167 deletions(-) diff --git a/Source/TonSwift/Address/ADNLAddress.swift b/Source/TonSwift/Address/ADNLAddress.swift index ca9f639..d76d1e3 100644 --- a/Source/TonSwift/Address/ADNLAddress.swift +++ b/Source/TonSwift/Address/ADNLAddress.swift @@ -21,9 +21,7 @@ struct ADNLAddress { } static func parseRaw(_ src: String) throws -> ADNLAddress { - let data = Data(base64Encoded: src) - - return try ADNLAddress(address: data!) + try ADNLAddress(address: Data(base64Encoded: src)!) } let address: Data @@ -37,7 +35,7 @@ struct ADNLAddress { } func toRaw() -> String { - return address.map { String(format: "%02X", $0) }.joined().uppercased() + address.map { String(format: "%02X", $0) }.joined().uppercased() } func toString() -> String { @@ -53,6 +51,6 @@ struct ADNLAddress { // MARK: - Equatable extension ADNLAddress: Equatable { static func == (lhs: ADNLAddress, rhs: ADNLAddress) -> Bool { - return lhs.address == rhs.address + lhs.address == rhs.address } } diff --git a/Source/TonSwift/Address/Address.swift b/Source/TonSwift/Address/Address.swift index b236f44..7d4f643 100644 --- a/Source/TonSwift/Address/Address.swift +++ b/Source/TonSwift/Address/Address.swift @@ -12,16 +12,12 @@ public struct Address: Hashable, Codable { /// Generates a test address public static func mock(workchain: Int8, seed: String) -> Self { - return Address(workchain: workchain, hash: Data(seed.utf8).sha256()) + Address(workchain: workchain, hash: Data(seed.utf8).sha256()) } /// Parses address from any format public static func parse(_ source: String) throws -> Address { - if source.firstIndex(of: ":") == nil { - return try FriendlyAddress(string: source).address - } else { - return try parse(raw: source) - } + source.firstIndex(of: ":") == nil ? try FriendlyAddress(string: source).address : try parse(raw: source) } /// Initializes address from the raw format `:` (decimal workchain, hex-encoded hash part) @@ -41,28 +37,24 @@ public struct Address: Hashable, Codable { /// Returns raw format of the address: `:` (decimal workchain, hex-encoded hash part) public func toRaw() -> String { - return "\(workchain):\(hash.hexString())" + "\(workchain):\(hash.hexString())" } /// Returns raw format of the address: `:` (decimal workchain, hex-encoded hash part) public func toFriendly(testOnly: Bool = false, bounceable: Bool = BounceableDefault) -> FriendlyAddress { - return FriendlyAddress(address: self, testOnly: testOnly, bounceable: bounceable) + FriendlyAddress(address: self, testOnly: testOnly, bounceable: bounceable) } /// Shortcut for constructing FriendlyAddress with all the options. public func toString(urlSafe: Bool = true, testOnly: Bool = false, bounceable: Bool = BounceableDefault) -> String { - return self.toFriendly(testOnly: testOnly, bounceable: bounceable).toString(urlSafe: urlSafe) + self.toFriendly(testOnly: testOnly, bounceable: bounceable).toString(urlSafe: urlSafe) } } // MARK: - Equatable extension Address: Equatable { public static func == (lhs: Address, rhs: Address) -> Bool { - if lhs.workchain != rhs.workchain { - return false - } - - return lhs.hash == rhs.hash + lhs.workchain != rhs.workchain ? false : lhs.hash == rhs.hash } } @@ -89,7 +81,7 @@ extension Address: CellCodable, StaticSize { } public static func loadFrom(slice: Slice) throws -> Address { - return try slice.tryLoad { s in + try slice.tryLoad { s in let type = try s.loadUint(bits: 2) if type != 2 { throw TonError.custom("Unsupported address type: expecting internal address `addr_std$10`") @@ -125,7 +117,7 @@ public struct CompactAddress: Hashable, CellCodable, StaticSize { } public static func loadFrom(slice: Slice) throws -> CompactAddress { - return try slice.tryLoad { s in + try slice.tryLoad { s in let wc = Int8(try s.loadInt(bits: 8)) let hash = try s.loadBytes(32) return CompactAddress(Address(workchain: wc, hash: hash)) diff --git a/Source/TonSwift/Address/ExternalAddress.swift b/Source/TonSwift/Address/ExternalAddress.swift index 3b2e68e..a4c737d 100644 --- a/Source/TonSwift/Address/ExternalAddress.swift +++ b/Source/TonSwift/Address/ExternalAddress.swift @@ -12,12 +12,11 @@ public struct ExternalAddress { } public func toString() -> String { - return "External<\(value.length):\(value.toString())>" + "External<\(value.length):\(value.toString())>" } public static func mock(seed: String) throws -> Self { - let value = Bitstring(data: Data(seed.utf8).sha256()) - return ExternalAddress(value: value) + ExternalAddress(value: Bitstring(data: Data(seed.utf8).sha256())) } } @@ -29,7 +28,7 @@ extension ExternalAddress: CellCodable { } public static func loadFrom(slice: Slice) throws -> ExternalAddress { - return try slice.tryLoad { s in + try slice.tryLoad { s in let type = try s.loadUint(bits: 2) if type != 1 { throw TonError.custom("Invalid ExternalAddress") diff --git a/Source/TonSwift/Address/FriendlyAddress.swift b/Source/TonSwift/Address/FriendlyAddress.swift index 3a5ae9e..39df1e7 100644 --- a/Source/TonSwift/Address/FriendlyAddress.swift +++ b/Source/TonSwift/Address/FriendlyAddress.swift @@ -13,8 +13,8 @@ public struct FriendlyAddress: Codable { public let isBounceable: Bool public let address: Address - var workchain: Int8 { return self.address.workchain } - var hash: Data { return self.address.hash } + var workchain: Int8 { self.address.workchain } + var hash: Data { self.address.hash } init(string: String) throws { // Convert from url-friendly to true base64 diff --git a/Source/TonSwift/Cells/Bitstring.swift b/Source/TonSwift/Cells/Bitstring.swift index cbad200..3da6f9c 100644 --- a/Source/TonSwift/Cells/Bitstring.swift +++ b/Source/TonSwift/Cells/Bitstring.swift @@ -151,7 +151,7 @@ public struct Bitstring: Hashable { /// Drops first `n` bits from the bitstring. public func dropFirst(_ n: Int) throws -> Bitstring { - return try substring(offset: n, length: self.length - n) + try substring(offset: n, length: self.length - n) } /// Formats the bitstring as a hex-encoded string with a `_` trailing symbol indicating `10*` padding to 4-bit alignment. @@ -186,7 +186,7 @@ public struct Bitstring: Hashable { /// Formats the bitstring as a hex-encoded string with a `_` trailing symbol indicating `10*` padding to 4-bit alignment. public func toString() -> String { - return toHex() + toHex() } private func checkOffset(offset: Int, length: Int) throws { @@ -266,7 +266,7 @@ extension Bitstring: Equatable { extension Data { public func subdata(in range: ClosedRange) -> Data { - return subdata(in: range.lowerBound ..< range.upperBound) + subdata(in: range.lowerBound ..< range.upperBound) } public func hexString() -> String { diff --git a/Source/TonSwift/Cells/Boc.swift b/Source/TonSwift/Cells/Boc.swift index ac800c9..c11d210 100644 --- a/Source/TonSwift/Cells/Boc.swift +++ b/Source/TonSwift/Cells/Boc.swift @@ -115,7 +115,7 @@ func readCell(reader: Slice, sizeBytes: Int) throws -> (exotic: Bool, bits: Bits } func calcCellSize(cell: Cell, sizeBytes: Int) -> Int { - return 2 /* D1+D2 */ + Int(ceil(Double(cell.bits.length) / 8.0)) + cell.refs.count * sizeBytes + 2 /* D1+D2 */ + Int(ceil(Double(cell.bits.length) / 8.0)) + cell.refs.count * sizeBytes } func deserializeBoc(src: Data) throws -> [Cell] { diff --git a/Source/TonSwift/Cells/Builder.swift b/Source/TonSwift/Cells/Builder.swift index 7f97be5..066c8fb 100644 --- a/Source/TonSwift/Cells/Builder.swift +++ b/Source/TonSwift/Cells/Builder.swift @@ -40,7 +40,7 @@ public class Builder { /// Clones slice at its current state. public func clone() -> Builder { - return Builder(capacity: capacity, buffer: _buffer, length: _length, refs: refs) + Builder(capacity: capacity, buffer: _buffer, length: _length, refs: refs) } /// Completes cell @@ -51,12 +51,12 @@ public class Builder { /// Same as `endCell` public func asCell() throws -> Cell { - return try endCell() + try endCell() } /// Converts builder into BitString public func bitstring() -> Bitstring { - return Bitstring(data: _buffer, unchecked:(offset: 0, length: _length)) + Bitstring(data: _buffer, unchecked:(offset: 0, length: _length)) } /// Converts to data if the bitstring contains a whole number of bytes. @@ -75,37 +75,37 @@ public class Builder { /// Returns whether the written bits are byte-aligned public var aligned: Bool { - return _length % 8 == 0 + _length % 8 == 0 } /// Number of written bits public var bitsCount: Int { - return _length + _length } /// Number of references added to this cell public var refsCount: Int { - return refs.count + refs.count } /// Remaining bits available public var availableBits: Int { - return capacity - bitsCount + capacity - bitsCount } /// Remaining refs available public var availableRefs: Int { - return RefsPerCell - refsCount + RefsPerCell - refsCount } /// Returns metrics for the currently stored data public var metrics: CellMetrics { - return CellMetrics(bitsCount: bitsCount, refsCount: refsCount) + CellMetrics(bitsCount: bitsCount, refsCount: refsCount) } /// Returns metrics for the remaining space in the cell public var remainingMetrics: CellMetrics { - return CellMetrics(bitsCount: availableBits, refsCount: availableRefs) + CellMetrics(bitsCount: availableBits, refsCount: availableRefs) } /// Tries to fit the cell with the given metrics and returns the remaining space. @@ -165,7 +165,7 @@ public class Builder { } @discardableResult public func store(ref builder: Builder) throws -> Self { - return try store(ref: try builder.endCell()) + try store(ref: try builder.endCell()) } /** @@ -265,7 +265,7 @@ public class Builder { /// Writes bit as a boolean (true => 1, false => 0) @discardableResult public func store(bit: Bool) throws -> Self { - return try store(bit: bit ? 1 : 0) + try store(bit: bit ? 1 : 0) } /// Write repeating bit a given number of times. @@ -346,7 +346,7 @@ public class Builder { */ @discardableResult public func store(uint value: T, bits: Int) throws -> Self where T: BinaryInteger { - return try store(biguint: BigUInt(value), bits: bits) + try store(biguint: BigUInt(value), bits: bits) } @discardableResult @@ -422,7 +422,7 @@ public class Builder { @discardableResult public func store(int value: any BinaryInteger, bits: Int) throws -> Self { - return try store(bigint: BigInt(value), bits: bits) + try store(bigint: BigInt(value), bits: bits) } @discardableResult @@ -478,7 +478,7 @@ public class Builder { /// Therefore, `(VarUInteger 16)` accepts 120-bit number (15 bytes) and uses 4 bits to encode length prefix 0...15. @discardableResult func store(varuint v: UInt64, limit: Int) throws -> Self { - return try store(varuint: BigUInt(v), limit: limit) + try store(varuint: BigUInt(v), limit: limit) } /// Stores VarUInteger with a given `limit` in bytes. diff --git a/Source/TonSwift/Cells/Cell.swift b/Source/TonSwift/Cells/Cell.swift index fa16ecb..0b3c037 100644 --- a/Source/TonSwift/Cells/Cell.swift +++ b/Source/TonSwift/Cells/Cell.swift @@ -35,7 +35,7 @@ public struct Cell: Hashable { public var isExotic: Bool { type != .ordinary } public var metrics: CellMetrics { - return CellMetrics(bitsCount: bits.length, refsCount: refs.count) + CellMetrics(bitsCount: bits.length, refsCount: refs.count) } // Precomputed data @@ -88,7 +88,7 @@ public struct Cell: Hashable { - returns array of cells */ static func fromBoc(src: Data) throws -> [Cell] { - return try deserializeBoc(src: src) + try deserializeBoc(src: src) } /** @@ -114,14 +114,14 @@ public struct Cell: Hashable { - returns cell hash */ public func hash(level: Int = 3) -> Data { - return _hashes[min(_hashes.count - 1, level)] + _hashes[min(_hashes.count - 1, level)] } /// Returns the lowest-order hash that represents an actual tree of cells, possibly pruned. /// This is the hash of the data being transmitted. /// For the hash of the underlying contents see `hash(level:)` method. public func representationHash() -> Data { - return _hashes[0] + _hashes[0] } @@ -131,7 +131,7 @@ public struct Cell: Hashable { - returns cell depth */ public func depth(level: Int = 3) -> UInt32 { - return _depths[min(_depths.count - 1, level)] + _depths[min(_depths.count - 1, level)] } /// Convert cell to slice so it can be parsed. @@ -147,19 +147,19 @@ public struct Cell: Hashable { /// Convert cell to slice so it can be parsed. /// Same as `beginParse`. public func toSlice() throws -> Slice { - return try beginParse() + try beginParse() } /// Convert cell to a builder that has this cell pre-stored. Finalizing this builder yields the same cell. public func toBuilder() throws -> Builder { - return try Builder().store(slice: toSlice()) + try Builder().store(slice: toSlice()) } /** Serializes cell to BOC - parameter opts: options */ func toBoc(idx: Bool = false, crc32: Bool = true) throws -> Data { - return try serializeBoc(root: self, idx: idx, crc32: crc32) + try serializeBoc(root: self, idx: idx, crc32: crc32) } /** @@ -200,7 +200,7 @@ extension Cell: Equatable { - returns true if cells are equal */ public static func == (lhs: Cell, rhs: Cell) -> Bool { - return lhs.hash() == rhs.hash() + lhs.hash() == rhs.hash() } } @@ -582,33 +582,33 @@ public struct LevelMask { } public var value: UInt32 { - return _mask + _mask } public var level: UInt32 { - return UInt32(32 - _mask.leadingZeroBitCount) + UInt32(32 - _mask.leadingZeroBitCount) } public var hashIndex: UInt32 { - return _hashIndex + _hashIndex } public var hashCount: UInt32 { - return _hashCount + _hashCount } public func apply(level: UInt32) -> LevelMask { - return LevelMask(mask: _mask & ((1 << level) - 1)) + LevelMask(mask: _mask & ((1 << level) - 1)) } public func isSignificant(level: UInt32) -> Bool { - return level == 0 || (_mask >> (level - 1)) % 2 != 0 + level == 0 || (_mask >> (level - 1)) % 2 != 0 } } extension LevelMask: Hashable { public static func == (lhs: LevelMask, rhs: LevelMask) -> Bool { - return lhs._mask == rhs._mask && + lhs._mask == rhs._mask && lhs._hashIndex == rhs._hashIndex && lhs._hashCount == rhs._hashCount } @@ -634,5 +634,5 @@ func countSetBits(_ n: UInt32) -> UInt32 { /// denotes the subtype of the natural numbers type #, consisting of integers 0 . . . p; /// it is serialized into ⌈log2(p + 1)⌉ bits as an unsigned big-endian integer. public func bitsForInt(_ n: Int) -> Int { - return Int(ceil(log2(Double(n + 1)))) + Int(ceil(log2(Double(n + 1)))) } diff --git a/Source/TonSwift/Cells/Dictionary.swift b/Source/TonSwift/Cells/Dictionary.swift index d2a22ec..3168a5c 100644 --- a/Source/TonSwift/Cells/Dictionary.swift +++ b/Source/TonSwift/Cells/Dictionary.swift @@ -9,7 +9,7 @@ public protocol CellCodableDictionary: CellCodable { extension Dictionary: CellCodable where Key: CellCodable & StaticSize, Value: CellCodable { public static func loadFrom(slice: Slice) throws -> Self { - return try DictionaryCoder.default().load(slice) + try DictionaryCoder.default().load(slice) } public func storeTo(builder: Builder) throws { try DictionaryCoder.default().store(map: self, builder: builder) @@ -23,7 +23,7 @@ extension Dictionary: CellCodableDictionary where Key: CellCodable & StaticSize, } public static func loadRootFrom(slice: Slice) throws -> Self { - return try DictionaryCoder.default().loadRoot(slice) + try DictionaryCoder.default().loadRoot(slice) } } @@ -70,7 +70,7 @@ public class DictionaryCoder where K.T: Hashable { VT: CellCodable, K == DefaultCoder, V == DefaultCoder { - return DictionaryCoder( + DictionaryCoder( keyLength: KT.bitWidth, DefaultCoder(), DefaultCoder() diff --git a/Source/TonSwift/Cells/Integers.swift b/Source/TonSwift/Cells/Integers.swift index 7c34e67..30e7d07 100644 --- a/Source/TonSwift/Cells/Integers.swift +++ b/Source/TonSwift/Cells/Integers.swift @@ -35,7 +35,7 @@ extension Bool: CellCodable, StaticSize { } public static func loadFrom(slice: Slice) throws -> Self { - return try slice.loadBoolean() + try slice.loadBoolean() } } @@ -54,7 +54,7 @@ public struct UInt256: Hashable, CellCodable, StaticSize { } public static func loadFrom(slice: Slice) throws -> Self { - return Self(biguint: try slice.loadUintBig(bits: Self.bitWidth)) + Self(biguint: try slice.loadUintBig(bits: Self.bitWidth)) } } @@ -68,7 +68,7 @@ extension UInt8: CellCodable, StaticSize { } public static func loadFrom(slice: Slice) throws -> Self { - return Self(try slice.loadUint(bits: Self.bitWidth)) + Self(try slice.loadUint(bits: Self.bitWidth)) } } @@ -80,7 +80,7 @@ extension UInt16: CellCodable, StaticSize { } public static func loadFrom(slice: Slice) throws -> Self { - return Self(try slice.loadUint(bits: Self.bitWidth)) + Self(try slice.loadUint(bits: Self.bitWidth)) } } @@ -92,7 +92,7 @@ extension UInt32: CellCodable, StaticSize { } public static func loadFrom(slice: Slice) throws -> Self { - return Self(try slice.loadUint(bits: Self.bitWidth)) + Self(try slice.loadUint(bits: Self.bitWidth)) } } @@ -104,7 +104,7 @@ extension UInt64: CellCodable, StaticSize { } public static func loadFrom(slice: Slice) throws -> Self { - return Self(try slice.loadUint(bits: Self.bitWidth)) + Self(try slice.loadUint(bits: Self.bitWidth)) } } @@ -118,7 +118,7 @@ extension Int8: CellCodable, StaticSize { } public static func loadFrom(slice: Slice) throws -> Self { - return Self(try slice.loadInt(bits: Self.bitWidth)) + Self(try slice.loadInt(bits: Self.bitWidth)) } } @@ -130,7 +130,7 @@ extension Int16: CellCodable, StaticSize { } public static func loadFrom(slice: Slice) throws -> Self { - return Self(try slice.loadInt(bits: Self.bitWidth)) + Self(try slice.loadInt(bits: Self.bitWidth)) } } @@ -142,7 +142,7 @@ extension Int32: CellCodable, StaticSize { } public static func loadFrom(slice: Slice) throws -> Self { - return Self(try slice.loadInt(bits: Self.bitWidth)) + Self(try slice.loadInt(bits: Self.bitWidth)) } } @@ -154,7 +154,7 @@ extension Int64: CellCodable, StaticSize { } public static func loadFrom(slice: Slice) throws -> Self { - return Self(try slice.loadInt(bits: Self.bitWidth)) + Self(try slice.loadInt(bits: Self.bitWidth)) } } @@ -171,7 +171,7 @@ public struct VarUInt248: Hashable, CellCodable { try builder.store(varuint: value, limit: 32) } public static func loadFrom(slice: Slice) throws -> Self { - return Self(value: try slice.loadVarUintBig(limit: 32)) + Self(value: try slice.loadVarUintBig(limit: 32)) } } @@ -182,7 +182,7 @@ public struct VarUInt120: Hashable, CellCodable { try builder.store(varuint: value, limit: 16) } public static func loadFrom(slice: Slice) throws -> Self { - return Self(value: try slice.loadVarUintBig(limit: 16)) + Self(value: try slice.loadVarUintBig(limit: 16)) } } @@ -200,7 +200,7 @@ public struct IntCoder: TypeCoder { } public func loadValue(from src: Slice) throws -> T { - return try src.loadIntBig(bits: bits) + try src.loadIntBig(bits: bits) } } @@ -218,7 +218,7 @@ public struct UIntCoder: TypeCoder { } public func loadValue(from src: Slice) throws -> T { - return try src.loadUintBig(bits: bits) + try src.loadUintBig(bits: bits) } } @@ -243,6 +243,6 @@ public struct VarUIntCoder: TypeCoder { } public func loadValue(from src: Slice) throws -> T { - return try src.loadVarUintBig(limit: limit) + try src.loadVarUintBig(limit: limit) } } diff --git a/Source/TonSwift/Cells/Serialization.swift b/Source/TonSwift/Cells/Serialization.swift index 283e22c..89313f1 100644 --- a/Source/TonSwift/Cells/Serialization.swift +++ b/Source/TonSwift/Cells/Serialization.swift @@ -32,7 +32,7 @@ public class DefaultCoder: TypeCoder { try src.storeTo(builder: builder) } public func loadValue(from src: Slice) throws -> T { - return try T.loadFrom(slice: src) + try T.loadFrom(slice: src) } } @@ -58,7 +58,7 @@ public class BytesCoder: TypeCoder { try builder.store(data: src) } public func loadValue(from src: Slice) throws -> T { - return try src.loadBytes(self.size) + try src.loadBytes(self.size) } } @@ -69,7 +69,7 @@ extension Cell: CellCodable { } public static func loadFrom(slice: Slice) throws -> Self { - return try slice.loadRef() + try slice.loadRef() } } @@ -80,7 +80,7 @@ extension Slice: CellCodable { } public static func loadFrom(slice: Slice) throws -> Self { - return slice.clone() as! Self + slice.clone() as! Self } } @@ -91,14 +91,14 @@ extension Builder: CellCodable { } public static func loadFrom(slice: Slice) throws -> Self { - return try slice.clone().toBuilder() as! Self + try slice.clone().toBuilder() as! Self } } /// Empty struct to store empty leafs in the dictionaries to form sets. public struct Empty: CellCodable { public static func loadFrom(slice: Slice) throws -> Self { - return Empty() + Empty() } public func storeTo(builder: Builder) throws { // store nothing diff --git a/Source/TonSwift/Cells/Slice.swift b/Source/TonSwift/Cells/Slice.swift index 69db649..8b8be7d 100644 --- a/Source/TonSwift/Cells/Slice.swift +++ b/Source/TonSwift/Cells/Slice.swift @@ -48,12 +48,12 @@ public class Slice { /// Remaining unread refs in this slice. public var remainingRefs: Int { - return refs.count + refs.count } /// Remaining unread bits in this slice. public var remainingBits: Int { - return bitstring.length - offset + bitstring.length - offset } @@ -72,13 +72,13 @@ public class Slice { /// Converts the remaining data in the slice to a Cell. /// This is the same as `asCell`, but reads better when you intend to read all the remaining data as a cell. public func loadRemainder() throws -> Cell { - return try toBuilder().endCell() + try toBuilder().endCell() } /// Converts the remaining data in the slice to a Cell. /// This is the same as `loadRemainder`, but reads better when you intend to serialize/inspect the slice. public func toCell() throws -> Cell { - return try toBuilder().endCell() + try toBuilder().endCell() } /// Converts slice to a Builder filled with remaining data in this slice. @@ -90,12 +90,12 @@ public class Slice { /// Clones slice at its current state. public func clone() -> Slice { - return Slice(bitstring: bitstring, offset: offset, refs: refs) + Slice(bitstring: bitstring, offset: offset, refs: refs) } /// Returns string representation of the slice as a cell. public func toString() throws -> String { - return try loadRemainder().toString() + try loadRemainder().toString() } @@ -106,21 +106,17 @@ public class Slice { /// Loads type T that implements interface Readable public func loadType() throws -> T { - return try T.loadFrom(slice: self) + try T.loadFrom(slice: self) } /// Preloads type T that implements interface Readable public func preloadType() throws -> T { - return try T.loadFrom(slice: self.clone()) + try T.loadFrom(slice: self.clone()) } /// Loads optional type T via closure. Function reads one bit that indicates the presence of data. If the bit is set, the closure is called to read T. public func loadMaybe(_ closure: (Slice) throws -> T) throws -> T? { - if try loadBoolean() { - return try closure(self) - } else { - return nil - } + try loadBoolean() ? try closure(self) : nil } /// Lets you attempt to read a complex data type. @@ -157,20 +153,12 @@ public class Slice { /// Loads an optional cell reference. public func loadMaybeRef() throws -> Cell? { - if try loadBoolean() { - return try loadRef() - } else { - return nil - } + try loadBoolean() ? try loadRef() : nil } /// Preloads an optional cell reference. public func preloadMaybeRef() throws -> Cell? { - if try preloadBit() == 1 { - return try preloadRef() - } else { - return nil - } + try preloadBit() == 1 ? try preloadRef() : nil } @@ -180,12 +168,12 @@ public class Slice { /// Reads a dictionary from the slice. public func loadDict() throws -> T where T: CellCodableDictionary { - return try T.loadFrom(slice: self) + try T.loadFrom(slice: self) } /// Reads the non-empty dictionary root directly from this slice. public func loadDictRoot() throws -> T where T: CellCodableDictionary { - return try T.loadRootFrom(slice: self) + try T.loadRootFrom(slice: self) } @@ -211,21 +199,17 @@ public class Slice { /// Load a single bit as a boolean value. public func loadBoolean() throws -> Bool { - return try loadBit() == 1 + try loadBit() == 1 } /// Loads an optional boolean. public func loadMaybeBoolean() throws -> Bool? { - if try loadBoolean() { - return try loadBoolean() - } else { - return nil - } + try? loadBoolean() } /// Preload a single bit without advancing the cursor. public func preloadBit() throws -> Bit { - return try bitstring.at(offset) + try bitstring.at(offset) } /// Loads the specified number of bits in a `BitString`. @@ -237,7 +221,7 @@ public class Slice { /// Preloads the specified number of bits in a `BitString` without advancing the cursor. public func preloadBits(_ bits: Int) throws -> Bitstring { - return try bitstring.substring(offset: offset, length: bits) + try bitstring.substring(offset: offset, length: bits) } /// Loads whole number of bytes and returns standard `Data` object. @@ -250,7 +234,7 @@ public class Slice { /// Preloads whole number of bytes and returns standard `Data` object without advancing the cursor. public func preloadBytes(_ bytes: Int) throws -> Data { - return try _preloadBuffer(bytes: bytes) + try _preloadBuffer(bytes: bytes) } @@ -295,7 +279,7 @@ public class Slice { - returns read value as number */ public func loadUint(bits: Int) throws -> UInt64 { - return UInt64(try loadUintBig(bits: bits)) + UInt64(try loadUintBig(bits: bits)) } /** @@ -340,7 +324,7 @@ public class Slice { - returns read value as number */ public func preloadUint(bits: Int) throws -> UInt64 { - return try _preloadUint(bits: bits, offset: offset) + try _preloadUint(bits: bits, offset: offset) } /** @@ -349,7 +333,7 @@ public class Slice { - returns read value as bigint */ public func preloadUintBig(bits: Int) throws -> BigUInt { - return try _preloadBigUint(bits: bits, offset: offset) + try _preloadBigUint(bits: bits, offset: offset) } /** @@ -358,11 +342,7 @@ public class Slice { - returns uint value or null */ public func loadMaybeUint(bits: Int) throws -> UInt64? { - if try loadBoolean() { - return try loadUint(bits: bits) - } else { - return nil - } + try loadBoolean() ? try loadUint(bits: bits) : nil } /** @@ -371,11 +351,7 @@ public class Slice { - returns uint value or null */ public func loadMaybeUintBig(bits: Int) throws -> BigUInt? { - if try loadBoolean() { - return try loadUintBig(bits: bits) - } else { - return nil - } + try loadBoolean() ? try loadUintBig(bits: bits) : nil } diff --git a/Source/TonSwift/Cells/SnakeEncoding.swift b/Source/TonSwift/Cells/SnakeEncoding.swift index a0aceda..74c5bb3 100644 --- a/Source/TonSwift/Cells/SnakeEncoding.swift +++ b/Source/TonSwift/Cells/SnakeEncoding.swift @@ -61,14 +61,14 @@ extension Builder { /// Writes snake-encoded UTF-8 string. public func writeSnakeString(_ src: String) throws -> Self { - return try writeSnakeData(Data(src.utf8)) + try writeSnakeData(Data(src.utf8)) } } extension String { /// Encodes a String into a Cell public func toTonCell() throws -> Cell { - return try Builder().writeSnakeData(Data(self.utf8)).endCell() + try Builder().writeSnakeData(Data(self.utf8)).endCell() } } diff --git a/Source/TonSwift/Contracts/Coins.swift b/Source/TonSwift/Contracts/Coins.swift index 0235557..1514a32 100644 --- a/Source/TonSwift/Contracts/Coins.swift +++ b/Source/TonSwift/Contracts/Coins.swift @@ -19,7 +19,7 @@ extension Coins: RawRepresentable { } public var rawValue: BigUInt { - return self.amount + self.amount } } @@ -28,27 +28,24 @@ extension Coins: CellCodable { try builder.store(varuint: self.amount, limit: 16) } public static func loadFrom(slice: Slice) throws -> Coins { - return Coins(try slice.loadVarUintBig(limit: 16)) + Coins(try slice.loadVarUintBig(limit: 16)) } } extension Slice { /// Loads Coins value public func loadCoins() throws -> Coins { - return try loadType() + try loadType() } /// Preloads Coins value public func preloadCoins() throws -> Coins { - return try preloadType() + try preloadType() } /// Load optionals Coins value. public func loadMaybeCoins() throws -> Coins? { - if try loadBoolean() { - return try loadCoins() - } - return nil + try loadBoolean() ? try loadCoins() : nil } } @@ -57,7 +54,7 @@ extension Builder { /// Write coins amount in varuint format @discardableResult func store(coins: Coins) throws -> Self { - return try store(varuint: coins.amount, limit: 16) + try store(varuint: coins.amount, limit: 16) } /** diff --git a/Source/TonSwift/Contracts/Message.swift b/Source/TonSwift/Contracts/Message.swift index 2b107a3..1014547 100644 --- a/Source/TonSwift/Contracts/Message.swift +++ b/Source/TonSwift/Contracts/Message.swift @@ -63,7 +63,7 @@ public struct Message: CellCodable { } public static func external(to: Address, stateInit: StateInit?, body: Cell = .empty) -> Message { - return Message( + Message( info: .externalInInfo( info: CommonMsgInfoExternalIn( src: nil, diff --git a/Source/TonSwift/Contracts/MessageRelaxed.swift b/Source/TonSwift/Contracts/MessageRelaxed.swift index a5eaee6..165e732 100644 --- a/Source/TonSwift/Contracts/MessageRelaxed.swift +++ b/Source/TonSwift/Contracts/MessageRelaxed.swift @@ -63,8 +63,14 @@ public struct MessageRelaxed: CellCodable { } } - public static func `internal`(to: Address, value: BigUInt, bounce: Bool = true, stateInit: StateInit? = nil, body: Cell = .empty) -> MessageRelaxed { - return MessageRelaxed( + public static func `internal`( + to: Address, + value: BigUInt, + bounce: Bool = true, + stateInit: StateInit? = nil, + body: Cell = .empty + ) -> MessageRelaxed { + MessageRelaxed( info: .internalInfo( info: CommonMsgInfoRelaxedInternal( @@ -84,8 +90,23 @@ public struct MessageRelaxed: CellCodable { body: body ) } - public static func `internal`(to: Address, value: BigUInt, bounce: Bool = true, stateInit: StateInit? = nil, textPayload: String) throws -> MessageRelaxed { - let body = try Builder().store(int: 0, bits: 32).writeSnakeData(Data(textPayload.utf8)).endCell() - return .internal(to: to, value: value, bounce: bounce, stateInit: stateInit, body: body) + + public static func `internal`( + to: Address, + value: BigUInt, + bounce: Bool = true, + stateInit: StateInit? = nil, + textPayload: String + ) throws -> MessageRelaxed { + .internal( + to: to, + value: value, + bounce: bounce, + stateInit: stateInit, + body: try Builder() + .store(int: 0, bits: 32) + .writeSnakeData(Data(textPayload.utf8)) + .endCell() + ) } } diff --git a/Source/TonSwift/Contracts/SendMode.swift b/Source/TonSwift/Contracts/SendMode.swift index b32de4f..9fe9883 100644 --- a/Source/TonSwift/Contracts/SendMode.swift +++ b/Source/TonSwift/Contracts/SendMode.swift @@ -25,7 +25,7 @@ public struct SendMode { /// and ignore errors so that sequence number can be bumped securely, /// so that bad transactions cannot be replayed indefinitely. static func walletDefault() -> Self { - return SendMode(payMsgFees: true, ignoreErrors: true) + SendMode(payMsgFees: true, ignoreErrors: true) } } diff --git a/Source/TonSwift/Contracts/StateInit.swift b/Source/TonSwift/Contracts/StateInit.swift index 4d42f52..a687158 100644 --- a/Source/TonSwift/Contracts/StateInit.swift +++ b/Source/TonSwift/Contracts/StateInit.swift @@ -76,7 +76,7 @@ struct TickTock: CellCodable { } static func loadFrom(slice: Slice) throws -> TickTock { - return TickTock( + TickTock( tick: try slice.loadBoolean(), tock: try slice.loadBoolean() ) @@ -97,7 +97,7 @@ extension SimpleLibrary: CellCodable { try builder.store(ref: self.root) } public static func loadFrom(slice: Slice) throws -> SimpleLibrary { - return Self( + Self( public: try slice.loadBoolean(), root: try slice.loadRef() ) diff --git a/Source/TonSwift/Keys/PublicKey.swift b/Source/TonSwift/Keys/PublicKey.swift index 9cc9918..a11eea9 100644 --- a/Source/TonSwift/Keys/PublicKey.swift +++ b/Source/TonSwift/Keys/PublicKey.swift @@ -21,7 +21,7 @@ extension PublicKey: CellCodable { } public static func loadFrom(slice: Slice) throws -> PublicKey { - return try slice.tryLoad { s in + try slice.tryLoad { s in let data = try s.loadBytes(.publicKeyLength) return PublicKey(data: data) } diff --git a/Source/TonSwift/Mnemonic/Mnemonic.swift b/Source/TonSwift/Mnemonic/Mnemonic.swift index 57b55b4..e07e912 100644 --- a/Source/TonSwift/Mnemonic/Mnemonic.swift +++ b/Source/TonSwift/Mnemonic/Mnemonic.swift @@ -70,7 +70,7 @@ public enum Mnemonic { - returns: 64 byte entropy */ public static func mnemonicToEntropy(mnemonicArray: [String], password: String = "") -> Data { - return hmacSha512(phrase: mnemonicArray.joined(separator: " "), password: password) + hmacSha512(phrase: mnemonicArray.joined(separator: " "), password: password) } /** @@ -127,7 +127,7 @@ public enum Mnemonic { } public static func normalizeMnemonic(src: [String]) -> [String] { - return src.map({ $0.lowercased() }) + src.map({ $0.lowercased() }) } /** diff --git a/Source/TonSwift/TVM/TupleReader.swift b/Source/TonSwift/TVM/TupleReader.swift index b2a183c..b967ae9 100644 --- a/Source/TonSwift/TVM/TupleReader.swift +++ b/Source/TonSwift/TVM/TupleReader.swift @@ -9,7 +9,7 @@ public class TupleReader { } public var remaining: Int { - return items.count + items.count } public func peek() throws -> Tuple { @@ -76,7 +76,7 @@ public class TupleReader { } public func readAddress() throws -> Address { - return try readCell().beginParse().loadType() + try readCell().beginParse().loadType() } public func readAddressOpt() throws -> Address? { diff --git a/Source/TonSwift/Util/BitsCount.swift b/Source/TonSwift/Util/BitsCount.swift index ddd6b55..790889c 100644 --- a/Source/TonSwift/Util/BitsCount.swift +++ b/Source/TonSwift/Util/BitsCount.swift @@ -30,6 +30,6 @@ extension BigInt { extension Int { public func bitsCount(mode: BitsMode) throws -> Int { - return try BigInt(self).bitsCount(mode: mode) + try BigInt(self).bitsCount(mode: mode) } } diff --git a/Source/TonSwift/Util/Data+Bytes.swift b/Source/TonSwift/Util/Data+Bytes.swift index 6cf8fe0..afdea64 100644 --- a/Source/TonSwift/Util/Data+Bytes.swift +++ b/Source/TonSwift/Util/Data+Bytes.swift @@ -2,6 +2,6 @@ import Foundation extension Data { var bytes: Data { - return Data(self.map({ UInt8($0) })) + Data(map({ UInt8($0) })) } } diff --git a/Tests/TonSwiftTests/Cells/DictionaryTest.swift b/Tests/TonSwiftTests/Cells/DictionaryTest.swift index 27dec5c..b11cd42 100644 --- a/Tests/TonSwiftTests/Cells/DictionaryTest.swift +++ b/Tests/TonSwiftTests/Cells/DictionaryTest.swift @@ -119,11 +119,11 @@ final class DictionaryTest: XCTestCase { } func int2bits(_ i: Int, bits: Int = 16) -> Bitstring { - return try! Builder().store(int: i, bits: bits).bitstring() + try! Builder().store(int: i, bits: bits).bitstring() } func b(_ s: String) -> Bitstring { - return try! Bitstring(binaryString: s) + try! Bitstring(binaryString: s) } private func builderFrom(_ src: String) throws -> Builder { diff --git a/Tests/TonSwiftTests/Wallets/WalletContractV1Test.swift b/Tests/TonSwiftTests/Wallets/WalletContractV1Test.swift index cec326b..526ae5e 100644 --- a/Tests/TonSwiftTests/Wallets/WalletContractV1Test.swift +++ b/Tests/TonSwiftTests/Wallets/WalletContractV1Test.swift @@ -48,7 +48,7 @@ final class WalletContractV1Test: XCTestCase { } private func args() throws -> WalletTransferData { - return try WalletTransferData( + try WalletTransferData( seqno: 62, secretKey: secretKey, messages: [ diff --git a/Tests/TonSwiftTests/Wallets/WalletContractV2Test.swift b/Tests/TonSwiftTests/Wallets/WalletContractV2Test.swift index c22435f..97f16ec 100644 --- a/Tests/TonSwiftTests/Wallets/WalletContractV2Test.swift +++ b/Tests/TonSwiftTests/Wallets/WalletContractV2Test.swift @@ -42,7 +42,7 @@ final class WalletContractV2Test: XCTestCase { } private func args() throws -> WalletTransferData { - return try WalletTransferData( + try WalletTransferData( seqno: 62, secretKey: secretKey, messages: [ diff --git a/Tests/TonSwiftTests/Wallets/WalletContractV3Test.swift b/Tests/TonSwiftTests/Wallets/WalletContractV3Test.swift index 2735181..ab89f46 100644 --- a/Tests/TonSwiftTests/Wallets/WalletContractV3Test.swift +++ b/Tests/TonSwiftTests/Wallets/WalletContractV3Test.swift @@ -42,7 +42,7 @@ final class WalletContractV3Test: XCTestCase { } private func args() throws -> WalletTransferData { - return try WalletTransferData( + try WalletTransferData( seqno: 62, secretKey: secretKey, messages: [ diff --git a/Tests/TonSwiftTests/Wallets/WalletContractV4Test.swift b/Tests/TonSwiftTests/Wallets/WalletContractV4Test.swift index eda7d9e..6e48331 100644 --- a/Tests/TonSwiftTests/Wallets/WalletContractV4Test.swift +++ b/Tests/TonSwiftTests/Wallets/WalletContractV4Test.swift @@ -44,7 +44,7 @@ final class WalletContractV4Test: XCTestCase { } private func args() throws -> WalletTransferData { - return try WalletTransferData( + try WalletTransferData( seqno: 62, secretKey: secretKey, messages: [ From d515a6f83206aa7a48ef48bb50a77cf47b825e37 Mon Sep 17 00:00:00 2001 From: Kirill Kirilenko Date: Thu, 20 Jul 2023 16:02:53 +0400 Subject: [PATCH 03/11] remove useless self. --- Source/TonSwift/Address/ADNLAddress.swift | 2 +- Source/TonSwift/Address/Address.swift | 6 ++-- Source/TonSwift/Address/ExternalAddress.swift | 4 +-- Source/TonSwift/Address/FriendlyAddress.swift | 8 ++--- Source/TonSwift/Cells/Bitstring.swift | 14 ++++---- Source/TonSwift/Cells/Dictionary.swift | 4 +-- Source/TonSwift/Cells/Serialization.swift | 2 +- Source/TonSwift/Cells/Slice.swift | 12 +++---- Source/TonSwift/Cells/SnakeEncoding.swift | 36 ++++++++++--------- Source/TonSwift/Contracts/Coins.swift | 8 ++--- Source/TonSwift/Contracts/StateInit.swift | 16 ++++----- Source/TonSwift/Util/Base32.swift | 4 +-- Source/TonSwift/Util/CRC.swift | 2 +- Source/TonSwift/Util/String+Subscript.swift | 2 +- Source/TonSwift/Wallets/WalletV4.swift | 6 ++-- 15 files changed, 65 insertions(+), 61 deletions(-) diff --git a/Source/TonSwift/Address/ADNLAddress.swift b/Source/TonSwift/Address/ADNLAddress.swift index d76d1e3..72f829c 100644 --- a/Source/TonSwift/Address/ADNLAddress.swift +++ b/Source/TonSwift/Address/ADNLAddress.swift @@ -39,7 +39,7 @@ struct ADNLAddress { } func toString() -> String { - var data = Data([0x2D]) + self.address + var data = Data([0x2D]) + address let hash = data.crc16() data = data + hash diff --git a/Source/TonSwift/Address/Address.swift b/Source/TonSwift/Address/Address.swift index 7d4f643..273b321 100644 --- a/Source/TonSwift/Address/Address.swift +++ b/Source/TonSwift/Address/Address.swift @@ -47,7 +47,7 @@ public struct Address: Hashable, Codable { /// Shortcut for constructing FriendlyAddress with all the options. public func toString(urlSafe: Bool = true, testOnly: Bool = false, bounceable: Bool = BounceableDefault) -> String { - self.toFriendly(testOnly: testOnly, bounceable: bounceable).toString(urlSafe: urlSafe) + toFriendly(testOnly: testOnly, bounceable: bounceable).toString(urlSafe: urlSafe) } } @@ -76,8 +76,8 @@ extension Address: CellCodable, StaticSize { public func storeTo(builder b: Builder) throws { try b.store(uint: 2, bits: 2) // $10 try b.store(uint: 0, bits: 1) - try b.store(int: self.workchain, bits: 8) - try b.store(data: self.hash) + try b.store(int: workchain, bits: 8) + try b.store(data: hash) } public static func loadFrom(slice: Slice) throws -> Address { diff --git a/Source/TonSwift/Address/ExternalAddress.swift b/Source/TonSwift/Address/ExternalAddress.swift index a4c737d..7afcb91 100644 --- a/Source/TonSwift/Address/ExternalAddress.swift +++ b/Source/TonSwift/Address/ExternalAddress.swift @@ -23,8 +23,8 @@ public struct ExternalAddress { extension ExternalAddress: CellCodable { public func storeTo(builder: Builder) throws { try builder.store(uint: 1, bits: 2) - try builder.store(uint: self.value.length, bits: 9) - try builder.store(bits: self.value) + try builder.store(uint: value.length, bits: 9) + try builder.store(bits: value) } public static func loadFrom(slice: Slice) throws -> ExternalAddress { diff --git a/Source/TonSwift/Address/FriendlyAddress.swift b/Source/TonSwift/Address/FriendlyAddress.swift index 39df1e7..d095fd1 100644 --- a/Source/TonSwift/Address/FriendlyAddress.swift +++ b/Source/TonSwift/Address/FriendlyAddress.swift @@ -13,8 +13,8 @@ public struct FriendlyAddress: Codable { public let isBounceable: Bool public let address: Address - var workchain: Int8 { self.address.workchain } - var hash: Data { self.address.hash } + var workchain: Int8 { address.workchain } + var hash: Data { address.hash } init(string: String) throws { // Convert from url-friendly to true base64 @@ -76,10 +76,10 @@ public struct FriendlyAddress: Codable { } var wcByte: UInt8 - if self.address.workchain == -1 { + if address.workchain == -1 { wcByte = UInt8.max } else { - wcByte = UInt8(self.workchain) + wcByte = UInt8(workchain) } var addr = Data(count: 34) diff --git a/Source/TonSwift/Cells/Bitstring.swift b/Source/TonSwift/Cells/Bitstring.swift index 3da6f9c..86bc2a3 100644 --- a/Source/TonSwift/Cells/Bitstring.swift +++ b/Source/TonSwift/Cells/Bitstring.swift @@ -151,15 +151,15 @@ public struct Bitstring: Hashable { /// Drops first `n` bits from the bitstring. public func dropFirst(_ n: Int) throws -> Bitstring { - try substring(offset: n, length: self.length - n) + try substring(offset: n, length: length - n) } /// Formats the bitstring as a hex-encoded string with a `_` trailing symbol indicating `10*` padding to 4-bit alignment. public func toHex() -> String { - let padded = Data(self.bitsToPaddedBuffer()) + let padded = Data(bitsToPaddedBuffer()) if _length % 4 == 0 { - let s = padded[0..<(self._length + 7) / 8].hexString().uppercased() + let s = padded[0..<(_length + 7) / 8].hexString().uppercased() if _length % 8 == 0 { return s } else { @@ -196,19 +196,19 @@ public struct Bitstring: Hashable { } public func padLeft(_ n: Int = 0) -> Bitstring { - let cap = max(n, self.length) + let cap = max(n, length) let b = Builder(capacity: cap) - try! b.store(bit: 0, repeat: cap - self.length) + try! b.store(bit: 0, repeat: cap - length) try! b.store(bits: self) return try! b.endCell().bits } /// Pads bitstring with `10*` bits. public func bitsToPaddedBuffer() -> Data { - let builder = Builder(capacity: (self.length + 7) / 8 * 8) + let builder = Builder(capacity: (length + 7) / 8 * 8) try! builder.store(bits: self) - let padding = (self.length + 7) / 8 * 8 - self.length + let padding = (length + 7) / 8 * 8 - length for i in 0.. T { - try src.loadBytes(self.size) + try src.loadBytes(size) } } diff --git a/Source/TonSwift/Cells/Slice.swift b/Source/TonSwift/Cells/Slice.swift index 8b8be7d..ecc7714 100644 --- a/Source/TonSwift/Cells/Slice.swift +++ b/Source/TonSwift/Cells/Slice.swift @@ -111,7 +111,7 @@ public class Slice { /// Preloads type T that implements interface Readable public func preloadType() throws -> T { - try T.loadFrom(slice: self.clone()) + try T.loadFrom(slice: clone()) } /// Loads optional type T via closure. Function reads one bit that indicates the presence of data. If the bit is set, the closure is called to read T. @@ -123,11 +123,11 @@ public class Slice { /// If parsing succeeded, the slice is advanced. /// If parsing failed, the slice remains unchanged. public func tryLoad(_ closure: (Slice) throws -> T) throws -> T { - let tmpslice = self.clone() + let tmpslice = clone() let result = try closure(tmpslice) - self.bitstring = tmpslice.bitstring - self.offset = tmpslice.offset - self.refs = tmpslice.refs + bitstring = tmpslice.bitstring + offset = tmpslice.offset + refs = tmpslice.refs return result } @@ -368,7 +368,7 @@ public class Slice { if limit > 9 { throw TonError.custom("VarUInteger \(limit) cannot store UInt64 (it occupies 8 bytes, so the largest type is VarUInteger 9)") } - return try UInt64(self.loadVarUintBig(limit: limit)) + return try UInt64(loadVarUintBig(limit: limit)) } /// Loads VarUInteger with a given `limit` in bytes. diff --git a/Source/TonSwift/Cells/SnakeEncoding.swift b/Source/TonSwift/Cells/SnakeEncoding.swift index 74c5bb3..de9ce33 100644 --- a/Source/TonSwift/Cells/SnakeEncoding.swift +++ b/Source/TonSwift/Cells/SnakeEncoding.swift @@ -4,7 +4,7 @@ extension Slice { /// Loads snake-encoded String. /// Fails if the string is malformed or not a valid UTF-8 string. public func loadSnakeString() throws -> String { - guard let str = String(data: try self.loadSnakeData(), encoding: .utf8) else { + guard let str = String(data: try loadSnakeData(), encoding: .utf8) else { throw TonError.custom("Cannot read slice to string") } return str @@ -13,27 +13,31 @@ extension Slice { /// Loads snake-encoded Data. Fails if the binary string is malformed. public func loadSnakeData() throws -> Data { // Check consistency - if self.remainingBits % 8 != 0 { - throw TonError.custom("Invalid string length: \(self.remainingBits)") + if remainingBits % 8 != 0 { + throw TonError.custom("Invalid string length: \(remainingBits)") } - if self.remainingRefs != 0 && self.remainingRefs != 1 { - throw TonError.custom("Invalid number of refs: \(self.remainingRefs)") + if remainingRefs != 0 && remainingRefs != 1 { + throw TonError.custom("Invalid number of refs: \(remainingRefs)") } - if self.remainingRefs == 1 && (BitsPerCell - self.remainingBits) > 7 { - throw TonError.custom("Invalid string length: \(self.remainingBits / 8)") + if remainingRefs == 1 && (BitsPerCell - remainingBits) > 7 { + throw TonError.custom("Invalid string length: \(remainingBits / 8)") } // Read string var res = Data() - if self.remainingBits == 0 { + if remainingBits == 0 { res = Data() } else { - res = try self.loadBytes(self.remainingBits / 8) + res = try loadBytes(remainingBits / 8) } // Read tail - if self.remainingRefs == 1 { - res.append(try self.loadRef().beginParse().loadSnakeData()) + if remainingRefs == 1 { + res.append( + try loadRef() + .beginParse() + .loadSnakeData() + ) } return res @@ -45,15 +49,15 @@ extension Builder { @discardableResult public func writeSnakeData(_ src: Data) throws -> Self { if src.count > 0 { - let bytes = Int(floor(Double(self.availableBits / 8))) + let bytes = Int(floor(Double(availableBits / 8))) if src.count > bytes { let a = src.subdata(in: 0.. Cell { - try Builder().writeSnakeData(Data(self.utf8)).endCell() + try Builder().writeSnakeData(Data(utf8)).endCell() } } diff --git a/Source/TonSwift/Contracts/Coins.swift b/Source/TonSwift/Contracts/Coins.swift index 1514a32..3b140f8 100644 --- a/Source/TonSwift/Contracts/Coins.swift +++ b/Source/TonSwift/Contracts/Coins.swift @@ -7,7 +7,7 @@ public struct Coins { init(_ a: some BinaryInteger) { // we use signed integer here because of `0` literal is a signed Int. - self.amount = BigUInt(a) + amount = BigUInt(a) } } @@ -15,17 +15,17 @@ extension Coins: RawRepresentable { public typealias RawValue = BigUInt public init?(rawValue: BigUInt) { - self.amount = rawValue + amount = rawValue } public var rawValue: BigUInt { - self.amount + amount } } extension Coins: CellCodable { public func storeTo(builder: Builder) throws { - try builder.store(varuint: self.amount, limit: 16) + try builder.store(varuint: amount, limit: 16) } public static func loadFrom(slice: Slice) throws -> Coins { Coins(try slice.loadVarUintBig(limit: 16)) diff --git a/Source/TonSwift/Contracts/StateInit.swift b/Source/TonSwift/Contracts/StateInit.swift index a687158..21ac075 100644 --- a/Source/TonSwift/Contracts/StateInit.swift +++ b/Source/TonSwift/Contracts/StateInit.swift @@ -26,23 +26,23 @@ public struct StateInit: CellCodable { } public func storeTo(builder: Builder) throws { - if let splitDepth = self.splitDepth { + if let splitDepth { try builder.store(bit: true) try builder.store(uint: UInt64(splitDepth), bits: 5) } else { try builder.store(bit: false) } - if let ticktock = self.special { + if let ticktock = special { try builder.store(bit: true) try builder.store(ticktock) } else { try builder.store(bit: false) } - try builder.storeMaybe(ref: self.code) - try builder.storeMaybe(ref: self.data) - try builder.store(self.libraries) + try builder.storeMaybe(ref: code) + try builder.storeMaybe(ref: data) + try builder.store(libraries) } static public func loadFrom(slice: Slice) throws -> StateInit { @@ -71,8 +71,8 @@ struct TickTock: CellCodable { var tock: Bool func storeTo(builder: Builder) throws { - try builder.store(bit: self.tick) - try builder.store(bit: self.tock) + try builder.store(bit: tick) + try builder.store(bit: tock) } static func loadFrom(slice: Slice) throws -> TickTock { @@ -94,7 +94,7 @@ public struct SimpleLibrary: Hashable { extension SimpleLibrary: CellCodable { public func storeTo(builder: Builder) throws { try builder.store(bit: self.public) - try builder.store(ref: self.root) + try builder.store(ref: root) } public static func loadFrom(slice: Slice) throws -> SimpleLibrary { Self( diff --git a/Source/TonSwift/Util/Base32.swift b/Source/TonSwift/Util/Base32.swift index 8e10ceb..70ebf0f 100644 --- a/Source/TonSwift/Util/Base32.swift +++ b/Source/TonSwift/Util/Base32.swift @@ -5,7 +5,7 @@ fileprivate let alphabet = "abcdefghijklmnopqrstuvwxyz234567" extension Data { /// Encodes data to base32 format public func toBase32() -> String { - let length = self.count + let length = count var bits = 0 var value = 0 var output = "" @@ -28,7 +28,7 @@ extension Data { extension String { func fromBase32() throws -> Data { - let cleanedInput = self.lowercased() + let cleanedInput = lowercased() let length = cleanedInput.count var bits = 0 var value = 0 diff --git a/Source/TonSwift/Util/CRC.swift b/Source/TonSwift/Util/CRC.swift index 11c538e..c43ac1d 100644 --- a/Source/TonSwift/Util/CRC.swift +++ b/Source/TonSwift/Util/CRC.swift @@ -34,7 +34,7 @@ extension Data { let poly: UInt32 = 0x82f63b78 var crc: UInt32 = 0 ^ 0xffffffff - for i in 0..> 1) ^ poly : crc >> 1 crc = ((crc & 1) != 0) ? (crc >> 1) ^ poly : crc >> 1 diff --git a/Source/TonSwift/Util/String+Subscript.swift b/Source/TonSwift/Util/String+Subscript.swift index 76431c3..0afa0bb 100644 --- a/Source/TonSwift/Util/String+Subscript.swift +++ b/Source/TonSwift/Util/String+Subscript.swift @@ -2,6 +2,6 @@ import Foundation extension String { public subscript(_ idx: Int) -> Character { - self[self.index(self.startIndex, offsetBy: idx)] + self[index(startIndex, offsetBy: idx)] } } diff --git a/Source/TonSwift/Wallets/WalletV4.swift b/Source/TonSwift/Wallets/WalletV4.swift index 259a1b4..70b17af 100644 --- a/Source/TonSwift/Wallets/WalletV4.swift +++ b/Source/TonSwift/Wallets/WalletV4.swift @@ -58,16 +58,16 @@ public class WalletV4: WalletContract { public var stateInit: StateInit { let data = try! Builder() .store(uint: 0, bits: 32) // initial seqno = 0 - .store(uint: self.walletId, bits: 32) + .store(uint: walletId, bits: 32) .store(data: publicKey) .store(dict: Set()) // initial plugins list = [] .endCell() - return StateInit(code: self.code, data: data) + return StateInit(code: code, data: data) } func pluginsCompact() -> Set { - Set(self.plugins.map{ a in CompactAddress(a) }) + Set(plugins.map{ a in CompactAddress(a) }) } public func createTransfer(args: WalletTransferData) throws -> Cell { From c0c157d37ac04e341dc18eb7f03d4177794426a1 Mon Sep 17 00:00:00 2001 From: Kirill Kirilenko Date: Thu, 20 Jul 2023 16:19:51 +0400 Subject: [PATCH 04/11] make builders normal --- Source/TonSwift/Address/Address.swift | 18 +++++---- Source/TonSwift/Address/ExternalAddress.swift | 7 ++-- Source/TonSwift/Cells/Bitstring.swift | 18 +++++---- Source/TonSwift/Cells/Boc.swift | 26 ++++++------- Source/TonSwift/Cells/Dictionary.swift | 10 ++--- Source/TonSwift/Cells/Serialization.swift | 6 +-- Source/TonSwift/Cells/Slice.swift | 5 +-- Source/TonSwift/Contracts/CommonMsgInfo.swift | 38 +++++++++---------- .../Contracts/CommonMsgInfoRelaxed.swift | 30 +++++++-------- .../Contracts/CurrencyCollection.swift | 2 +- Source/TonSwift/Contracts/Message.swift | 8 ++-- .../TonSwift/Contracts/MessageRelaxed.swift | 8 ++-- Source/TonSwift/Contracts/StateInit.swift | 12 +++--- Source/TonSwift/Wallets/WalletV1.swift | 9 ++--- Source/TonSwift/Wallets/WalletV2.swift | 9 ++--- Source/TonSwift/Wallets/WalletV3.swift | 9 ++--- Source/TonSwift/Wallets/WalletV4.swift | 9 ++--- 17 files changed, 112 insertions(+), 112 deletions(-) diff --git a/Source/TonSwift/Address/Address.swift b/Source/TonSwift/Address/Address.swift index 273b321..67d7148 100644 --- a/Source/TonSwift/Address/Address.swift +++ b/Source/TonSwift/Address/Address.swift @@ -73,11 +73,12 @@ extension Address: CellCodable, StaticSize { public static var bitWidth: Int = 267 - public func storeTo(builder b: Builder) throws { - try b.store(uint: 2, bits: 2) // $10 - try b.store(uint: 0, bits: 1) - try b.store(int: workchain, bits: 8) - try b.store(data: hash) + public func storeTo(builder: Builder) throws { + try builder + .store(uint: 2, bits: 2) + .store(uint: 0, bits: 1) + .store(int: workchain, bits: 8) + .store(data: hash) } public static func loadFrom(slice: Slice) throws -> Address { @@ -111,9 +112,10 @@ public struct CompactAddress: Hashable, CellCodable, StaticSize { self.inner = inner } - public func storeTo(builder b: Builder) throws { - try b.store(int: inner.workchain, bits: 8) - try b.store(data: inner.hash) + public func storeTo(builder: Builder) throws { + try builder + .store(int: inner.workchain, bits: 8) + .store(data: inner.hash) } public static func loadFrom(slice: Slice) throws -> CompactAddress { diff --git a/Source/TonSwift/Address/ExternalAddress.swift b/Source/TonSwift/Address/ExternalAddress.swift index 7afcb91..e41f244 100644 --- a/Source/TonSwift/Address/ExternalAddress.swift +++ b/Source/TonSwift/Address/ExternalAddress.swift @@ -22,9 +22,10 @@ public struct ExternalAddress { extension ExternalAddress: CellCodable { public func storeTo(builder: Builder) throws { - try builder.store(uint: 1, bits: 2) - try builder.store(uint: value.length, bits: 9) - try builder.store(bits: value) + try builder + .store(uint: 1, bits: 2) + .store(uint: value.length, bits: 9) + .store(bits: value) } public static func loadFrom(slice: Slice) throws -> ExternalAddress { diff --git a/Source/TonSwift/Cells/Bitstring.swift b/Source/TonSwift/Cells/Bitstring.swift index 86bc2a3..9ad1cd3 100644 --- a/Source/TonSwift/Cells/Bitstring.swift +++ b/Source/TonSwift/Cells/Bitstring.swift @@ -58,8 +58,9 @@ public struct Bitstring: Hashable { /// Constructs BitString from a binary string of 1s and 0s. public init(binaryString: String) throws { - let cell = try Builder().store(binaryString: binaryString).endCell() - self = cell.bits + self = try Builder() + .store(binaryString: binaryString) + .endCell().bits } @@ -197,16 +198,17 @@ public struct Bitstring: Hashable { public func padLeft(_ n: Int = 0) -> Bitstring { let cap = max(n, length) - let b = Builder(capacity: cap) - try! b.store(bit: 0, repeat: cap - length) - try! b.store(bits: self) - return try! b.endCell().bits + return try! Builder(capacity: cap) + .store(bit: 0, repeat: cap - length) + .store(bits: self) + .endCell() + .bits } /// Pads bitstring with `10*` bits. public func bitsToPaddedBuffer() -> Data { - let builder = Builder(capacity: (length + 7) / 8 * 8) - try! builder.store(bits: self) + let builder = try! Builder(capacity: (length + 7) / 8 * 8) + .store(bits: self) let padding = (length + 7) / 8 * 8 - length for i in 0.. Data { ) * 8 // Serialize - var builder = Builder(capacity: totalSize) - try builder.store(uint: 0xb5ee9c72, bits: 32) // Magic - try builder.store(bit: hasIdx) // Has index - try builder.store(bit: hasCrc32c) // Has crc32c - try builder.store(bit: hasCacheBits) // Has cache bits - try builder.store(uint: flags, bits: 2) // Flags - try builder.store(uint: sizeBytes, bits: 3) // Size bytes - try builder.store(uint: offsetBytes, bits: 8) // Offset bytes - try builder.store(uint: cellsNum, bits: sizeBytes * 8) // Cells num - try builder.store(uint: 1, bits: sizeBytes * 8) // Roots num - try builder.store(uint: 0, bits: sizeBytes * 8) // Absent num - try builder.store(uint: totalCellSize, bits: offsetBytes * 8) // Total cell size - try builder.store(uint: 0, bits: sizeBytes * 8) // Root id == 0 + var builder = try Builder(capacity: totalSize) + .store(uint: 0xb5ee9c72, bits: 32) // Magic + .store(bit: hasIdx) // Has index + .store(bit: hasCrc32c) // Has crc32c + .store(bit: hasCacheBits) // Has cache bits + .store(uint: flags, bits: 2) // Flags + .store(uint: sizeBytes, bits: 3) // Size bytes + .store(uint: offsetBytes, bits: 8) // Offset bytes + .store(uint: cellsNum, bits: sizeBytes * 8) // Cells num + .store(uint: 1, bits: sizeBytes * 8) // Roots num + .store(uint: 0, bits: sizeBytes * 8) // Absent num + .store(uint: totalCellSize, bits: offsetBytes * 8) // Total cell size + .store(uint: 0, bits: sizeBytes * 8) // Root id == 0 if hasIdx { for i in 0 ..< cellsNum { diff --git a/Source/TonSwift/Cells/Dictionary.swift b/Source/TonSwift/Cells/Dictionary.swift index 5dd2e53..30d8ea8 100644 --- a/Source/TonSwift/Cells/Dictionary.swift +++ b/Source/TonSwift/Cells/Dictionary.swift @@ -115,12 +115,12 @@ public class DictionaryCoder where K.T: Hashable { // Serialize keys var paddedMap: [Bitstring: V.T] = [:] - for (k, v) in map { - let b = Builder() - try keyCoder.storeValue(k, to: b) - let keybits = b.bitstring() + for (key, value) in map { + let builder = Builder() + try keyCoder.storeValue(key, to: builder) + let keybits = builder.bitstring() let paddedKey = keybits.padLeft(keyLength) - paddedMap[paddedKey] = v + paddedMap[paddedKey] = value } // Calculate root label diff --git a/Source/TonSwift/Cells/Serialization.swift b/Source/TonSwift/Cells/Serialization.swift index 1ecc35c..644c8d5 100644 --- a/Source/TonSwift/Cells/Serialization.swift +++ b/Source/TonSwift/Cells/Serialization.swift @@ -39,9 +39,9 @@ public class DefaultCoder: TypeCoder { public extension TypeCoder { /// Serializes type to Cell func serializeToCell(_ src: T) throws -> Cell { - let b = Builder() - try storeValue(src, to: b) - return try b.endCell() + let builder = Builder() + try storeValue(src, to: builder) + return try builder.endCell() } } diff --git a/Source/TonSwift/Cells/Slice.swift b/Source/TonSwift/Cells/Slice.swift index ecc7714..c06d64e 100644 --- a/Source/TonSwift/Cells/Slice.swift +++ b/Source/TonSwift/Cells/Slice.swift @@ -83,9 +83,8 @@ public class Slice { /// Converts slice to a Builder filled with remaining data in this slice. public func toBuilder() throws -> Builder { - let builder = Builder() - try builder.store(slice: self) - return builder + try Builder() + .store(slice: self) } /// Clones slice at its current state. diff --git a/Source/TonSwift/Contracts/CommonMsgInfo.swift b/Source/TonSwift/Contracts/CommonMsgInfo.swift index 2b4084f..7773ca0 100644 --- a/Source/TonSwift/Contracts/CommonMsgInfo.swift +++ b/Source/TonSwift/Contracts/CommonMsgInfo.swift @@ -118,31 +118,31 @@ public enum CommonMsgInfo: CellCodable { switch self { case .internalInfo(let info): try builder.store(bit: 0) - try builder.store(bit: info.ihrDisabled) - try builder.store(bit: info.bounce) - try builder.store(bit: info.bounced) - try builder.store(AnyAddress(info.src)) - try builder.store(AnyAddress(info.dest)) - try builder.store(info.value) - try builder.store(coins: info.ihrFee) - try builder.store(coins: info.forwardFee) - try builder.store(uint: info.createdLt, bits: 64) - try builder.store(uint: UInt64(info.createdAt), bits: 32) + .store(bit: info.ihrDisabled) + .store(bit: info.bounce) + .store(bit: info.bounced) + .store(AnyAddress(info.src)) + .store(AnyAddress(info.dest)) + .store(info.value) + .store(coins: info.ihrFee) + .store(coins: info.forwardFee) + .store(uint: info.createdLt, bits: 64) + .store(uint: UInt64(info.createdAt), bits: 32) case .externalOutInfo(let info): try builder.store(bit: 1) - try builder.store(bit: 1) - try builder.store(AnyAddress(info.src)) - try builder.store(AnyAddress(info.dest)) - try builder.store(uint: info.createdLt, bits: 64) - try builder.store(uint: UInt64(info.createdAt), bits: 32) + .store(bit: 1) + .store(AnyAddress(info.src)) + .store(AnyAddress(info.dest)) + .store(uint: info.createdLt, bits: 64) + .store(uint: UInt64(info.createdAt), bits: 32) case .externalInInfo(let info): try builder.store(bit: true) - try builder.store(bit: false) - try builder.store(AnyAddress(info.src)) - try builder.store(AnyAddress(info.dest)) - try builder.store(coins: info.importFee) + .store(bit: false) + .store(AnyAddress(info.src)) + .store(AnyAddress(info.dest)) + .store(coins: info.importFee) } } } diff --git a/Source/TonSwift/Contracts/CommonMsgInfoRelaxed.swift b/Source/TonSwift/Contracts/CommonMsgInfoRelaxed.swift index a15a612..c0e1461 100644 --- a/Source/TonSwift/Contracts/CommonMsgInfoRelaxed.swift +++ b/Source/TonSwift/Contracts/CommonMsgInfoRelaxed.swift @@ -101,24 +101,24 @@ public enum CommonMsgInfoRelaxed: CellCodable { switch self { case .internalInfo(let info): try builder.store(bit: false) - try builder.store(bit: info.ihrDisabled) - try builder.store(bit: info.bounce) - try builder.store(bit: info.bounced) - try builder.store(info.src) - try builder.store(AnyAddress(info.dest)) - try builder.store(info.value) - try builder.store(coins: info.ihrFee) - try builder.store(coins: info.forwardFee) - try builder.store(uint: info.createdLt, bits: 64) - try builder.store(uint: UInt64(info.createdAt), bits: 32) + .store(bit: info.ihrDisabled) + .store(bit: info.bounce) + .store(bit: info.bounced) + .store(info.src) + .store(AnyAddress(info.dest)) + .store(info.value) + .store(coins: info.ihrFee) + .store(coins: info.forwardFee) + .store(uint: info.createdLt, bits: 64) + .store(uint: UInt64(info.createdAt), bits: 32) case .externalOutInfo(let info): try builder.store(bit:true) - try builder.store(bit:true) - try builder.store(info.src) - try builder.store(AnyAddress(info.dest)) - try builder.store(uint: info.createdLt, bits: 64) - try builder.store(uint: UInt64(info.createdAt), bits: 32) + .store(bit:true) + .store(info.src) + .store(AnyAddress(info.dest)) + .store(uint: info.createdLt, bits: 64) + .store(uint: UInt64(info.createdAt), bits: 32) } } } diff --git a/Source/TonSwift/Contracts/CurrencyCollection.swift b/Source/TonSwift/Contracts/CurrencyCollection.swift index 7245814..292ae62 100644 --- a/Source/TonSwift/Contracts/CurrencyCollection.swift +++ b/Source/TonSwift/Contracts/CurrencyCollection.swift @@ -30,6 +30,6 @@ struct CurrencyCollection: CellCodable { func storeTo(builder: Builder) throws { try builder.store(coins: coins) - try builder.store(other) + .store(other) } } diff --git a/Source/TonSwift/Contracts/Message.swift b/Source/TonSwift/Contracts/Message.swift index 1014547..6423e02 100644 --- a/Source/TonSwift/Contracts/Message.swift +++ b/Source/TonSwift/Contracts/Message.swift @@ -44,10 +44,10 @@ public struct Message: CellCodable { // check if we fit the cell inline with 2 bits for the stateinit and the body if let space = builder.fit(initCell.metrics), space.bitsCount >= 2 { try builder.store(bit: 0) - try builder.store(initCell) + .store(initCell) } else { try builder.store(bit: 1) - try builder.store(ref:initCell) + .store(ref:initCell) } } else { try builder.store(bit:0) @@ -55,10 +55,10 @@ public struct Message: CellCodable { if let space = builder.fit(body.metrics), space.bitsCount >= 1 { try builder.store(bit: 0) - try builder.store(body.toBuilder()) + .store(body.toBuilder()) } else { try builder.store(bit: 1) - try builder.store(ref:body) + .store(ref:body) } } diff --git a/Source/TonSwift/Contracts/MessageRelaxed.swift b/Source/TonSwift/Contracts/MessageRelaxed.swift index 165e732..8141993 100644 --- a/Source/TonSwift/Contracts/MessageRelaxed.swift +++ b/Source/TonSwift/Contracts/MessageRelaxed.swift @@ -45,10 +45,10 @@ public struct MessageRelaxed: CellCodable { // check if we fit the cell inline with 2 bits for the stateinit and the body if let space = builder.fit(initCell.metrics), space.bitsCount >= 2 { try builder.store(bit: 0) - try builder.store(initCell) + .store(initCell) } else { try builder.store(bit: 1) - try builder.store(ref:initCell) + .store(ref:initCell) } } else { try builder.store(bit: 0) @@ -56,10 +56,10 @@ public struct MessageRelaxed: CellCodable { if let space = builder.fit(body.metrics), space.bitsCount >= 1 { try builder.store(bit: 0) - try builder.store(body.toBuilder()) + .store(body.toBuilder()) } else { try builder.store(bit: 1) - try builder.store(ref: body) + .store(ref: body) } } diff --git a/Source/TonSwift/Contracts/StateInit.swift b/Source/TonSwift/Contracts/StateInit.swift index 21ac075..2bb5518 100644 --- a/Source/TonSwift/Contracts/StateInit.swift +++ b/Source/TonSwift/Contracts/StateInit.swift @@ -28,21 +28,21 @@ public struct StateInit: CellCodable { public func storeTo(builder: Builder) throws { if let splitDepth { try builder.store(bit: true) - try builder.store(uint: UInt64(splitDepth), bits: 5) + .store(uint: UInt64(splitDepth), bits: 5) } else { try builder.store(bit: false) } if let ticktock = special { try builder.store(bit: true) - try builder.store(ticktock) + .store(ticktock) } else { try builder.store(bit: false) } try builder.storeMaybe(ref: code) - try builder.storeMaybe(ref: data) - try builder.store(libraries) + .storeMaybe(ref: data) + .store(libraries) } static public func loadFrom(slice: Slice) throws -> StateInit { @@ -72,7 +72,7 @@ struct TickTock: CellCodable { func storeTo(builder: Builder) throws { try builder.store(bit: tick) - try builder.store(bit: tock) + .store(bit: tock) } static func loadFrom(slice: Slice) throws -> TickTock { @@ -94,7 +94,7 @@ public struct SimpleLibrary: Hashable { extension SimpleLibrary: CellCodable { public func storeTo(builder: Builder) throws { try builder.store(bit: self.public) - try builder.store(ref: root) + .store(ref: root) } public static func loadFrom(slice: Slice) throws -> SimpleLibrary { Self( diff --git a/Source/TonSwift/Wallets/WalletV1.swift b/Source/TonSwift/Wallets/WalletV1.swift index 68087d0..997c00b 100644 --- a/Source/TonSwift/Wallets/WalletV1.swift +++ b/Source/TonSwift/Wallets/WalletV1.swift @@ -46,10 +46,9 @@ public final class WalletV1: WalletContract { let signature = try NaclSign.sign(message: signingMessage.endCell().hash(), secretKey: args.secretKey) - let body = Builder() - try body.store(data: signature) - try body.store(signingMessage) - - return try body.endCell() + return try Builder() + .store(data: signature) + .store(signingMessage) + .endCell() } } diff --git a/Source/TonSwift/Wallets/WalletV2.swift b/Source/TonSwift/Wallets/WalletV2.swift index 493307d..0bdae9c 100644 --- a/Source/TonSwift/Wallets/WalletV2.swift +++ b/Source/TonSwift/Wallets/WalletV2.swift @@ -49,10 +49,9 @@ public final class WalletV2: WalletContract { let signature = try NaclSign.sign(message: signingMessage.endCell().hash(), secretKey: args.secretKey) - let body = Builder() - try body.store(data: signature) - try body.store(signingMessage) - - return try body.endCell() + return try Builder() + .store(data: signature) + .store(signingMessage) + .endCell() } } diff --git a/Source/TonSwift/Wallets/WalletV3.swift b/Source/TonSwift/Wallets/WalletV3.swift index 5718c31..0732577 100644 --- a/Source/TonSwift/Wallets/WalletV3.swift +++ b/Source/TonSwift/Wallets/WalletV3.swift @@ -59,10 +59,9 @@ public final class WalletV3: WalletContract { let signature = try NaclSign.sign(message: signingMessage.endCell().hash(), secretKey: args.secretKey) - let body = Builder() - try body.store(data: signature) - try body.store(signingMessage) - - return try body.endCell() + return try Builder() + .store(data: signature) + .store(signingMessage) + .endCell() } } diff --git a/Source/TonSwift/Wallets/WalletV4.swift b/Source/TonSwift/Wallets/WalletV4.swift index 70b17af..9b46bfd 100644 --- a/Source/TonSwift/Wallets/WalletV4.swift +++ b/Source/TonSwift/Wallets/WalletV4.swift @@ -88,10 +88,9 @@ public class WalletV4: WalletContract { let signature = try NaclSign.sign(message: signingMessage.endCell().hash(), secretKey: args.secretKey) - let body = Builder() - try body.store(data: signature) - try body.store(signingMessage) - - return try body.endCell() + return try Builder() + .store(data: signature) + .store(signingMessage) + .endCell() } } From 74776e7929273a60873b11ebad751b6d4dcb010d Mon Sep 17 00:00:00 2001 From: Kirill Kirilenko Date: Thu, 20 Jul 2023 16:48:44 +0400 Subject: [PATCH 05/11] remove useless extensions + divided big files in Cells --- Source/TonSwift/Address/ADNLAddress.swift | 6 +- Source/TonSwift/Address/Address.swift | 57 ++-- Source/TonSwift/Address/AnyAddress.swift | 6 +- Source/TonSwift/Address/CompactAddress.swift | 32 +++ Source/TonSwift/Address/ExternalAddress.swift | 4 +- Source/TonSwift/Cells/Bitstring.swift | 23 +- Source/TonSwift/Cells/Boc.swift | 8 +- Source/TonSwift/Cells/BocMagic.swift | 12 + Source/TonSwift/Cells/Builder.swift | 77 ++---- Source/TonSwift/Cells/Cell/BasicCell.swift | 157 +++++++++++ Source/TonSwift/Cells/{ => Cell}/Cell.swift | 224 +--------------- Source/TonSwift/Cells/Cell/CellMetrics.swift | 14 + Source/TonSwift/Cells/Cell/CellType.swift | 13 + Source/TonSwift/Cells/Integers.swift | 248 ------------------ Source/TonSwift/Cells/Integers/Bit.swift | 10 + .../Cells/Integers/Coders/IntCoder.swift | 26 ++ .../Cells/Integers/Coders/UIntCoder.swift | 26 ++ .../Cells/Integers/Coders/VarUIntCoder.swift | 33 +++ .../DynamicallySized/VarUInt120.swift | 19 ++ .../DynamicallySized/VarUInt248.swift | 19 ++ .../Integers/Signed/Int16+CellCodable.swift | 18 ++ .../Integers/Signed/Int32+CellCodable.swift | 18 ++ .../Integers/Signed/Int64+CellCodable.swift | 18 ++ .../Integers/Signed/Int8+CellCodable.swift | 18 ++ Source/TonSwift/Cells/Integers/Unary.swift | 28 ++ .../Unsigned/UInt16+CellCodable.swift | 18 ++ .../Cells/Integers/Unsigned/UInt256.swift | 27 ++ .../Unsigned/UInt32+CellCodable.swift | 18 ++ .../Unsigned/UInt64+CellCodable.swift | 18 ++ .../Integers/Unsigned/UInt8+CellCodable.swift | 18 ++ Source/TonSwift/Cells/LevelMask.swift | 56 ++++ Source/TonSwift/Util/Bool+CellCodable.swift | 18 ++ Source/TonSwift/Util/Data+FromHex.swift | 9 +- 33 files changed, 694 insertions(+), 602 deletions(-) create mode 100644 Source/TonSwift/Address/CompactAddress.swift create mode 100644 Source/TonSwift/Cells/BocMagic.swift create mode 100644 Source/TonSwift/Cells/Cell/BasicCell.swift rename Source/TonSwift/Cells/{ => Cell}/Cell.swift (65%) create mode 100644 Source/TonSwift/Cells/Cell/CellMetrics.swift create mode 100644 Source/TonSwift/Cells/Cell/CellType.swift delete mode 100644 Source/TonSwift/Cells/Integers.swift create mode 100644 Source/TonSwift/Cells/Integers/Bit.swift create mode 100644 Source/TonSwift/Cells/Integers/Coders/IntCoder.swift create mode 100644 Source/TonSwift/Cells/Integers/Coders/UIntCoder.swift create mode 100644 Source/TonSwift/Cells/Integers/Coders/VarUIntCoder.swift create mode 100644 Source/TonSwift/Cells/Integers/DynamicallySized/VarUInt120.swift create mode 100644 Source/TonSwift/Cells/Integers/DynamicallySized/VarUInt248.swift create mode 100644 Source/TonSwift/Cells/Integers/Signed/Int16+CellCodable.swift create mode 100644 Source/TonSwift/Cells/Integers/Signed/Int32+CellCodable.swift create mode 100644 Source/TonSwift/Cells/Integers/Signed/Int64+CellCodable.swift create mode 100644 Source/TonSwift/Cells/Integers/Signed/Int8+CellCodable.swift create mode 100644 Source/TonSwift/Cells/Integers/Unary.swift create mode 100644 Source/TonSwift/Cells/Integers/Unsigned/UInt16+CellCodable.swift create mode 100644 Source/TonSwift/Cells/Integers/Unsigned/UInt256.swift create mode 100644 Source/TonSwift/Cells/Integers/Unsigned/UInt32+CellCodable.swift create mode 100644 Source/TonSwift/Cells/Integers/Unsigned/UInt64+CellCodable.swift create mode 100644 Source/TonSwift/Cells/Integers/Unsigned/UInt8+CellCodable.swift create mode 100644 Source/TonSwift/Cells/LevelMask.swift create mode 100644 Source/TonSwift/Util/Bool+CellCodable.swift diff --git a/Source/TonSwift/Address/ADNLAddress.swift b/Source/TonSwift/Address/ADNLAddress.swift index 72f829c..7fbff65 100644 --- a/Source/TonSwift/Address/ADNLAddress.swift +++ b/Source/TonSwift/Address/ADNLAddress.swift @@ -1,6 +1,6 @@ import Foundation -struct ADNLAddress { +struct ADNLAddress: Equatable { static func parseFriendly(_ src: String) throws -> ADNLAddress { if src.count != 55 { throw TonError.custom("Invalid address") @@ -46,10 +46,8 @@ struct ADNLAddress { return String(data.toBase32().dropFirst()) } -} + // MARK: - Equatable -// MARK: - Equatable -extension ADNLAddress: Equatable { static func == (lhs: ADNLAddress, rhs: ADNLAddress) -> Bool { lhs.address == rhs.address } diff --git a/Source/TonSwift/Address/Address.swift b/Source/TonSwift/Address/Address.swift index 67d7148..19bc7f2 100644 --- a/Source/TonSwift/Address/Address.swift +++ b/Source/TonSwift/Address/Address.swift @@ -1,6 +1,6 @@ import Foundation -public struct Address: Hashable, Codable { +public struct Address: Hashable, Codable, Equatable, CellCodable, StaticSize { public let workchain: Int8 public let hash: Data @@ -49,27 +49,25 @@ public struct Address: Hashable, Codable { public func toString(urlSafe: Bool = true, testOnly: Bool = false, bounceable: Bool = BounceableDefault) -> String { toFriendly(testOnly: testOnly, bounceable: bounceable).toString(urlSafe: urlSafe) } -} -// MARK: - Equatable -extension Address: Equatable { + // MARK: - Equatable + public static func == (lhs: Address, rhs: Address) -> Bool { lhs.workchain != rhs.workchain ? false : lhs.hash == rhs.hash } -} -/// ``` -/// anycast_info$_ depth:(#<= 30) { depth >= 1 } -/// rewrite_pfx:(bits depth) = Anycast; -/// addr_std$10 anycast:(Maybe Anycast) -/// workchain_id:int8 -/// address:bits256 = MsgAddressInt; -/// addr_var$11 anycast:(Maybe Anycast) -/// addr_len:(## 9) -/// workchain_id:int32 -/// address:(bits addr_len) = MsgAddressInt; -/// ``` -extension Address: CellCodable, StaticSize { + // MARK: - CellCodable, StaticSize + /// ``` + /// anycast_info$_ depth:(#<= 30) { depth >= 1 } + /// rewrite_pfx:(bits depth) = Anycast; + /// addr_std$10 anycast:(Maybe Anycast) + /// workchain_id:int8 + /// address:bits256 = MsgAddressInt; + /// addr_var$11 anycast:(Maybe Anycast) + /// addr_len:(## 9) + /// workchain_id:int32 + /// address:(bits addr_len) = MsgAddressInt; + /// ``` public static var bitWidth: Int = 267 @@ -102,28 +100,3 @@ extension Address: CellCodable, StaticSize { } } } - -/// The most compact address encoding that's often used within smart contracts: workchain + hash. -public struct CompactAddress: Hashable, CellCodable, StaticSize { - public static var bitWidth: Int = 8 + 256 - public let inner: Address - - init(_ inner: Address) { - self.inner = inner - } - - public func storeTo(builder: Builder) throws { - try builder - .store(int: inner.workchain, bits: 8) - .store(data: inner.hash) - } - - public static func loadFrom(slice: Slice) throws -> CompactAddress { - try slice.tryLoad { s in - let wc = Int8(try s.loadInt(bits: 8)) - let hash = try s.loadBytes(32) - return CompactAddress(Address(workchain: wc, hash: hash)) - } - } -} - diff --git a/Source/TonSwift/Address/AnyAddress.swift b/Source/TonSwift/Address/AnyAddress.swift index 5978ce9..42411a7 100644 --- a/Source/TonSwift/Address/AnyAddress.swift +++ b/Source/TonSwift/Address/AnyAddress.swift @@ -16,7 +16,7 @@ import Foundation /// _ _:MsgAddressExt = MsgAddress; /// ``` /// -enum AnyAddress { +enum AnyAddress: CellCodable { case none case internalAddr(Address) case externalAddr(ExternalAddress) @@ -61,9 +61,9 @@ enum AnyAddress { case .externalAddr(let addr): return addr } } -} -extension AnyAddress: CellCodable { + // MARK: - CellCodable + func storeTo(builder: Builder) throws { switch self { case .none: diff --git a/Source/TonSwift/Address/CompactAddress.swift b/Source/TonSwift/Address/CompactAddress.swift new file mode 100644 index 0000000..99bdabf --- /dev/null +++ b/Source/TonSwift/Address/CompactAddress.swift @@ -0,0 +1,32 @@ +// +// CompactAddress.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +import Foundation + +/// The most compact address encoding that's often used within smart contracts: workchain + hash. +public struct CompactAddress: Hashable, CellCodable, StaticSize { + public static var bitWidth: Int = 8 + 256 + public let inner: Address + + init(_ inner: Address) { + self.inner = inner + } + + public func storeTo(builder: Builder) throws { + try builder + .store(int: inner.workchain, bits: 8) + .store(data: inner.hash) + } + + public static func loadFrom(slice: Slice) throws -> CompactAddress { + try slice.tryLoad { s in + let wc = Int8(try s.loadInt(bits: 8)) + let hash = try s.loadBytes(32) + return CompactAddress(Address(workchain: wc, hash: hash)) + } + } +} diff --git a/Source/TonSwift/Address/ExternalAddress.swift b/Source/TonSwift/Address/ExternalAddress.swift index e41f244..2525b0b 100644 --- a/Source/TonSwift/Address/ExternalAddress.swift +++ b/Source/TonSwift/Address/ExternalAddress.swift @@ -4,7 +4,7 @@ import Foundation /// ``` /// addr_extern$01 len:(## 9) external_address:(bits len) = MsgAddressExt; /// ``` -public struct ExternalAddress { +public struct ExternalAddress: CellCodable { private(set) var value: Bitstring public init(value: Bitstring) { @@ -18,9 +18,7 @@ public struct ExternalAddress { public static func mock(seed: String) throws -> Self { ExternalAddress(value: Bitstring(data: Data(seed.utf8).sha256())) } -} -extension ExternalAddress: CellCodable { public func storeTo(builder: Builder) throws { try builder .store(uint: 1, bits: 2) diff --git a/Source/TonSwift/Cells/Bitstring.swift b/Source/TonSwift/Cells/Bitstring.swift index 9ad1cd3..7a15475 100644 --- a/Source/TonSwift/Cells/Bitstring.swift +++ b/Source/TonSwift/Cells/Bitstring.swift @@ -1,8 +1,6 @@ import Foundation - - -public struct Bitstring: Hashable { +public struct Bitstring: Hashable, Comparable, Equatable { public static let empty = Bitstring(data: .init(), unchecked: (offset: 0, length: 0)) @@ -221,10 +219,10 @@ public struct Bitstring: Hashable { return try! builder.alignedBitstring() // we guarantee alignment in this method } -} -/// Bitstring implements lexicographic comparison. -extension Bitstring: Comparable { + // MARK: - Comparable + /// Bitstring implements lexicographic comparison. + public static func < (lhs: Self, rhs: Self) -> Bool { for i in 0..) -> Data { - subdata(in: range.lowerBound ..< range.upperBound) - } - - public func hexString() -> String { - map({ String(format: "%02hhx", $0) }).joined() - } -} diff --git a/Source/TonSwift/Cells/Boc.swift b/Source/TonSwift/Cells/Boc.swift index 905bf90..390af33 100644 --- a/Source/TonSwift/Cells/Boc.swift +++ b/Source/TonSwift/Cells/Boc.swift @@ -1,11 +1,5 @@ import Foundation -enum BocMagic: UInt32 { - case V1 = 0x68ff65f3 - case V2 = 0xacc3a728 - case V3 = 0xb5ee9c72 -} - /// BoC = Bag-of-Cells, data structure for efficient storage and transmission of a collection of cells. struct Boc { let size: Int @@ -17,7 +11,7 @@ struct Boc { let index: Data? let cellData: Data let rootIndices: [UInt64] - + init(data: Data) throws { let reader = Slice(data: data) guard let magic = BocMagic(rawValue: UInt32(try reader.loadUint(bits: 32))) else { diff --git a/Source/TonSwift/Cells/BocMagic.swift b/Source/TonSwift/Cells/BocMagic.swift new file mode 100644 index 0000000..3ac7784 --- /dev/null +++ b/Source/TonSwift/Cells/BocMagic.swift @@ -0,0 +1,12 @@ +// +// BocMagic.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +enum BocMagic: UInt32 { + case V1 = 0x68ff65f3 + case V2 = 0xacc3a728 + case V3 = 0xb5ee9c72 +} diff --git a/Source/TonSwift/Cells/Builder.swift b/Source/TonSwift/Cells/Builder.swift index 066c8fb..5297a62 100644 --- a/Source/TonSwift/Cells/Builder.swift +++ b/Source/TonSwift/Cells/Builder.swift @@ -8,10 +8,7 @@ public class Builder { public private(set) var refs: [Cell] - - // MARK: - Initializers - /// Initialize the Builder with a given capacity. /// Note: Builder can be used to construct larger bitstrings, not only 1023-bit Cells. E.g. to build a BoC. @@ -34,10 +31,8 @@ public class Builder { self.refs = refs } - // MARK: - Finalization - /// Clones slice at its current state. public func clone() -> Builder { Builder(capacity: capacity, buffer: _buffer, length: _length, refs: refs) @@ -67,12 +62,9 @@ public class Builder { } return _buffer.subdata(in: 0..._length / 8) } - - - + // MARK: - Metrics - /// Returns whether the written bits are byte-aligned public var aligned: Bool { _length % 8 == 0 @@ -120,11 +112,9 @@ public class Builder { return nil } } - - + // MARK: - Storing Generic Types - - + /// Stores an object @discardableResult public func store(_ object: CellCodable) throws -> Self { @@ -144,12 +134,9 @@ public class Builder { return self } - - - + // MARK: - Storing Refs - - + /** Store reference - parameter cell: cell or builder to store @@ -163,6 +150,7 @@ public class Builder { refs.append(cell) return self } + @discardableResult public func store(ref builder: Builder) throws -> Self { try store(ref: try builder.endCell()) @@ -184,6 +172,7 @@ public class Builder { return self } + @discardableResult public func storeMaybe(ref builder: Builder?) throws -> Self { if let builder = builder { @@ -225,29 +214,22 @@ public class Builder { try store(bit: false) } } - - - + // MARK: - Storing Dictionaries - - + @discardableResult public func store(dict: any CellCodableDictionary) throws -> Self { try dict.storeTo(builder: self) return self } - + @discardableResult public func store(dictRoot dict: any CellCodableDictionary) throws -> Self { try dict.storeRootTo(builder: self) return self } - - - - + // MARK: - Storing Bits - /// Write a single bit: the bit is set for positive values, not set for zero or negative @discardableResult @@ -261,13 +243,13 @@ public class Builder { _length += 1 return self } - + /// Writes bit as a boolean (true => 1, false => 0) @discardableResult public func store(bit: Bool) throws -> Self { try store(bit: bit ? 1 : 0) } - + /// Write repeating bit a given number of times. @discardableResult public func store(bit: Bit, repeat count: Int) throws -> Self { @@ -277,7 +259,7 @@ public class Builder { } return self } - + /// Writes bits from a bitstring @discardableResult public func store(bits: Bitstring) throws -> Self { @@ -330,15 +312,9 @@ public class Builder { } return self } - - - - - - + // MARK: - Storing Integers - - + /** Write uint value - parameter value: value as bigint or number @@ -348,7 +324,7 @@ public class Builder { public func store(uint value: T, bits: Int) throws -> Self where T: BinaryInteger { try store(biguint: BigUInt(value), bits: bits) } - + @discardableResult public func store(biguint value: BigUInt, bits: Int) throws -> Self { try checkCapacity(bits) @@ -381,7 +357,7 @@ public class Builder { return self } } - + // Corner case for zero bits if bits == 0 { if value != 0 { @@ -418,13 +394,12 @@ public class Builder { } return self } - - + @discardableResult public func store(int value: any BinaryInteger, bits: Int) throws -> Self { try store(bigint: BigInt(value), bits: bits) } - + @discardableResult func store(bigint value: BigInt, bits: Int) throws -> Self { var v = value @@ -465,14 +440,8 @@ public class Builder { return self } - - - - - // MARK: - Storing Variable-Length Integers - - + /// Stores VarUInteger with a given `limit` in bytes. /// The integer must be at most `limit-1` bytes long. /// Therefore, `(VarUInteger 16)` accepts 120-bit number (15 bytes) and uses 4 bits to encode length prefix 0...15. @@ -480,7 +449,7 @@ public class Builder { func store(varuint v: UInt64, limit: Int) throws -> Self { try store(varuint: BigUInt(v), limit: limit) } - + /// Stores VarUInteger with a given `limit` in bytes. /// The integer must be at most `limit-1` bytes long. /// Therefore, `(VarUInteger 16)` accepts 120-bit number (15 bytes) and uses 4 bits to encode length prefix 0...15. @@ -515,12 +484,10 @@ public class Builder { return self } - private func checkCapacity(_ bits: Int) throws { if availableBits < bits || bits < 0 { throw TonError.custom("Builder overflow: need to write \(bits), but available \(availableBits)") } } - } diff --git a/Source/TonSwift/Cells/Cell/BasicCell.swift b/Source/TonSwift/Cells/Cell/BasicCell.swift new file mode 100644 index 0000000..d86450c --- /dev/null +++ b/Source/TonSwift/Cells/Cell/BasicCell.swift @@ -0,0 +1,157 @@ +// +// BasicCell.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +import Foundation + +/// Internal basic Cell data: type, bits and refs. +/// This is used for internal computations to produce full-featured `Cell` type with various precomputed data. +struct BasicCell: Hashable { + let type: CellType + let bits: Bitstring + let refs: [Cell] + + /// Parse the exotic cell + static func exotic(bits: Bitstring, refs: [Cell]) throws -> Self { + let reader = Slice(bits: bits) + let typeInt = try reader.preloadUint(bits: 8) + + let type: CellType + switch typeInt { + case 1: + type = try resolvePruned(bits: bits, refs: refs).type + + case 2: + throw TonError.custom("Library cell must be loaded automatically") + + case 3: + type = try resolveMerkleProof(bits: bits, refs: refs).type + + case 4: + type = try resolveMerkleUpdate(bits: bits, refs: refs).type + + default: + throw TonError.custom("Invalid exotic cell type: \(typeInt)") + } + + return BasicCell(type: type, bits: bits, refs: refs) + } + + // This function replicates precomputation logic on the cell data + // https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/vm/cells/DataCell.cpp#L214 + func precompute() throws -> (mask: LevelMask, hashes: [Data], depths: [UInt32]) { + var levelMask: LevelMask + var pruned: ExoticPruned? = nil + + switch type { + case .ordinary: + var mask: UInt32 = 0 + for r in refs { + mask = mask | r.mask.value + } + levelMask = LevelMask(mask: mask) + + case .prunedBranch: + pruned = try exoticPruned(bits: bits, refs: refs) + levelMask = LevelMask(mask: pruned!.mask) + + case .merkleProof: + try exoticMerkleProof(bits: bits, refs: refs) + levelMask = LevelMask(mask: refs[0].mask.value >> 1) + + case .merkleUpdate: + try exoticMerkleUpdate(bits: bits, refs: refs) + levelMask = LevelMask(mask: (refs[0].mask.value | refs[1].mask.value) >> 1) + } + + // + // Calculate hashes and depths + // NOTE: https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/vm/cells/DataCell.cpp#L214 + // + + var depths: [UInt32] = [] + var hashes: [Data] = [] + + let hashCount = type == .prunedBranch ? 1 : levelMask.hashCount + let totalHashCount = levelMask.hashCount + let hashIOffset = totalHashCount - hashCount + + var hashI: UInt32 = 0 + for levelI in 0...levelMask.level { + if !levelMask.isSignificant(level: levelI) { + continue + } + + if hashI < hashIOffset { + hashI += 1 + continue + } + + // Bits + var currentBits: Bitstring + if hashI == hashIOffset { + if !(levelI == 0 || type == .prunedBranch) { + throw TonError.custom("Invalid") + } + currentBits = bits + } else { + if !(levelI != 0 && type != .prunedBranch) { + throw TonError.custom("Invalid: \(levelI), \(type)") + } + currentBits = Bitstring(data: hashes[Int(hashI - hashIOffset) - 1], unchecked: (offset: 0, length: 256)) + } + + // Depth + var currentDepth: UInt32 = 0 + for c in refs { + var childDepth: UInt32 + if type == .merkleProof || type == .merkleUpdate { + childDepth = c.depth(level: Int(levelI) + 1) + } else { + childDepth = c.depth(level: Int(levelI)) + } + currentDepth = max(currentDepth, childDepth) + } + if refs.count > 0 { + currentDepth += 1 + } + + // Hash + let repr = try getRepr(bits: currentBits, refs: refs, level: levelI, type: type) + let hash = repr.sha256() + let destI = hashI - hashIOffset + depths.insert(currentDepth, at: Int(destI)) + hashes.insert(hash, at: Int(destI)) + + hashI += 1 + } + + // Calculate hash and depth for all levels + var resolvedHashes: [Data] = [] + var resolvedDepths: [UInt32] = [] + if pruned != nil { + for i in 0..<4 { + let hashIndex = levelMask.apply(level: UInt32(i)).hashIndex + let thisHashIndex = levelMask.hashIndex + if hashIndex != thisHashIndex { + resolvedHashes.append(pruned!.pruned[Int(hashIndex)].hash) + resolvedDepths.append(pruned!.pruned[Int(hashIndex)].depth) + } else { + resolvedHashes.append(hashes[0]) + resolvedDepths.append(depths[0]) + } + } + } else { + for i in 0..<4 { + let hashIndex = levelMask.apply(level: UInt32(i)).hashIndex + resolvedHashes.append(hashes[Int(hashIndex)]) + resolvedDepths.append(depths[Int(hashIndex)]) + } + } + + return (levelMask, resolvedHashes, resolvedDepths) + } +} diff --git a/Source/TonSwift/Cells/Cell.swift b/Source/TonSwift/Cells/Cell/Cell.swift similarity index 65% rename from Source/TonSwift/Cells/Cell.swift rename to Source/TonSwift/Cells/Cell/Cell.swift index 0b3c037..95d3076 100644 --- a/Source/TonSwift/Cells/Cell.swift +++ b/Source/TonSwift/Cells/Cell/Cell.swift @@ -6,24 +6,8 @@ public let BitsPerCell = 1023 /// Number of refs that fits into a cell public let RefsPerCell = 4 - -public enum CellType: Int { - case ordinary = -1 - case prunedBranch = 1 - case merkleProof = 3 - case merkleUpdate = 4 -} - -/// Metrics of a cell or a builder -public struct CellMetrics { - /// Number of bits in the cell - var bitsCount: Int - /// Number of refs in the cell - var refsCount: Int -} - /// TON cell -public struct Cell: Hashable { +public struct Cell: Hashable, Equatable { private let basic: BasicCell @@ -41,7 +25,7 @@ public struct Cell: Hashable { // Precomputed data private var _hashes: [Data] = [] private var _depths: [UInt32] = [] - fileprivate let mask: LevelMask + let mask: LevelMask /// Empty cell with no bits and no refs. public static let empty = Cell() @@ -188,12 +172,9 @@ public struct Cell: Hashable { return s } - -} + // MARK: - Equatable -// MARK: - Equatable -extension Cell: Equatable { /** Checks cell to be euqal to another cell - parameter other: other cell @@ -206,155 +187,6 @@ extension Cell: Equatable { // MARK: - Internal implementation -/// Internal basic Cell data: type, bits and refs. -/// This is used for internal computations to produce full-featured `Cell` type with various precomputed data. -fileprivate struct BasicCell: Hashable { - let type: CellType - let bits: Bitstring - let refs: [Cell] - - /// Parse the exotic cell - static func exotic(bits: Bitstring, refs: [Cell]) throws -> Self { - let reader = Slice(bits: bits) - let typeInt = try reader.preloadUint(bits: 8) - - let type: CellType - switch typeInt { - case 1: - type = try resolvePruned(bits: bits, refs: refs).type - - case 2: - throw TonError.custom("Library cell must be loaded automatically") - - case 3: - type = try resolveMerkleProof(bits: bits, refs: refs).type - - case 4: - type = try resolveMerkleUpdate(bits: bits, refs: refs).type - - default: - throw TonError.custom("Invalid exotic cell type: \(typeInt)") - } - - return BasicCell(type: type, bits: bits, refs: refs) - } - - // This function replicates precomputation logic on the cell data - // https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/vm/cells/DataCell.cpp#L214 - func precompute() throws -> (mask: LevelMask, hashes: [Data], depths: [UInt32]) { - var levelMask: LevelMask - var pruned: ExoticPruned? = nil - - switch type { - case .ordinary: - var mask: UInt32 = 0 - for r in refs { - mask = mask | r.mask.value - } - levelMask = LevelMask(mask: mask) - - case .prunedBranch: - pruned = try exoticPruned(bits: bits, refs: refs) - levelMask = LevelMask(mask: pruned!.mask) - - case .merkleProof: - try exoticMerkleProof(bits: bits, refs: refs) - levelMask = LevelMask(mask: refs[0].mask.value >> 1) - - case .merkleUpdate: - try exoticMerkleUpdate(bits: bits, refs: refs) - levelMask = LevelMask(mask: (refs[0].mask.value | refs[1].mask.value) >> 1) - } - - // - // Calculate hashes and depths - // NOTE: https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/vm/cells/DataCell.cpp#L214 - // - - var depths: [UInt32] = [] - var hashes: [Data] = [] - - let hashCount = type == .prunedBranch ? 1 : levelMask.hashCount - let totalHashCount = levelMask.hashCount - let hashIOffset = totalHashCount - hashCount - - var hashI: UInt32 = 0 - for levelI in 0...levelMask.level { - if !levelMask.isSignificant(level: levelI) { - continue - } - - if hashI < hashIOffset { - hashI += 1 - continue - } - - // Bits - var currentBits: Bitstring - if hashI == hashIOffset { - if !(levelI == 0 || type == .prunedBranch) { - throw TonError.custom("Invalid") - } - currentBits = bits - } else { - if !(levelI != 0 && type != .prunedBranch) { - throw TonError.custom("Invalid: \(levelI), \(type)") - } - currentBits = Bitstring(data: hashes[Int(hashI - hashIOffset) - 1], unchecked: (offset: 0, length: 256)) - } - - // Depth - var currentDepth: UInt32 = 0 - for c in refs { - var childDepth: UInt32 - if type == .merkleProof || type == .merkleUpdate { - childDepth = c.depth(level: Int(levelI) + 1) - } else { - childDepth = c.depth(level: Int(levelI)) - } - currentDepth = max(currentDepth, childDepth) - } - if refs.count > 0 { - currentDepth += 1 - } - - // Hash - let repr = try getRepr(bits: currentBits, refs: refs, level: levelI, type: type) - let hash = repr.sha256() - let destI = hashI - hashIOffset - depths.insert(currentDepth, at: Int(destI)) - hashes.insert(hash, at: Int(destI)) - - hashI += 1 - } - - // Calculate hash and depth for all levels - var resolvedHashes: [Data] = [] - var resolvedDepths: [UInt32] = [] - if pruned != nil { - for i in 0..<4 { - let hashIndex = levelMask.apply(level: UInt32(i)).hashIndex - let thisHashIndex = levelMask.hashIndex - if hashIndex != thisHashIndex { - resolvedHashes.append(pruned!.pruned[Int(hashIndex)].hash) - resolvedDepths.append(pruned!.pruned[Int(hashIndex)].depth) - } else { - resolvedHashes.append(hashes[0]) - resolvedDepths.append(depths[0]) - } - } - } else { - for i in 0..<4 { - let hashIndex = levelMask.apply(level: UInt32(i)).hashIndex - resolvedHashes.append(hashes[Int(hashIndex)]) - resolvedDepths.append(depths[Int(hashIndex)]) - } - } - - return (levelMask, resolvedHashes, resolvedDepths) - } -} - func getRepr(bits: Bitstring, refs: [Cell], level: UInt32, type: CellType) throws -> Data { // Allocate let bitsLen = (bits.length + 7) / 8 @@ -570,56 +402,6 @@ func exoticMerkleProof(bits: Bitstring, refs: [Cell]) throws -> (proofDepth: UIn return (proofDepth, proofHash) } -public struct LevelMask { - private var _mask: UInt32 = 0 - private var _hashIndex: UInt32 - private var _hashCount: UInt32 - - public init(mask: UInt32 = 0) { - self._mask = mask - self._hashIndex = countSetBits(self._mask) - self._hashCount = self._hashIndex + 1 - } - - public var value: UInt32 { - _mask - } - - public var level: UInt32 { - UInt32(32 - _mask.leadingZeroBitCount) - } - - public var hashIndex: UInt32 { - _hashIndex - } - - public var hashCount: UInt32 { - _hashCount - } - - public func apply(level: UInt32) -> LevelMask { - LevelMask(mask: _mask & ((1 << level) - 1)) - } - - public func isSignificant(level: UInt32) -> Bool { - level == 0 || (_mask >> (level - 1)) % 2 != 0 - } -} - -extension LevelMask: Hashable { - public static func == (lhs: LevelMask, rhs: LevelMask) -> Bool { - lhs._mask == rhs._mask && - lhs._hashIndex == rhs._hashIndex && - lhs._hashCount == rhs._hashCount - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(_mask) - hasher.combine(_hashIndex) - hasher.combine(_hashCount) - } -} - func countSetBits(_ n: UInt32) -> UInt32 { var n = n - ((n >> 1) & 0x55555555) n = (n & 0x33333333) + ((n >> 2) & 0x33333333) diff --git a/Source/TonSwift/Cells/Cell/CellMetrics.swift b/Source/TonSwift/Cells/Cell/CellMetrics.swift new file mode 100644 index 0000000..191876a --- /dev/null +++ b/Source/TonSwift/Cells/Cell/CellMetrics.swift @@ -0,0 +1,14 @@ +// +// CellMetrics.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +/// Metrics of a cell or a builder +public struct CellMetrics { + /// Number of bits in the cell + var bitsCount: Int + /// Number of refs in the cell + var refsCount: Int +} diff --git a/Source/TonSwift/Cells/Cell/CellType.swift b/Source/TonSwift/Cells/Cell/CellType.swift new file mode 100644 index 0000000..c38c30a --- /dev/null +++ b/Source/TonSwift/Cells/Cell/CellType.swift @@ -0,0 +1,13 @@ +// +// CellType.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +public enum CellType: Int { + case ordinary = -1 + case prunedBranch = 1 + case merkleProof = 3 + case merkleUpdate = 4 +} diff --git a/Source/TonSwift/Cells/Integers.swift b/Source/TonSwift/Cells/Integers.swift deleted file mode 100644 index 30e7d07..0000000 --- a/Source/TonSwift/Cells/Integers.swift +++ /dev/null @@ -1,248 +0,0 @@ -import Foundation -import BigInt - -/// All APIs that take bit as a parameter or return a bit are expressed using typealias `Bit` based on `Int`. -/// An API that produces `Bit` guarantees that it is in range `[0,1]`. -public typealias Bit = Int - -/// Represents unary integer encoding: `0` for 0, `10` for 1, `110` for 2, `1{n}0` for n. -public struct Unary: CellCodable { - public let value: Int - - init(_ v: Int) { - value = v - } - - public func storeTo(builder: Builder) throws { - try builder.store(bit: 1, repeat: value) - try builder.store(bit: 0) - } - - public static func loadFrom(slice: Slice) throws -> Self { - var v: Int = 0 - while try slice.loadBit() == 1 { - v += 1 - } - return Unary(v) - } -} - -extension Bool: CellCodable, StaticSize { - public static var bitWidth: Int = 1 - - public func storeTo(builder: Builder) throws { - try builder.store(bit: self) - } - - public static func loadFrom(slice: Slice) throws -> Self { - try slice.loadBoolean() - } -} - -/// 256-bit unsigned integer -public struct UInt256: Hashable, CellCodable, StaticSize { - public var value: BigUInt - - public static var bitWidth: Int = 256 - - init(biguint: BigUInt) { - value = biguint - } - - public func storeTo(builder: Builder) throws { - try builder.store(uint: value, bits: Self.bitWidth) - } - - public static func loadFrom(slice: Slice) throws -> Self { - Self(biguint: try slice.loadUintBig(bits: Self.bitWidth)) - } -} - -// Unsigned short integers - -extension UInt8: CellCodable, StaticSize { - public static var bitWidth: Int = 8 - - public func storeTo(builder: Builder) throws { - try builder.store(uint: self, bits: Self.bitWidth) - } - - public static func loadFrom(slice: Slice) throws -> Self { - Self(try slice.loadUint(bits: Self.bitWidth)) - } -} - -extension UInt16: CellCodable, StaticSize { - public static var bitWidth: Int = 16 - - public func storeTo(builder: Builder) throws { - try builder.store(uint: self, bits: Self.bitWidth) - } - - public static func loadFrom(slice: Slice) throws -> Self { - Self(try slice.loadUint(bits: Self.bitWidth)) - } -} - -extension UInt32: CellCodable, StaticSize { - public static var bitWidth: Int = 32 - - public func storeTo(builder: Builder) throws { - try builder.store(uint: self, bits: Self.bitWidth) - } - - public static func loadFrom(slice: Slice) throws -> Self { - Self(try slice.loadUint(bits: Self.bitWidth)) - } -} - -extension UInt64: CellCodable, StaticSize { - public static var bitWidth: Int = 64 - - public func storeTo(builder: Builder) throws { - try builder.store(uint: self, bits: Self.bitWidth) - } - - public static func loadFrom(slice: Slice) throws -> Self { - Self(try slice.loadUint(bits: Self.bitWidth)) - } -} - -// Signed short integers - -extension Int8: CellCodable, StaticSize { - public static var bitWidth: Int = 8 - - public func storeTo(builder: Builder) throws { - try builder.store(int: self, bits: Self.bitWidth) - } - - public static func loadFrom(slice: Slice) throws -> Self { - Self(try slice.loadInt(bits: Self.bitWidth)) - } -} - -extension Int16: CellCodable, StaticSize { - public static var bitWidth: Int = 16 - - public func storeTo(builder: Builder) throws { - try builder.store(int: self, bits: Self.bitWidth) - } - - public static func loadFrom(slice: Slice) throws -> Self { - Self(try slice.loadInt(bits: Self.bitWidth)) - } -} - -extension Int32: CellCodable, StaticSize { - public static var bitWidth: Int = 32 - - public func storeTo(builder: Builder) throws { - try builder.store(int: self, bits: Self.bitWidth) - } - - public static func loadFrom(slice: Slice) throws -> Self { - Self(try slice.loadInt(bits: Self.bitWidth)) - } -} - -extension Int64: CellCodable, StaticSize { - public static var bitWidth: Int = 64 - - public func storeTo(builder: Builder) throws { - try builder.store(int: self, bits: Self.bitWidth) - } - - public static func loadFrom(slice: Slice) throws -> Self { - Self(try slice.loadInt(bits: Self.bitWidth)) - } -} - - - -// -// Dynamically-sized integers -// - -/// Up-to-31-byte (248-bit) unsigned integer (5-bit length prefix) -public struct VarUInt248: Hashable, CellCodable { - public var value: BigUInt - public func storeTo(builder: Builder) throws { - try builder.store(varuint: value, limit: 32) - } - public static func loadFrom(slice: Slice) throws -> Self { - Self(value: try slice.loadVarUintBig(limit: 32)) - } -} - -/// Up-to-15-byte (120-bit) unsigned integer (4-bit length prefix) -public struct VarUInt120: Hashable, CellCodable { - public var value: BigUInt - public func storeTo(builder: Builder) throws { - try builder.store(varuint: value, limit: 16) - } - public static func loadFrom(slice: Slice) throws -> Self { - Self(value: try slice.loadVarUintBig(limit: 16)) - } -} - -public struct IntCoder: TypeCoder { - public typealias T = BigInt - - public let bits: Int - - public init(bits: Int) { - self.bits = bits - } - - public func storeValue(_ src: T, to builder: Builder) throws { - try builder.store(int: src, bits: bits) - } - - public func loadValue(from src: Slice) throws -> T { - try src.loadIntBig(bits: bits) - } -} - -public struct UIntCoder: TypeCoder { - public typealias T = BigUInt - - public let bits: Int - - public init(bits: Int) { - self.bits = bits - } - - public func storeValue(_ src: T, to builder: Builder) throws { - try builder.store(uint: src, bits: bits) - } - - public func loadValue(from src: Slice) throws -> T { - try src.loadUintBig(bits: bits) - } -} - -/// Encodes variable-length integers using `limit` bound on integer size in _bytes_. -/// Therefore, `VarUIntCoder(32)` can represent 248-bit integers (lengths 0...31 bytes). -/// TL-B: -/// ``` -/// var_uint$_ {n:#} len:(#< n) value:(uint (len * 8)) = VarUInteger n; -/// var_int$_ {n:#} len:(#< n) value:(int (len * 8)) = VarInteger n; -/// ``` -public struct VarUIntCoder: TypeCoder { - public typealias T = BigUInt - - public let limit: Int - - public init(limit: Int) { - self.limit = limit - } - - public func storeValue(_ src: T, to builder: Builder) throws { - try builder.store(varuint: src, limit: limit) - } - - public func loadValue(from src: Slice) throws -> T { - try src.loadVarUintBig(limit: limit) - } -} diff --git a/Source/TonSwift/Cells/Integers/Bit.swift b/Source/TonSwift/Cells/Integers/Bit.swift new file mode 100644 index 0000000..e6bc765 --- /dev/null +++ b/Source/TonSwift/Cells/Integers/Bit.swift @@ -0,0 +1,10 @@ +// +// Bit.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +/// All APIs that take bit as a parameter or return a bit are expressed using typealias `Bit` based on `Int`. +/// An API that produces `Bit` guarantees that it is in range `[0,1]`. +public typealias Bit = Int diff --git a/Source/TonSwift/Cells/Integers/Coders/IntCoder.swift b/Source/TonSwift/Cells/Integers/Coders/IntCoder.swift new file mode 100644 index 0000000..58bc01e --- /dev/null +++ b/Source/TonSwift/Cells/Integers/Coders/IntCoder.swift @@ -0,0 +1,26 @@ +// +// IntCoder.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +import BigInt + +public struct IntCoder: TypeCoder { + public typealias T = BigInt + + public let bits: Int + + public init(bits: Int) { + self.bits = bits + } + + public func storeValue(_ src: T, to builder: Builder) throws { + try builder.store(int: src, bits: bits) + } + + public func loadValue(from src: Slice) throws -> T { + try src.loadIntBig(bits: bits) + } +} diff --git a/Source/TonSwift/Cells/Integers/Coders/UIntCoder.swift b/Source/TonSwift/Cells/Integers/Coders/UIntCoder.swift new file mode 100644 index 0000000..16ced5c --- /dev/null +++ b/Source/TonSwift/Cells/Integers/Coders/UIntCoder.swift @@ -0,0 +1,26 @@ +// +// UIntCoder.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +import BigInt + +public struct UIntCoder: TypeCoder { + public typealias T = BigUInt + + public let bits: Int + + public init(bits: Int) { + self.bits = bits + } + + public func storeValue(_ src: T, to builder: Builder) throws { + try builder.store(uint: src, bits: bits) + } + + public func loadValue(from src: Slice) throws -> T { + try src.loadUintBig(bits: bits) + } +} diff --git a/Source/TonSwift/Cells/Integers/Coders/VarUIntCoder.swift b/Source/TonSwift/Cells/Integers/Coders/VarUIntCoder.swift new file mode 100644 index 0000000..2ae316f --- /dev/null +++ b/Source/TonSwift/Cells/Integers/Coders/VarUIntCoder.swift @@ -0,0 +1,33 @@ +// +// VarUIntCoder.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +import BigInt + +/// Encodes variable-length integers using `limit` bound on integer size in _bytes_. +/// Therefore, `VarUIntCoder(32)` can represent 248-bit integers (lengths 0...31 bytes). +/// TL-B: +/// ``` +/// var_uint$_ {n:#} len:(#< n) value:(uint (len * 8)) = VarUInteger n; +/// var_int$_ {n:#} len:(#< n) value:(int (len * 8)) = VarInteger n; +/// ``` +public struct VarUIntCoder: TypeCoder { + public typealias T = BigUInt + + public let limit: Int + + public init(limit: Int) { + self.limit = limit + } + + public func storeValue(_ src: T, to builder: Builder) throws { + try builder.store(varuint: src, limit: limit) + } + + public func loadValue(from src: Slice) throws -> T { + try src.loadVarUintBig(limit: limit) + } +} diff --git a/Source/TonSwift/Cells/Integers/DynamicallySized/VarUInt120.swift b/Source/TonSwift/Cells/Integers/DynamicallySized/VarUInt120.swift new file mode 100644 index 0000000..dbdf87d --- /dev/null +++ b/Source/TonSwift/Cells/Integers/DynamicallySized/VarUInt120.swift @@ -0,0 +1,19 @@ +// +// VarUInt120.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +import BigInt + +/// Up-to-15-byte (120-bit) unsigned integer (4-bit length prefix) +public struct VarUInt120: Hashable, CellCodable { + public var value: BigUInt + public func storeTo(builder: Builder) throws { + try builder.store(varuint: value, limit: 16) + } + public static func loadFrom(slice: Slice) throws -> Self { + Self(value: try slice.loadVarUintBig(limit: 16)) + } +} diff --git a/Source/TonSwift/Cells/Integers/DynamicallySized/VarUInt248.swift b/Source/TonSwift/Cells/Integers/DynamicallySized/VarUInt248.swift new file mode 100644 index 0000000..2bb30fa --- /dev/null +++ b/Source/TonSwift/Cells/Integers/DynamicallySized/VarUInt248.swift @@ -0,0 +1,19 @@ +// +// VarUInt248.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +import BigInt + +/// Up-to-31-byte (248-bit) unsigned integer (5-bit length prefix) +public struct VarUInt248: Hashable, CellCodable { + public var value: BigUInt + public func storeTo(builder: Builder) throws { + try builder.store(varuint: value, limit: 32) + } + public static func loadFrom(slice: Slice) throws -> Self { + Self(value: try slice.loadVarUintBig(limit: 32)) + } +} diff --git a/Source/TonSwift/Cells/Integers/Signed/Int16+CellCodable.swift b/Source/TonSwift/Cells/Integers/Signed/Int16+CellCodable.swift new file mode 100644 index 0000000..d547bee --- /dev/null +++ b/Source/TonSwift/Cells/Integers/Signed/Int16+CellCodable.swift @@ -0,0 +1,18 @@ +// +// Int16+CellCodable.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +extension Int16: CellCodable, StaticSize { + public static var bitWidth: Int = 16 + + public func storeTo(builder: Builder) throws { + try builder.store(int: self, bits: Self.bitWidth) + } + + public static func loadFrom(slice: Slice) throws -> Self { + Self(try slice.loadInt(bits: Self.bitWidth)) + } +} diff --git a/Source/TonSwift/Cells/Integers/Signed/Int32+CellCodable.swift b/Source/TonSwift/Cells/Integers/Signed/Int32+CellCodable.swift new file mode 100644 index 0000000..e89be3f --- /dev/null +++ b/Source/TonSwift/Cells/Integers/Signed/Int32+CellCodable.swift @@ -0,0 +1,18 @@ +// +// Int32+CellCodable.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +extension Int32: CellCodable, StaticSize { + public static var bitWidth: Int = 32 + + public func storeTo(builder: Builder) throws { + try builder.store(int: self, bits: Self.bitWidth) + } + + public static func loadFrom(slice: Slice) throws -> Self { + Self(try slice.loadInt(bits: Self.bitWidth)) + } +} diff --git a/Source/TonSwift/Cells/Integers/Signed/Int64+CellCodable.swift b/Source/TonSwift/Cells/Integers/Signed/Int64+CellCodable.swift new file mode 100644 index 0000000..7ed7bec --- /dev/null +++ b/Source/TonSwift/Cells/Integers/Signed/Int64+CellCodable.swift @@ -0,0 +1,18 @@ +// +// Int64+CellCodable.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +extension Int64: CellCodable, StaticSize { + public static var bitWidth: Int = 64 + + public func storeTo(builder: Builder) throws { + try builder.store(int: self, bits: Self.bitWidth) + } + + public static func loadFrom(slice: Slice) throws -> Self { + Self(try slice.loadInt(bits: Self.bitWidth)) + } +} diff --git a/Source/TonSwift/Cells/Integers/Signed/Int8+CellCodable.swift b/Source/TonSwift/Cells/Integers/Signed/Int8+CellCodable.swift new file mode 100644 index 0000000..4c90b62 --- /dev/null +++ b/Source/TonSwift/Cells/Integers/Signed/Int8+CellCodable.swift @@ -0,0 +1,18 @@ +// +// Int8+CellCodable.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +extension Int8: CellCodable, StaticSize { + public static var bitWidth: Int = 8 + + public func storeTo(builder: Builder) throws { + try builder.store(int: self, bits: Self.bitWidth) + } + + public static func loadFrom(slice: Slice) throws -> Self { + Self(try slice.loadInt(bits: Self.bitWidth)) + } +} diff --git a/Source/TonSwift/Cells/Integers/Unary.swift b/Source/TonSwift/Cells/Integers/Unary.swift new file mode 100644 index 0000000..f874c0c --- /dev/null +++ b/Source/TonSwift/Cells/Integers/Unary.swift @@ -0,0 +1,28 @@ +// +// Unary.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +/// Represents unary integer encoding: `0` for 0, `10` for 1, `110` for 2, `1{n}0` for n. +public struct Unary: CellCodable { + public let value: Int + + init(_ v: Int) { + value = v + } + + public func storeTo(builder: Builder) throws { + try builder.store(bit: 1, repeat: value) + try builder.store(bit: 0) + } + + public static func loadFrom(slice: Slice) throws -> Self { + var v: Int = 0 + while try slice.loadBit() == 1 { + v += 1 + } + return Unary(v) + } +} diff --git a/Source/TonSwift/Cells/Integers/Unsigned/UInt16+CellCodable.swift b/Source/TonSwift/Cells/Integers/Unsigned/UInt16+CellCodable.swift new file mode 100644 index 0000000..72bf3c4 --- /dev/null +++ b/Source/TonSwift/Cells/Integers/Unsigned/UInt16+CellCodable.swift @@ -0,0 +1,18 @@ +// +// UInt16+CellCodable.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +extension UInt16: CellCodable, StaticSize { + public static var bitWidth: Int = 16 + + public func storeTo(builder: Builder) throws { + try builder.store(uint: self, bits: Self.bitWidth) + } + + public static func loadFrom(slice: Slice) throws -> Self { + Self(try slice.loadUint(bits: Self.bitWidth)) + } +} diff --git a/Source/TonSwift/Cells/Integers/Unsigned/UInt256.swift b/Source/TonSwift/Cells/Integers/Unsigned/UInt256.swift new file mode 100644 index 0000000..8fcbccb --- /dev/null +++ b/Source/TonSwift/Cells/Integers/Unsigned/UInt256.swift @@ -0,0 +1,27 @@ +// +// UInt256.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +import BigInt + +/// 256-bit unsigned integer +public struct UInt256: Hashable, CellCodable, StaticSize { + public var value: BigUInt + + public static var bitWidth: Int = 256 + + init(biguint: BigUInt) { + value = biguint + } + + public func storeTo(builder: Builder) throws { + try builder.store(uint: value, bits: Self.bitWidth) + } + + public static func loadFrom(slice: Slice) throws -> Self { + Self(biguint: try slice.loadUintBig(bits: Self.bitWidth)) + } +} diff --git a/Source/TonSwift/Cells/Integers/Unsigned/UInt32+CellCodable.swift b/Source/TonSwift/Cells/Integers/Unsigned/UInt32+CellCodable.swift new file mode 100644 index 0000000..b3e5c7f --- /dev/null +++ b/Source/TonSwift/Cells/Integers/Unsigned/UInt32+CellCodable.swift @@ -0,0 +1,18 @@ +// +// UInt32+CellCodable.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +extension UInt32: CellCodable, StaticSize { + public static var bitWidth: Int = 32 + + public func storeTo(builder: Builder) throws { + try builder.store(uint: self, bits: Self.bitWidth) + } + + public static func loadFrom(slice: Slice) throws -> Self { + Self(try slice.loadUint(bits: Self.bitWidth)) + } +} diff --git a/Source/TonSwift/Cells/Integers/Unsigned/UInt64+CellCodable.swift b/Source/TonSwift/Cells/Integers/Unsigned/UInt64+CellCodable.swift new file mode 100644 index 0000000..d87029f --- /dev/null +++ b/Source/TonSwift/Cells/Integers/Unsigned/UInt64+CellCodable.swift @@ -0,0 +1,18 @@ +// +// UInt64+CellCodable.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +extension UInt64: CellCodable, StaticSize { + public static var bitWidth: Int = 64 + + public func storeTo(builder: Builder) throws { + try builder.store(uint: self, bits: Self.bitWidth) + } + + public static func loadFrom(slice: Slice) throws -> Self { + Self(try slice.loadUint(bits: Self.bitWidth)) + } +} diff --git a/Source/TonSwift/Cells/Integers/Unsigned/UInt8+CellCodable.swift b/Source/TonSwift/Cells/Integers/Unsigned/UInt8+CellCodable.swift new file mode 100644 index 0000000..0169e6e --- /dev/null +++ b/Source/TonSwift/Cells/Integers/Unsigned/UInt8+CellCodable.swift @@ -0,0 +1,18 @@ +// +// UInt8+CellCodable.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +extension UInt8: CellCodable, StaticSize { + public static var bitWidth: Int = 8 + + public func storeTo(builder: Builder) throws { + try builder.store(uint: self, bits: Self.bitWidth) + } + + public static func loadFrom(slice: Slice) throws -> Self { + Self(try slice.loadUint(bits: Self.bitWidth)) + } +} diff --git a/Source/TonSwift/Cells/LevelMask.swift b/Source/TonSwift/Cells/LevelMask.swift new file mode 100644 index 0000000..18ac184 --- /dev/null +++ b/Source/TonSwift/Cells/LevelMask.swift @@ -0,0 +1,56 @@ +// +// LevelMask.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +public struct LevelMask: Hashable { + private var _mask: UInt32 = 0 + private var _hashIndex: UInt32 + private var _hashCount: UInt32 + + public init(mask: UInt32 = 0) { + self._mask = mask + self._hashIndex = countSetBits(self._mask) + self._hashCount = self._hashIndex + 1 + } + + public var value: UInt32 { + _mask + } + + public var level: UInt32 { + UInt32(32 - _mask.leadingZeroBitCount) + } + + public var hashIndex: UInt32 { + _hashIndex + } + + public var hashCount: UInt32 { + _hashCount + } + + public func apply(level: UInt32) -> LevelMask { + LevelMask(mask: _mask & ((1 << level) - 1)) + } + + public func isSignificant(level: UInt32) -> Bool { + level == 0 || (_mask >> (level - 1)) % 2 != 0 + } + + // MARK: - Hashable + + public static func == (lhs: LevelMask, rhs: LevelMask) -> Bool { + lhs._mask == rhs._mask && + lhs._hashIndex == rhs._hashIndex && + lhs._hashCount == rhs._hashCount + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(_mask) + hasher.combine(_hashIndex) + hasher.combine(_hashCount) + } +} diff --git a/Source/TonSwift/Util/Bool+CellCodable.swift b/Source/TonSwift/Util/Bool+CellCodable.swift new file mode 100644 index 0000000..8ec6f66 --- /dev/null +++ b/Source/TonSwift/Util/Bool+CellCodable.swift @@ -0,0 +1,18 @@ +// +// Bool+CellCodable.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +extension Bool: CellCodable, StaticSize { + public static var bitWidth = 1 + + public func storeTo(builder: Builder) throws { + try builder.store(bit: self) + } + + public static func loadFrom(slice: Slice) throws -> Self { + try slice.loadBoolean() + } +} diff --git a/Source/TonSwift/Util/Data+FromHex.swift b/Source/TonSwift/Util/Data+FromHex.swift index 0b16a5c..2974d46 100644 --- a/Source/TonSwift/Util/Data+FromHex.swift +++ b/Source/TonSwift/Util/Data+FromHex.swift @@ -21,5 +21,12 @@ public extension Data { self = data } - + + func subdata(in range: ClosedRange) -> Data { + subdata(in: range.lowerBound ..< range.upperBound) + } + + func hexString() -> String { + map { String(format: "%02hhx", $0) }.joined() + } } From 33c2bfcd9ad77fa3ccf66135dd515a68dbedaf79 Mon Sep 17 00:00:00 2001 From: Kirill Kirilenko Date: Thu, 20 Jul 2023 17:09:01 +0400 Subject: [PATCH 06/11] divide dictionary --- .../Dictionaries/CellCodableDictionary.swift | 13 + .../Cells/Dictionaries/DictionaryCoder.swift | 306 +++++++++++++++ Source/TonSwift/Cells/Dictionaries/Edge.swift | 16 + .../Extensions/Dictionary+CellCodable.swift | 15 + .../Dictionary+CellCodableDictionary.swift | 16 + .../Extensions/Set+CellCodable.swift | 17 + .../Set+CellCodableDictionary.swift | 17 + Source/TonSwift/Cells/Dictionaries/Node.swift | 11 + Source/TonSwift/Cells/Dictionary.swift | 363 ------------------ .../TonSwiftTests/Cells/DictionaryTest.swift | 13 +- 10 files changed, 418 insertions(+), 369 deletions(-) create mode 100644 Source/TonSwift/Cells/Dictionaries/CellCodableDictionary.swift create mode 100644 Source/TonSwift/Cells/Dictionaries/DictionaryCoder.swift create mode 100644 Source/TonSwift/Cells/Dictionaries/Edge.swift create mode 100644 Source/TonSwift/Cells/Dictionaries/Extensions/Dictionary+CellCodable.swift create mode 100644 Source/TonSwift/Cells/Dictionaries/Extensions/Dictionary+CellCodableDictionary.swift create mode 100644 Source/TonSwift/Cells/Dictionaries/Extensions/Set+CellCodable.swift create mode 100644 Source/TonSwift/Cells/Dictionaries/Extensions/Set+CellCodableDictionary.swift create mode 100644 Source/TonSwift/Cells/Dictionaries/Node.swift delete mode 100644 Source/TonSwift/Cells/Dictionary.swift diff --git a/Source/TonSwift/Cells/Dictionaries/CellCodableDictionary.swift b/Source/TonSwift/Cells/Dictionaries/CellCodableDictionary.swift new file mode 100644 index 0000000..4f9ff8c --- /dev/null +++ b/Source/TonSwift/Cells/Dictionaries/CellCodableDictionary.swift @@ -0,0 +1,13 @@ +// +// CellCodableDictionary.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +/// Type of a standard dictionary where keys have a statically known length. +/// To work with dynamically known key lengths, use `DictionaryCoder` to load and store dictionaries. +public protocol CellCodableDictionary: CellCodable { + func storeRootTo(builder: Builder) throws + static func loadRootFrom(slice: Slice) throws -> Self +} diff --git a/Source/TonSwift/Cells/Dictionaries/DictionaryCoder.swift b/Source/TonSwift/Cells/Dictionaries/DictionaryCoder.swift new file mode 100644 index 0000000..1cbe371 --- /dev/null +++ b/Source/TonSwift/Cells/Dictionaries/DictionaryCoder.swift @@ -0,0 +1,306 @@ +// +// DictionaryCoder.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +// TODO: class too big - find how to make it less +/// Coder for the dictionaries that stores the coding rules for keys and values. +/// Use this explicit API when working with dynamically-sized dictionary keys. +/// In all other cases use `Dictionary` type where the key size is known at compile time. +public class DictionaryCoder where K.T: Hashable { + let keyLength: Int + let keyCoder: K + let valueCoder: V + + init(keyLength: Int, _ keyCoder: K, _ valueCoder: V) { + self.keyLength = keyLength + self.keyCoder = keyCoder + self.valueCoder = valueCoder + } + + static func `default`() -> DictionaryCoder + where KT: CellCodable & StaticSize & Hashable, + VT: CellCodable, + K == DefaultCoder, + V == DefaultCoder { + DictionaryCoder( + keyLength: KT.bitWidth, + DefaultCoder(), + DefaultCoder() + ) + } + + // MARK: Public methods + + /// Loads dictionary from slice + public func load(_ slice: Slice) throws -> [K.T:V.T] { + let cell = try slice.loadMaybeRef() + if let cell, !cell.isExotic { + return try loadRoot(cell.beginParse()) + } else { + // TODO: review this decision to return empty dicts from exotic cells + // Steve Korshakov says the reason for this is that + // pruned branches should yield empty dicts somewhere down the line. + return [:] + } + } + + /// Loads root of the dictionary directly from a slice + public func loadRoot(_ slice: Slice) throws -> [K.T:V.T] { + var map = [K.T: V.T]() + try doParse(prefix: Builder(), slice: slice, n: keyLength, result: &map) + return map + } + + // MARK: Internal methods + + func store(map: [K.T: V.T], builder: Builder) throws { + if map.count == 0 { + try builder.store(bit: 0) + } else { + try builder.store(bit: 1) + let subcell = Builder() + try storeRoot(map: map, builder: subcell) + try builder.store(ref: try subcell.endCell()) + } + } + + func storeRoot(map: [K.T: V.T], builder: Builder) throws { + if map.count == 0 { + throw TonError.custom("Cannot store empty dictionary directly") + } + + // Serialize keys + var paddedMap: [Bitstring: V.T] = [:] + for (key, value) in map { + let builder = Builder() + try keyCoder.storeValue(key, to: builder) + let keybits = builder.bitstring() + let paddedKey = keybits.padLeft(keyLength) + paddedMap[paddedKey] = value + } + + // Calculate root label + let rootEdge = try buildEdge(paddedMap) + try writeEdge(src: rootEdge, keyLength: keyLength, valueCoder: valueCoder, to: builder) + } + + func findCommonPrefix(src: some Collection) -> Bitstring { + // Corner cases + if src.isEmpty { + return Bitstring() + } + if src.count == 1 { + return src.first! + } + + // Searching for prefix + let sorted = src.sorted() + let first = sorted.first! + let last = sorted.last! + + var size = 0 + for i in 0..(_ src: [Bitstring: T]) throws -> Node { + try invariant(!src.isEmpty) + if src.count == 1 { + return .leaf(value: src.values.first!) + } else { + let (left, right) = try forkMap(src) + return .fork(left: try buildEdge(left), right: try buildEdge(right)) + } + } + + private func buildEdge(_ src: [Bitstring: T]) throws -> Edge { + try invariant(!src.isEmpty) + let label = findCommonPrefix(src: Array(src.keys)) + return Edge(label: label, node: try buildNode(removePrefixMap(src, label.length))) + } + + /// Removes `n` bits from all the keys in a map + private func removePrefixMap(_ src: [Bitstring: T], _ length: Int) -> [Bitstring: T] { + if length == 0 { + return src + } + var res: [Bitstring: T] = [:] + for (k, d) in src { + res[try! k.dropFirst(length)] = d + } + return res + } + + private func writeNode(src: Node, keyLength: Int, valueCoder: V, to builder: Builder) throws where V: TypeCoder, V.T == T { + switch src { + case .fork(let left, let right): + let leftCell = Builder() + let rightCell = Builder() + + try writeEdge(src: left, keyLength: keyLength - 1, valueCoder: valueCoder, to: leftCell) + try writeEdge(src: right, keyLength: keyLength - 1, valueCoder: valueCoder, to: rightCell) + + try builder.store(ref: leftCell) + try builder.store(ref: rightCell) + + case .leaf(let value): + try valueCoder.storeValue(value, to: builder) + } + } + + private func writeEdge(src: Edge, keyLength: Int, valueCoder: V, to: Builder) throws where V: TypeCoder, V.T == T { + try writeLabel(src: src.label, keyLength: keyLength, to: to) + try writeNode(src: src.node, keyLength: keyLength - src.label.length, valueCoder: valueCoder, to: to) + } + + /// Splits the dictionary by the value of the first bit of the keys. 0-prefixed keys go into left map, 1-prefixed keys go into the right one. + /// First bit is removed from the keys. + private func forkMap(_ src: [Bitstring: T]) throws -> (left: [Bitstring: T], right: [Bitstring: T]) { + try invariant(!src.isEmpty) + + var left: [Bitstring: T] = [:] + var right: [Bitstring: T] = [:] + for (k, d) in src { + if k.at(unchecked: 0) == 0 { + left[try! k.dropFirst(1)] = d + } else { + right[try! k.dropFirst(1)] = d + } + } + + try invariant(!left.isEmpty) + try invariant(!right.isEmpty) + return (left, right) + } + + /// Deterministically produces optimal label type for a given label. + /// This implementation is equivalent to the C++ reference implementation, and resolves the ties in the same way. + /// + /// From crypto/vm/dict.cpp: + /// ``` + /// void append_dict_label_same(CellBuilder& cb, bool same, int len, int max_len) { + /// int k = 32 - td::count_leading_zeroes32(max_len); + /// assert(len >= 0 && len <= max_len && max_len <= 1023); + /// // options: mode '0', requires 2n+2 bits (always for n=0) + /// // mode '10', requires 2+k+n bits (only for n<=1) + /// // mode '11', requires 3+k bits (for n>=2, k<2n-1) + /// if (len > 1 && k < 2 * len - 1) { + /// // mode '11' + /// cb.store_long(6 + same, 3).store_long(len, k); + /// } else if (k < len) { + /// // mode '10' + /// cb.store_long(2, 2).store_long(len, k).store_long(-static_cast(same), len); + /// } else { + /// // mode '0' + /// cb.store_long(0, 1).store_long(-2, len + 1).store_long(-static_cast(same), len); + /// } + /// } + /// + /// void append_dict_label(CellBuilder& cb, td::ConstBitPtr label, int len, int max_len) { + /// assert(len <= max_len && max_len <= 1023); + /// if (len > 0 && (int)td::bitstring::bits_memscan(label, len, *label) == len) { + /// return append_dict_label_same(cb, *label, len, max_len); + /// } + /// int k = 32 - td::count_leading_zeroes32(max_len); + /// // two options: mode '0', requires 2n+2 bits + /// // mode '10', requires 2+k+n bits + /// if (k < len) { + /// cb.store_long(2, 2).store_long(len, k); + /// } else { + /// cb.store_long(0, 1).store_long(-2, len + 1); + /// } + /// if ((int)cb.remaining_bits() < len) { + /// throw VmError{Excno::cell_ov, "cannot store a label into a dictionary cell"}; + /// } + /// cb.store_bits(label, len); + /// } + /// ``` + private func writeLabel(src: Bitstring, keyLength: Int, to: Builder) throws { + let k = bitsForInt(keyLength) + let n = src.length + + // The goal is to choose the shortest encoding. + // In case of a tie, choose a lexicographically shorter one. + // Therefore, `short$0` comes ahead of `long$10` which is ahead of `same$11`. + // + // short mode '0' requires 2n+2 bits (always used for n=0) + // long mode '10' requires 2+k+n bits (used only for n<=1) + // same mode '11' requires 3+k bits (for n>=2, k<2n-1) + if let bit = src.repeatsSameBit(), n > 1 && k < 2 * n - 1 { // same mode '11' + try to.store(bits: 1, 1) // header + try to.store(bit: bit) // value + try to.store(uint: n, bits: k) // length + } else if k < n { // long mode '10' + try to.store(bits: 1, 0) // header + try to.store(uint: n, bits: k) // length + try to.store(bits: src) // the string itself + } else { // short mode '0' + try to.store(bit: 0) // header + try to.store(Unary(n)) // unary length prefix: 1{n}0 + try to.store(bits: src) // the string itself + } + } + + private func invariant(_ cond: Bool) throws { + if !cond { throw TonError.custom("Internal inconsistency") } + } +} diff --git a/Source/TonSwift/Cells/Dictionaries/Edge.swift b/Source/TonSwift/Cells/Dictionaries/Edge.swift new file mode 100644 index 0000000..64e33b0 --- /dev/null +++ b/Source/TonSwift/Cells/Dictionaries/Edge.swift @@ -0,0 +1,16 @@ +// +// Edge.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +class Edge { + let label: Bitstring + let node: Node + + init(label: Bitstring, node: Node) { + self.label = label + self.node = node + } +} diff --git a/Source/TonSwift/Cells/Dictionaries/Extensions/Dictionary+CellCodable.swift b/Source/TonSwift/Cells/Dictionaries/Extensions/Dictionary+CellCodable.swift new file mode 100644 index 0000000..9ab2ed0 --- /dev/null +++ b/Source/TonSwift/Cells/Dictionaries/Extensions/Dictionary+CellCodable.swift @@ -0,0 +1,15 @@ +// +// Dictionary+CellCodable.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +extension Dictionary: CellCodable where Key: CellCodable & StaticSize, Value: CellCodable { + public static func loadFrom(slice: Slice) throws -> Self { + try DictionaryCoder.default().load(slice) + } + public func storeTo(builder: Builder) throws { + try DictionaryCoder.default().store(map: self, builder: builder) + } +} diff --git a/Source/TonSwift/Cells/Dictionaries/Extensions/Dictionary+CellCodableDictionary.swift b/Source/TonSwift/Cells/Dictionaries/Extensions/Dictionary+CellCodableDictionary.swift new file mode 100644 index 0000000..9ecf071 --- /dev/null +++ b/Source/TonSwift/Cells/Dictionaries/Extensions/Dictionary+CellCodableDictionary.swift @@ -0,0 +1,16 @@ +// +// Dictionary+CellCodableDictionary.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +extension Dictionary: CellCodableDictionary where Key: CellCodable & StaticSize, Value: CellCodable { + public func storeRootTo(builder: Builder) throws { + try DictionaryCoder.default().storeRoot(map: self, builder: builder) + } + + public static func loadRootFrom(slice: Slice) throws -> Self { + try DictionaryCoder.default().loadRoot(slice) + } +} diff --git a/Source/TonSwift/Cells/Dictionaries/Extensions/Set+CellCodable.swift b/Source/TonSwift/Cells/Dictionaries/Extensions/Set+CellCodable.swift new file mode 100644 index 0000000..62675ac --- /dev/null +++ b/Source/TonSwift/Cells/Dictionaries/Extensions/Set+CellCodable.swift @@ -0,0 +1,17 @@ +// +// Set+CellCodable.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +extension Set: CellCodable where Element: CellCodable & StaticSize { + public static func loadFrom(slice: Slice) throws -> Self { + let dict: [Element: Empty] = try DictionaryCoder.default().load(slice) + return Set(dict.keys) + } + public func storeTo(builder: Builder) throws { + let dict: [Element: Empty] = Dictionary(uniqueKeysWithValues: map { k in (k, Empty()) }) + try DictionaryCoder.default().store(map: dict, builder: builder) + } +} diff --git a/Source/TonSwift/Cells/Dictionaries/Extensions/Set+CellCodableDictionary.swift b/Source/TonSwift/Cells/Dictionaries/Extensions/Set+CellCodableDictionary.swift new file mode 100644 index 0000000..f226de2 --- /dev/null +++ b/Source/TonSwift/Cells/Dictionaries/Extensions/Set+CellCodableDictionary.swift @@ -0,0 +1,17 @@ +// +// Set+CellCodableDictionary.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +extension Set: CellCodableDictionary where Element: CellCodable & StaticSize { + public static func loadRootFrom(slice: Slice) throws -> Self { + let dict: [Element: Empty] = try DictionaryCoder.default().loadRoot(slice) + return Set(dict.keys) + } + public func storeRootTo(builder: Builder) throws { + let dict: [Element: Empty] = Dictionary(uniqueKeysWithValues: map { k in (k, Empty()) }) + try DictionaryCoder.default().storeRoot(map: dict, builder: builder) + } +} diff --git a/Source/TonSwift/Cells/Dictionaries/Node.swift b/Source/TonSwift/Cells/Dictionaries/Node.swift new file mode 100644 index 0000000..4b8ee29 --- /dev/null +++ b/Source/TonSwift/Cells/Dictionaries/Node.swift @@ -0,0 +1,11 @@ +// +// Node.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +enum Node { + case fork(left: Edge, right: Edge) + case leaf(value: T) +} diff --git a/Source/TonSwift/Cells/Dictionary.swift b/Source/TonSwift/Cells/Dictionary.swift deleted file mode 100644 index 30d8ea8..0000000 --- a/Source/TonSwift/Cells/Dictionary.swift +++ /dev/null @@ -1,363 +0,0 @@ -import Foundation - -/// Type of a standard dictionary where keys have a statically known length. -/// To work with dynamically known key lengths, use `DictionaryCoder` to load and store dictionaries. -public protocol CellCodableDictionary: CellCodable { - func storeRootTo(builder: Builder) throws - static func loadRootFrom(slice: Slice) throws -> Self -} - -extension Dictionary: CellCodable where Key: CellCodable & StaticSize, Value: CellCodable { - public static func loadFrom(slice: Slice) throws -> Self { - try DictionaryCoder.default().load(slice) - } - public func storeTo(builder: Builder) throws { - try DictionaryCoder.default().store(map: self, builder: builder) - } -} - -extension Dictionary: CellCodableDictionary where Key: CellCodable & StaticSize, Value: CellCodable { - - public func storeRootTo(builder: Builder) throws { - try DictionaryCoder.default().storeRoot(map: self, builder: builder) - } - - public static func loadRootFrom(slice: Slice) throws -> Self { - try DictionaryCoder.default().loadRoot(slice) - } -} - -extension Set: CellCodable where Element: CellCodable & StaticSize { - public static func loadFrom(slice: Slice) throws -> Self { - let dict: [Element: Empty] = try DictionaryCoder.default().load(slice) - return Set(dict.keys) - } - public func storeTo(builder: Builder) throws { - let dict: [Element: Empty] = Dictionary(uniqueKeysWithValues: map { k in (k, Empty()) }) - try DictionaryCoder.default().store(map: dict, builder: builder) - } -} - -extension Set: CellCodableDictionary where Element: CellCodable & StaticSize { - public static func loadRootFrom(slice: Slice) throws -> Self { - let dict: [Element: Empty] = try DictionaryCoder.default().loadRoot(slice) - return Set(dict.keys) - } - public func storeRootTo(builder: Builder) throws { - let dict: [Element: Empty] = Dictionary(uniqueKeysWithValues: map { k in (k, Empty()) }) - try DictionaryCoder.default().storeRoot(map: dict, builder: builder) - } -} - - - -/// Coder for the dictionaries that stores the coding rules for keys and values. -/// Use this explicit API when working with dynamically-sized dictionary keys. -/// In all other cases use `Dictionary` type where the key size is known at compile time. -public class DictionaryCoder where K.T: Hashable { - let keyLength: Int - let keyCoder: K - let valueCoder: V - - init(keyLength: Int, _ keyCoder: K, _ valueCoder: V) { - self.keyLength = keyLength - self.keyCoder = keyCoder - self.valueCoder = valueCoder - } - - static func `default`() -> DictionaryCoder - where KT: CellCodable & StaticSize & Hashable, - VT: CellCodable, - K == DefaultCoder, - V == DefaultCoder { - DictionaryCoder( - keyLength: KT.bitWidth, - DefaultCoder(), - DefaultCoder() - ) - } - - /// Loads dictionary from slice - public func load(_ slice: Slice) throws -> [K.T:V.T] { - let cell = try slice.loadMaybeRef() - if let cell, !cell.isExotic { - return try loadRoot(cell.beginParse()) - } else { - // TODO: review this decision to return empty dicts from exotic cells - // Steve Korshakov says the reason for this is that - // pruned branches should yield empty dicts somewhere down the line. - return [:] - } - } - - /// Loads root of the dictionary directly from a slice - public func loadRoot(_ slice: Slice) throws -> [K.T:V.T] { - var map = [K.T: V.T]() - try doParse(prefix: Builder(), slice: slice, n: keyLength, result: &map) - return map - } - - func store(map: [K.T: V.T], builder: Builder) throws { - if map.count == 0 { - try builder.store(bit: 0) - } else { - try builder.store(bit: 1) - let subcell = Builder() - try storeRoot(map: map, builder: subcell) - try builder.store(ref: try subcell.endCell()) - } - } - - func storeRoot(map: [K.T: V.T], builder: Builder) throws { - if map.count == 0 { - throw TonError.custom("Cannot store empty dictionary directly") - } - - // Serialize keys - var paddedMap: [Bitstring: V.T] = [:] - for (key, value) in map { - let builder = Builder() - try keyCoder.storeValue(key, to: builder) - let keybits = builder.bitstring() - let paddedKey = keybits.padLeft(keyLength) - paddedMap[paddedKey] = value - } - - // Calculate root label - let rootEdge = try buildEdge(paddedMap) - try writeEdge(src: rootEdge, keyLength: keyLength, valueCoder: valueCoder, to: builder) - } - - private func doParse(prefix: Builder, slice: Slice, n: Int, result: inout [K.T: V.T]) throws { - // Reading label - let k = bitsForInt(n) - var pfxlen: Int = 0 - - // short mode: $0 - if try slice.loadBit() == 0 { - // Read - pfxlen = try Unary.loadFrom(slice: slice).value - try prefix.store(bits: try slice.loadBits(pfxlen)) - } else { - // long mode: $10 - if try slice.loadBit() == 0 { - pfxlen = Int(try slice.loadUint(bits: k)) - try prefix.store(bits: try slice.loadBits(pfxlen)) - // same mode: $11 - } else { - // Same label detected - let bit = try slice.loadBit() - pfxlen = Int(try slice.loadUint(bits: k)) - try prefix.store(bit: bit, repeat: pfxlen) - } - } - - // We did read the whole prefix and reached the leaf: - // parse the value and store it in the dictionary. - if n - pfxlen == 0 { - let fullkey = prefix.bitstring() - let parsedKey = try keyCoder.loadValue(from: Cell(bits: fullkey).beginParse()) - result[parsedKey] = try valueCoder.loadValue(from: slice) - } else { - // We have to drill down the tree - let left = try slice.loadRef() - let right = try slice.loadRef() - // Note: left and right branches implicitly contain prefixes '0' and '1' - if !left.isExotic { - let prefixleft = prefix.clone() - try prefixleft.store(bit: 0) - try doParse(prefix: prefixleft, slice: left.beginParse(), n: n - pfxlen - 1, result: &result) - } - if !right.isExotic { - let prefixright = prefix.clone() - try prefixright.store(bit: 1) - try doParse(prefix: prefixright, slice: right.beginParse(), n: n - pfxlen - 1, result: &result) - } - } - } -} - - - -enum Node { - case fork(left: Edge, right: Edge) - case leaf(value: T) -} - -class Edge { - let label: Bitstring - let node: Node - - init(label: Bitstring, node: Node) { - self.label = label - self.node = node - } -} - -/// Removes `n` bits from all the keys in a map -func removePrefixMap(_ src: [Bitstring: T], _ length: Int) -> [Bitstring: T] { - if length == 0 { - return src - } - var res: [Bitstring: T] = [:] - for (k, d) in src { - res[try! k.dropFirst(length)] = d - } - return res -} - -/// Splits the dictionary by the value of the first bit of the keys. 0-prefixed keys go into left map, 1-prefixed keys go into the right one. -/// First bit is removed from the keys. -func forkMap(_ src: [Bitstring: T]) throws -> (left: [Bitstring: T], right: [Bitstring: T]) { - try invariant(!src.isEmpty) - - var left: [Bitstring: T] = [:] - var right: [Bitstring: T] = [:] - for (k, d) in src { - if k.at(unchecked: 0) == 0 { - left[try! k.dropFirst(1)] = d - } else { - right[try! k.dropFirst(1)] = d - } - } - - try invariant(!left.isEmpty) - try invariant(!right.isEmpty) - return (left, right) -} - -func buildNode(_ src: [Bitstring: T]) throws -> Node { - try invariant(!src.isEmpty) - if src.count == 1 { - return .leaf(value: src.values.first!) - } else { - let (left, right) = try forkMap(src) - return .fork(left: try buildEdge(left), right: try buildEdge(right)) - } -} - -func buildEdge(_ src: [Bitstring: T]) throws -> Edge { - try invariant(!src.isEmpty) - let label = findCommonPrefix(src: Array(src.keys)) - return Edge(label: label, node: try buildNode(removePrefixMap(src, label.length))) -} - - -/// Deterministically produces optimal label type for a given label. -/// This implementation is equivalent to the C++ reference implementation, and resolves the ties in the same way. -/// -/// From crypto/vm/dict.cpp: -/// ``` -/// void append_dict_label_same(CellBuilder& cb, bool same, int len, int max_len) { -/// int k = 32 - td::count_leading_zeroes32(max_len); -/// assert(len >= 0 && len <= max_len && max_len <= 1023); -/// // options: mode '0', requires 2n+2 bits (always for n=0) -/// // mode '10', requires 2+k+n bits (only for n<=1) -/// // mode '11', requires 3+k bits (for n>=2, k<2n-1) -/// if (len > 1 && k < 2 * len - 1) { -/// // mode '11' -/// cb.store_long(6 + same, 3).store_long(len, k); -/// } else if (k < len) { -/// // mode '10' -/// cb.store_long(2, 2).store_long(len, k).store_long(-static_cast(same), len); -/// } else { -/// // mode '0' -/// cb.store_long(0, 1).store_long(-2, len + 1).store_long(-static_cast(same), len); -/// } -/// } -/// -/// void append_dict_label(CellBuilder& cb, td::ConstBitPtr label, int len, int max_len) { -/// assert(len <= max_len && max_len <= 1023); -/// if (len > 0 && (int)td::bitstring::bits_memscan(label, len, *label) == len) { -/// return append_dict_label_same(cb, *label, len, max_len); -/// } -/// int k = 32 - td::count_leading_zeroes32(max_len); -/// // two options: mode '0', requires 2n+2 bits -/// // mode '10', requires 2+k+n bits -/// if (k < len) { -/// cb.store_long(2, 2).store_long(len, k); -/// } else { -/// cb.store_long(0, 1).store_long(-2, len + 1); -/// } -/// if ((int)cb.remaining_bits() < len) { -/// throw VmError{Excno::cell_ov, "cannot store a label into a dictionary cell"}; -/// } -/// cb.store_bits(label, len); -/// } -/// ``` -func writeLabel(src: Bitstring, keyLength: Int, to: Builder) throws { - let k = bitsForInt(keyLength) - let n = src.length - - // The goal is to choose the shortest encoding. - // In case of a tie, choose a lexicographically shorter one. - // Therefore, `short$0` comes ahead of `long$10` which is ahead of `same$11`. - // - // short mode '0' requires 2n+2 bits (always used for n=0) - // long mode '10' requires 2+k+n bits (used only for n<=1) - // same mode '11' requires 3+k bits (for n>=2, k<2n-1) - if let bit = src.repeatsSameBit(), n > 1 && k < 2 * n - 1 { // same mode '11' - try to.store(bits: 1, 1) // header - try to.store(bit: bit) // value - try to.store(uint: n, bits: k) // length - } else if k < n { // long mode '10' - try to.store(bits: 1, 0) // header - try to.store(uint: n, bits: k) // length - try to.store(bits: src) // the string itself - } else { // short mode '0' - try to.store(bit: 0) // header - try to.store(Unary(n)) // unary length prefix: 1{n}0 - try to.store(bits: src) // the string itself - } -} - -func writeNode(src: Node, keyLength: Int, valueCoder: V, to builder: Builder) throws where V: TypeCoder, V.T == T { - switch src { - case .fork(let left, let right): - let leftCell = Builder() - let rightCell = Builder() - - try writeEdge(src: left, keyLength: keyLength - 1, valueCoder: valueCoder, to: leftCell) - try writeEdge(src: right, keyLength: keyLength - 1, valueCoder: valueCoder, to: rightCell) - - try builder.store(ref: leftCell) - try builder.store(ref: rightCell) - - case .leaf(let value): - try valueCoder.storeValue(value, to: builder) - } -} - -func writeEdge(src: Edge, keyLength: Int, valueCoder: V, to: Builder) throws where V: TypeCoder, V.T == T { - try writeLabel(src: src.label, keyLength: keyLength, to: to) - try writeNode(src: src.node, keyLength: keyLength - src.label.length, valueCoder: valueCoder, to: to) -} - -func findCommonPrefix(src: some Collection) -> Bitstring { - // Corner cases - if src.isEmpty { - return Bitstring() - } - if src.count == 1 { - return src.first! - } - - // Searching for prefix - let sorted = src.sorted() - let first = sorted.first! - let last = sorted.last! - - var size = 0 - for i in 0.. Date: Thu, 20 Jul 2023 17:17:31 +0400 Subject: [PATCH 07/11] divide serialization --- Source/TonSwift/Cells/Builder.swift | 13 ++- Source/TonSwift/Cells/Cell/Cell.swift | 12 +- Source/TonSwift/Cells/Serialization.swift | 106 ------------------ .../Cells/Serialization/BytesCoder.swift | 22 ++++ .../Cells/Serialization/CellCodable.swift | 18 +++ .../Cells/Serialization/DefaultCoder.swift | 18 +++ .../TonSwift/Cells/Serialization/Empty.swift | 16 +++ .../Cells/Serialization/StaticSize.swift | 12 ++ .../Cells/Serialization/TypeCoder.swift | 23 ++++ Source/TonSwift/Cells/Slice.swift | 65 ++++------- 10 files changed, 152 insertions(+), 153 deletions(-) delete mode 100644 Source/TonSwift/Cells/Serialization.swift create mode 100644 Source/TonSwift/Cells/Serialization/BytesCoder.swift create mode 100644 Source/TonSwift/Cells/Serialization/CellCodable.swift create mode 100644 Source/TonSwift/Cells/Serialization/DefaultCoder.swift create mode 100644 Source/TonSwift/Cells/Serialization/Empty.swift create mode 100644 Source/TonSwift/Cells/Serialization/StaticSize.swift create mode 100644 Source/TonSwift/Cells/Serialization/TypeCoder.swift diff --git a/Source/TonSwift/Cells/Builder.swift b/Source/TonSwift/Cells/Builder.swift index 5297a62..1c819b5 100644 --- a/Source/TonSwift/Cells/Builder.swift +++ b/Source/TonSwift/Cells/Builder.swift @@ -1,7 +1,7 @@ import Foundation import BigInt -public class Builder { +public class Builder: CellCodable { public let capacity: Int private var _buffer: Data private var _length: Int @@ -490,4 +490,15 @@ public class Builder { throw TonError.custom("Builder overflow: need to write \(bits), but available \(availableBits)") } } + + // Builder is encoded inline + // MARK: - CellCodable + + public func storeTo(builder: Builder) throws { + try builder.store(slice: endCell().beginParse()) + } + + public static func loadFrom(slice: Slice) throws -> Self { + try slice.clone().toBuilder() as! Self + } } diff --git a/Source/TonSwift/Cells/Cell/Cell.swift b/Source/TonSwift/Cells/Cell/Cell.swift index 95d3076..0ad44cc 100644 --- a/Source/TonSwift/Cells/Cell/Cell.swift +++ b/Source/TonSwift/Cells/Cell/Cell.swift @@ -7,7 +7,7 @@ public let BitsPerCell = 1023 public let RefsPerCell = 4 /// TON cell -public struct Cell: Hashable, Equatable { +public struct Cell: Hashable, Equatable, CellCodable { private let basic: BasicCell @@ -183,6 +183,16 @@ public struct Cell: Hashable, Equatable { public static func == (lhs: Cell, rhs: Cell) -> Bool { lhs.hash() == rhs.hash() } + + // Cell is encoded as a separate ref + // MARK: - CellCodable + public func storeTo(builder: Builder) throws { + try builder.store(ref: self) + } + + public static func loadFrom(slice: Slice) throws -> Self { + try slice.loadRef() + } } // MARK: - Internal implementation diff --git a/Source/TonSwift/Cells/Serialization.swift b/Source/TonSwift/Cells/Serialization.swift deleted file mode 100644 index 644c8d5..0000000 --- a/Source/TonSwift/Cells/Serialization.swift +++ /dev/null @@ -1,106 +0,0 @@ -import Foundation - -/// Types implementing both reading and writing -public protocol CellCodable { - func storeTo(builder: Builder) throws - static func loadFrom(slice: Slice) throws -> Self -} - -/// Types implement KnownSize protocol when they have statically-known size in bits -public protocol StaticSize { - /// Size of the type in bits - static var bitWidth: Int { get } -} - -/// Every type that can be used as a dictionary value has an accompanying coder object configured to read that type. -/// This protocol allows implement dependent types because the exact instance would have runtime parameter such as bitlength for the values of this type. -public protocol TypeCoder { - associatedtype T - func storeValue(_ src: T, to builder: Builder) throws - func loadValue(from src: Slice) throws -> T -} - -extension CellCodable { - static func defaultCoder() -> some TypeCoder { - DefaultCoder() - } -} - -public class DefaultCoder: TypeCoder { - public typealias T = X - public func storeValue(_ src: T, to builder: Builder) throws { - try src.storeTo(builder: builder) - } - public func loadValue(from src: Slice) throws -> T { - try T.loadFrom(slice: src) - } -} - -public extension TypeCoder { - /// Serializes type to Cell - func serializeToCell(_ src: T) throws -> Cell { - let builder = Builder() - try storeValue(src, to: builder) - return try builder.endCell() - } -} - - -public class BytesCoder: TypeCoder { - public typealias T = Data - let size: Int - - init(size: Int) { - self.size = size - } - - public func storeValue(_ src: T, to builder: Builder) throws { - try builder.store(data: src) - } - public func loadValue(from src: Slice) throws -> T { - try src.loadBytes(size) - } -} - -// Cell is encoded as a separate ref -extension Cell: CellCodable { - public func storeTo(builder: Builder) throws { - try builder.store(ref: self) - } - - public static func loadFrom(slice: Slice) throws -> Self { - try slice.loadRef() - } -} - -// Slice is encoded inline -extension Slice: CellCodable { - public func storeTo(builder: Builder) throws { - try builder.store(slice: self) - } - - public static func loadFrom(slice: Slice) throws -> Self { - slice.clone() as! Self - } -} - -// Builder is encoded inline -extension Builder: CellCodable { - public func storeTo(builder: Builder) throws { - try builder.store(slice: endCell().beginParse()) - } - - public static func loadFrom(slice: Slice) throws -> Self { - try slice.clone().toBuilder() as! Self - } -} - -/// Empty struct to store empty leafs in the dictionaries to form sets. -public struct Empty: CellCodable { - public static func loadFrom(slice: Slice) throws -> Self { - Empty() - } - public func storeTo(builder: Builder) throws { - // store nothing - } -} diff --git a/Source/TonSwift/Cells/Serialization/BytesCoder.swift b/Source/TonSwift/Cells/Serialization/BytesCoder.swift new file mode 100644 index 0000000..6bd891d --- /dev/null +++ b/Source/TonSwift/Cells/Serialization/BytesCoder.swift @@ -0,0 +1,22 @@ +// +// BytesCoder.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +public class BytesCoder: TypeCoder { + public typealias T = Data + let size: Int + + init(size: Int) { + self.size = size + } + + public func storeValue(_ src: T, to builder: Builder) throws { + try builder.store(data: src) + } + public func loadValue(from src: Slice) throws -> T { + try src.loadBytes(size) + } +} diff --git a/Source/TonSwift/Cells/Serialization/CellCodable.swift b/Source/TonSwift/Cells/Serialization/CellCodable.swift new file mode 100644 index 0000000..f3f6314 --- /dev/null +++ b/Source/TonSwift/Cells/Serialization/CellCodable.swift @@ -0,0 +1,18 @@ +// +// CellCodable.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +/// Types implementing both reading and writing +public protocol CellCodable { + func storeTo(builder: Builder) throws + static func loadFrom(slice: Slice) throws -> Self +} + +extension CellCodable { + static func defaultCoder() -> some TypeCoder { + DefaultCoder() + } +} diff --git a/Source/TonSwift/Cells/Serialization/DefaultCoder.swift b/Source/TonSwift/Cells/Serialization/DefaultCoder.swift new file mode 100644 index 0000000..7ccf1a7 --- /dev/null +++ b/Source/TonSwift/Cells/Serialization/DefaultCoder.swift @@ -0,0 +1,18 @@ +// +// DefaultCoder.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +public class DefaultCoder: TypeCoder { + public typealias T = X + + public func storeValue(_ src: T, to builder: Builder) throws { + try src.storeTo(builder: builder) + } + + public func loadValue(from src: Slice) throws -> T { + try T.loadFrom(slice: src) + } +} diff --git a/Source/TonSwift/Cells/Serialization/Empty.swift b/Source/TonSwift/Cells/Serialization/Empty.swift new file mode 100644 index 0000000..a51bb7d --- /dev/null +++ b/Source/TonSwift/Cells/Serialization/Empty.swift @@ -0,0 +1,16 @@ +// +// Empty.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +/// Empty struct to store empty leafs in the dictionaries to form sets. +public struct Empty: CellCodable { + public static func loadFrom(slice: Slice) throws -> Self { + Empty() + } + public func storeTo(builder: Builder) throws { + // store nothing + } +} diff --git a/Source/TonSwift/Cells/Serialization/StaticSize.swift b/Source/TonSwift/Cells/Serialization/StaticSize.swift new file mode 100644 index 0000000..7e815ff --- /dev/null +++ b/Source/TonSwift/Cells/Serialization/StaticSize.swift @@ -0,0 +1,12 @@ +// +// StaticSize.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +/// Types implement KnownSize protocol when they have statically-known size in bits +public protocol StaticSize { + /// Size of the type in bits + static var bitWidth: Int { get } +} diff --git a/Source/TonSwift/Cells/Serialization/TypeCoder.swift b/Source/TonSwift/Cells/Serialization/TypeCoder.swift new file mode 100644 index 0000000..9656115 --- /dev/null +++ b/Source/TonSwift/Cells/Serialization/TypeCoder.swift @@ -0,0 +1,23 @@ +// +// TypeCoder.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +/// Every type that can be used as a dictionary value has an accompanying coder object configured to read that type. +/// This protocol allows implement dependent types because the exact instance would have runtime parameter such as bitlength for the values of this type. +public protocol TypeCoder { + associatedtype T + func storeValue(_ src: T, to builder: Builder) throws + func loadValue(from src: Slice) throws -> T +} + +public extension TypeCoder { + /// Serializes type to Cell + func serializeToCell(_ src: T) throws -> Cell { + let builder = Builder() + try storeValue(src, to: builder) + return try builder.endCell() + } +} diff --git a/Source/TonSwift/Cells/Slice.swift b/Source/TonSwift/Cells/Slice.swift index c06d64e..bb0dc8e 100644 --- a/Source/TonSwift/Cells/Slice.swift +++ b/Source/TonSwift/Cells/Slice.swift @@ -3,14 +3,12 @@ import BigInt /// `Slice` is a class that allows to read cell data (bits and refs), consuming it along the way. /// Once you have done reading and want to make sure all the data is consumed, call `endParse()`. -public class Slice { +public class Slice: CellCodable { private var bitstring: Bitstring private var offset: Int private var refs: [Cell] - - // MARK: - Initializers init(cell: Cell) { @@ -40,12 +38,9 @@ public class Slice { self.offset = offset self.refs = refs } - - - + // MARK: - Metrics - - + /// Remaining unread refs in this slice. public var remainingRefs: Int { refs.count @@ -55,13 +50,9 @@ public class Slice { public var remainingBits: Int { bitstring.length - offset } - - - - + // MARK: - Finalization - - + /// Checks if the cell is fully processed without unread bits or refs. public func endParse() throws { if remainingBits > 0 || remainingRefs > 0 { @@ -97,10 +88,6 @@ public class Slice { try loadRemainder().toString() } - - - - // MARK: - Loading Generic Types /// Loads type T that implements interface Readable @@ -129,9 +116,7 @@ public class Slice { refs = tmpslice.refs return result } - - - + // MARK: - Loading Refs /// Loads a cell reference. @@ -159,11 +144,9 @@ public class Slice { public func preloadMaybeRef() throws -> Cell? { try preloadBit() == 1 ? try preloadRef() : nil } - - - + // MARK: - Loading Dictionaries - + /// Reads a dictionary from the slice. public func loadDict() throws -> T where T: CellCodableDictionary { @@ -175,12 +158,8 @@ public class Slice { try T.loadRootFrom(slice: self) } - - - // MARK: - Loading Bits - - + /// Advances cursor by the specified numbe rof bits. public func skip(_ bits: Int) throws { if bits < 0 || offset + bits > bitstring.length { @@ -265,13 +244,9 @@ public class Slice { return substring } - - - // MARK: - Loading Integers - /** Load uint value - parameter bits: uint bits @@ -352,10 +327,6 @@ public class Slice { public func loadMaybeUintBig(bits: Int) throws -> BigUInt? { try loadBoolean() ? try loadUintBig(bits: bits) : nil } - - - - // MARK: - Loading Variable-Length Integers @@ -382,13 +353,6 @@ public class Slice { } return try loadUintBig(bits: size * 8) } - - - - - - - // MARK: - Private methods @@ -474,4 +438,15 @@ public class Slice { return buf } + + // Slice is encoded inline + // MARK: - CellCodable { + + public func storeTo(builder: Builder) throws { + try builder.store(slice: self) + } + + public static func loadFrom(slice: Slice) throws -> Self { + slice.clone() as! Self + } } From dfe4f7f735d6e93bab055fdd7ad858f90795c795 Mon Sep 17 00:00:00 2001 From: Kirill Kirilenko Date: Thu, 20 Jul 2023 17:20:17 +0400 Subject: [PATCH 08/11] divide SnakeEncoding --- Source/TonSwift/Cells/Boc.swift | 2 + Source/TonSwift/Cells/Builder.swift | 25 ++++++ Source/TonSwift/Cells/Cell/Cell.swift | 2 +- Source/TonSwift/Cells/Slice.swift | 44 +++++++++++ Source/TonSwift/Cells/SnakeEncoding.swift | 78 ------------------- .../TonSwift/Util/String+CellEncoding.swift | 9 +++ 6 files changed, 81 insertions(+), 79 deletions(-) delete mode 100644 Source/TonSwift/Cells/SnakeEncoding.swift create mode 100644 Source/TonSwift/Util/String+CellEncoding.swift diff --git a/Source/TonSwift/Cells/Boc.swift b/Source/TonSwift/Cells/Boc.swift index 390af33..4984986 100644 --- a/Source/TonSwift/Cells/Boc.swift +++ b/Source/TonSwift/Cells/Boc.swift @@ -72,6 +72,8 @@ struct Boc { } } +// TODO: Make this functions not global + func getRefsDescriptor(refs: [Cell], level: UInt32, type: CellType) -> UInt8 { let typeFactor: UInt8 = type != .ordinary ? 1 : 0 return UInt8(refs.count) + typeFactor * 8 + UInt8(level) * 32 diff --git a/Source/TonSwift/Cells/Builder.swift b/Source/TonSwift/Cells/Builder.swift index 1c819b5..1af1a2b 100644 --- a/Source/TonSwift/Cells/Builder.swift +++ b/Source/TonSwift/Cells/Builder.swift @@ -501,4 +501,29 @@ public class Builder: CellCodable { public static func loadFrom(slice: Slice) throws -> Self { try slice.clone().toBuilder() as! Self } + + // MARK: SnakeEncoding + + /// Writes snake-encoded data + @discardableResult + public func writeSnakeData(_ src: Data) throws -> Self { + if src.count > 0 { + let bytes = Int(floor(Double(availableBits / 8))) + if src.count > bytes { + let a = src.subdata(in: 0.. Self { + try writeSnakeData(Data(src.utf8)) + } } diff --git a/Source/TonSwift/Cells/Cell/Cell.swift b/Source/TonSwift/Cells/Cell/Cell.swift index 0ad44cc..f3754ae 100644 --- a/Source/TonSwift/Cells/Cell/Cell.swift +++ b/Source/TonSwift/Cells/Cell/Cell.swift @@ -195,7 +195,7 @@ public struct Cell: Hashable, Equatable, CellCodable { } } -// MARK: - Internal implementation +// TODO: Make this functions not global func getRepr(bits: Bitstring, refs: [Cell], level: UInt32, type: CellType) throws -> Data { // Allocate diff --git a/Source/TonSwift/Cells/Slice.swift b/Source/TonSwift/Cells/Slice.swift index bb0dc8e..5fde304 100644 --- a/Source/TonSwift/Cells/Slice.swift +++ b/Source/TonSwift/Cells/Slice.swift @@ -449,4 +449,48 @@ public class Slice: CellCodable { public static func loadFrom(slice: Slice) throws -> Self { slice.clone() as! Self } + + // MARK: - Snake Encoding + + /// Loads snake-encoded String. + /// Fails if the string is malformed or not a valid UTF-8 string. + public func loadSnakeString() throws -> String { + guard let str = String(data: try loadSnakeData(), encoding: .utf8) else { + throw TonError.custom("Cannot read slice to string") + } + return str + } + + /// Loads snake-encoded Data. Fails if the binary string is malformed. + public func loadSnakeData() throws -> Data { + // Check consistency + if remainingBits % 8 != 0 { + throw TonError.custom("Invalid string length: \(remainingBits)") + } + if remainingRefs != 0 && remainingRefs != 1 { + throw TonError.custom("Invalid number of refs: \(remainingRefs)") + } + if remainingRefs == 1 && (BitsPerCell - remainingBits) > 7 { + throw TonError.custom("Invalid string length: \(remainingBits / 8)") + } + + // Read string + var res = Data() + if remainingBits == 0 { + res = Data() + } else { + res = try loadBytes(remainingBits / 8) + } + + // Read tail + if remainingRefs == 1 { + res.append( + try loadRef() + .beginParse() + .loadSnakeData() + ) + } + + return res + } } diff --git a/Source/TonSwift/Cells/SnakeEncoding.swift b/Source/TonSwift/Cells/SnakeEncoding.swift deleted file mode 100644 index de9ce33..0000000 --- a/Source/TonSwift/Cells/SnakeEncoding.swift +++ /dev/null @@ -1,78 +0,0 @@ -import Foundation - -extension Slice { - /// Loads snake-encoded String. - /// Fails if the string is malformed or not a valid UTF-8 string. - public func loadSnakeString() throws -> String { - guard let str = String(data: try loadSnakeData(), encoding: .utf8) else { - throw TonError.custom("Cannot read slice to string") - } - return str - } - - /// Loads snake-encoded Data. Fails if the binary string is malformed. - public func loadSnakeData() throws -> Data { - // Check consistency - if remainingBits % 8 != 0 { - throw TonError.custom("Invalid string length: \(remainingBits)") - } - if remainingRefs != 0 && remainingRefs != 1 { - throw TonError.custom("Invalid number of refs: \(remainingRefs)") - } - if remainingRefs == 1 && (BitsPerCell - remainingBits) > 7 { - throw TonError.custom("Invalid string length: \(remainingBits / 8)") - } - - // Read string - var res = Data() - if remainingBits == 0 { - res = Data() - } else { - res = try loadBytes(remainingBits / 8) - } - - // Read tail - if remainingRefs == 1 { - res.append( - try loadRef() - .beginParse() - .loadSnakeData() - ) - } - - return res - } -} - -extension Builder { - /// Writes snake-encoded data - @discardableResult - public func writeSnakeData(_ src: Data) throws -> Self { - if src.count > 0 { - let bytes = Int(floor(Double(availableBits / 8))) - if src.count > bytes { - let a = src.subdata(in: 0.. Self { - try writeSnakeData(Data(src.utf8)) - } -} - -extension String { - /// Encodes a String into a Cell - public func toTonCell() throws -> Cell { - try Builder().writeSnakeData(Data(utf8)).endCell() - } -} - diff --git a/Source/TonSwift/Util/String+CellEncoding.swift b/Source/TonSwift/Util/String+CellEncoding.swift new file mode 100644 index 0000000..0d3e15a --- /dev/null +++ b/Source/TonSwift/Util/String+CellEncoding.swift @@ -0,0 +1,9 @@ +import Foundation + +extension String { + /// Encodes a String into a Cell + public func toTonCell() throws -> Cell { + try Builder().writeSnakeData(Data(utf8)).endCell() + } +} + From 66e3df5391e815c487f5ca601426c77ff30adeca Mon Sep 17 00:00:00 2001 From: Kirill Kirilenko Date: Thu, 20 Jul 2023 17:26:22 +0400 Subject: [PATCH 09/11] divide coins --- .../Cells/Serialization/BytesCoder.swift | 2 + Source/TonSwift/Contracts/Coins.swift | 76 ------------------- .../Contracts/Coins/Builder+Coins.swift | 30 ++++++++ Source/TonSwift/Contracts/Coins/Coins.swift | 34 +++++++++ .../Contracts/Coins/Slice+Coins.swift | 23 ++++++ 5 files changed, 89 insertions(+), 76 deletions(-) delete mode 100644 Source/TonSwift/Contracts/Coins.swift create mode 100644 Source/TonSwift/Contracts/Coins/Builder+Coins.swift create mode 100644 Source/TonSwift/Contracts/Coins/Coins.swift create mode 100644 Source/TonSwift/Contracts/Coins/Slice+Coins.swift diff --git a/Source/TonSwift/Cells/Serialization/BytesCoder.swift b/Source/TonSwift/Cells/Serialization/BytesCoder.swift index 6bd891d..f2b5ca7 100644 --- a/Source/TonSwift/Cells/Serialization/BytesCoder.swift +++ b/Source/TonSwift/Cells/Serialization/BytesCoder.swift @@ -5,6 +5,8 @@ // Created by Kirill Kirilenko on 20/07/2023. // +import Foundation + public class BytesCoder: TypeCoder { public typealias T = Data let size: Int diff --git a/Source/TonSwift/Contracts/Coins.swift b/Source/TonSwift/Contracts/Coins.swift deleted file mode 100644 index 3b140f8..0000000 --- a/Source/TonSwift/Contracts/Coins.swift +++ /dev/null @@ -1,76 +0,0 @@ -import Foundation -import BigInt - -/// 128-bit integer representing base TON currency: toncoins (aka `grams` in block.tlb). -public struct Coins { - var amount: BigUInt - - init(_ a: some BinaryInteger) { - // we use signed integer here because of `0` literal is a signed Int. - amount = BigUInt(a) - } -} - -extension Coins: RawRepresentable { - public typealias RawValue = BigUInt - - public init?(rawValue: BigUInt) { - amount = rawValue - } - - public var rawValue: BigUInt { - amount - } -} - -extension Coins: CellCodable { - public func storeTo(builder: Builder) throws { - try builder.store(varuint: amount, limit: 16) - } - public static func loadFrom(slice: Slice) throws -> Coins { - Coins(try slice.loadVarUintBig(limit: 16)) - } -} - -extension Slice { - /// Loads Coins value - public func loadCoins() throws -> Coins { - try loadType() - } - - /// Preloads Coins value - public func preloadCoins() throws -> Coins { - try preloadType() - } - - /// Load optionals Coins value. - public func loadMaybeCoins() throws -> Coins? { - try loadBoolean() ? try loadCoins() : nil - } -} - -extension Builder { - - /// Write coins amount in varuint format - @discardableResult - func store(coins: Coins) throws -> Self { - try store(varuint: coins.amount, limit: 16) - } - - /** - * Store optional coins value - * @param amount amount of coins, null or undefined - * @returns this builder - */ - @discardableResult - public func storeMaybe(coins: Coins?) throws -> Self { - if let coins { - try store(bit: true) - try store(coins: coins) - } else { - try store(bit: false) - } - return self - } - -} diff --git a/Source/TonSwift/Contracts/Coins/Builder+Coins.swift b/Source/TonSwift/Contracts/Coins/Builder+Coins.swift new file mode 100644 index 0000000..e0a1b2b --- /dev/null +++ b/Source/TonSwift/Contracts/Coins/Builder+Coins.swift @@ -0,0 +1,30 @@ +// +// Builder+Coins.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +extension Builder { + /// Write coins amount in varuint format + @discardableResult + func store(coins: Coins) throws -> Self { + try store(varuint: coins.amount, limit: 16) + } + + /** + * Store optional coins value + * @param amount amount of coins, null or undefined + * @returns this builder + */ + @discardableResult + public func storeMaybe(coins: Coins?) throws -> Self { + if let coins { + try store(bit: true) + try store(coins: coins) + } else { + try store(bit: false) + } + return self + } +} diff --git a/Source/TonSwift/Contracts/Coins/Coins.swift b/Source/TonSwift/Contracts/Coins/Coins.swift new file mode 100644 index 0000000..f41b683 --- /dev/null +++ b/Source/TonSwift/Contracts/Coins/Coins.swift @@ -0,0 +1,34 @@ +import Foundation +import BigInt + +/// 128-bit integer representing base TON currency: toncoins (aka `grams` in block.tlb). +public struct Coins: RawRepresentable, CellCodable { + var amount: BigUInt + + init(_ a: some BinaryInteger) { + // we use signed integer here because of `0` literal is a signed Int. + amount = BigUInt(a) + } + + // MARK: RawRepresentable + + public typealias RawValue = BigUInt + + public init?(rawValue: BigUInt) { + amount = rawValue + } + + public var rawValue: BigUInt { + amount + } + + // MARK: CellCodable + + public func storeTo(builder: Builder) throws { + try builder.store(varuint: amount, limit: 16) + } + + public static func loadFrom(slice: Slice) throws -> Coins { + Coins(try slice.loadVarUintBig(limit: 16)) + } +} diff --git a/Source/TonSwift/Contracts/Coins/Slice+Coins.swift b/Source/TonSwift/Contracts/Coins/Slice+Coins.swift new file mode 100644 index 0000000..7bf7b32 --- /dev/null +++ b/Source/TonSwift/Contracts/Coins/Slice+Coins.swift @@ -0,0 +1,23 @@ +// +// Slice+Coins.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +extension Slice { + /// Loads Coins value + public func loadCoins() throws -> Coins { + try loadType() + } + + /// Preloads Coins value + public func preloadCoins() throws -> Coins { + try preloadType() + } + + /// Load optionals Coins value. + public func loadMaybeCoins() throws -> Coins? { + try loadBoolean() ? try loadCoins() : nil + } +} From 07de8fbe9c6a1247d8da8f6012e4170bf64aa5c1 Mon Sep 17 00:00:00 2001 From: Kirill Kirilenko Date: Thu, 20 Jul 2023 17:37:28 +0400 Subject: [PATCH 10/11] divide files in contracts --- .../{ => CommonMsgInfo}/CommonMsgInfo.swift | 26 ---- .../CommonMsgInfoExternalIn.swift | 12 ++ .../CommonMsgInfoExternalOut.swift | 13 ++ .../CommonMsgInfo/CommonMsgInfoInternal.swift | 19 +++ .../CommonMsgInfoRelaxed.swift | 20 --- .../CommonMsgInfoRelaxedExternalOut.swift | 13 ++ .../CommonMsgInfoRelaxedInternal.swift | 19 +++ Source/TonSwift/Contracts/Contract.swift | 44 ------ .../Contracts/Contract/Contract.swift | 16 +++ .../Contracts/Contract/ContractState.swift | 14 ++ .../Contract/ContractStateLast.swift | 14 ++ .../Contract/ContractStateStatus.swift | 14 ++ .../Contracts/Contract/OpaqueContract.swift | 16 +++ Source/TonSwift/Contracts/SendMode.swift | 126 ------------------ .../Contracts/SendMode/SendMode.swift | 52 ++++++++ .../Contracts/SendMode/SendModeTypes.swift | 36 +++++ .../Contracts/SendMode/SendValueOptions.swift | 50 +++++++ Source/TonSwift/Contracts/Sender/Sender.swift | 4 + .../SenderArguments.swift} | 13 +- Source/TonSwift/Contracts/SimpleLibrary.swift | 27 ++++ Source/TonSwift/Contracts/StateInit.swift | 41 ------ Source/TonSwift/Contracts/TickTock.swift | 26 +++- 22 files changed, 351 insertions(+), 264 deletions(-) rename Source/TonSwift/Contracts/{ => CommonMsgInfo}/CommonMsgInfo.swift (89%) create mode 100644 Source/TonSwift/Contracts/CommonMsgInfo/CommonMsgInfoExternalIn.swift create mode 100644 Source/TonSwift/Contracts/CommonMsgInfo/CommonMsgInfoExternalOut.swift create mode 100644 Source/TonSwift/Contracts/CommonMsgInfo/CommonMsgInfoInternal.swift rename Source/TonSwift/Contracts/{ => CommonMsgInfoRelaxed}/CommonMsgInfoRelaxed.swift (89%) create mode 100644 Source/TonSwift/Contracts/CommonMsgInfoRelaxed/CommonMsgInfoRelaxedExternalOut.swift create mode 100644 Source/TonSwift/Contracts/CommonMsgInfoRelaxed/CommonMsgInfoRelaxedInternal.swift delete mode 100644 Source/TonSwift/Contracts/Contract.swift create mode 100644 Source/TonSwift/Contracts/Contract/Contract.swift create mode 100644 Source/TonSwift/Contracts/Contract/ContractState.swift create mode 100644 Source/TonSwift/Contracts/Contract/ContractStateLast.swift create mode 100644 Source/TonSwift/Contracts/Contract/ContractStateStatus.swift create mode 100644 Source/TonSwift/Contracts/Contract/OpaqueContract.swift delete mode 100644 Source/TonSwift/Contracts/SendMode.swift create mode 100644 Source/TonSwift/Contracts/SendMode/SendMode.swift create mode 100644 Source/TonSwift/Contracts/SendMode/SendModeTypes.swift create mode 100644 Source/TonSwift/Contracts/SendMode/SendValueOptions.swift create mode 100644 Source/TonSwift/Contracts/Sender/Sender.swift rename Source/TonSwift/Contracts/{Sender.swift => Sender/SenderArguments.swift} (60%) create mode 100644 Source/TonSwift/Contracts/SimpleLibrary.swift diff --git a/Source/TonSwift/Contracts/CommonMsgInfo.swift b/Source/TonSwift/Contracts/CommonMsgInfo/CommonMsgInfo.swift similarity index 89% rename from Source/TonSwift/Contracts/CommonMsgInfo.swift rename to Source/TonSwift/Contracts/CommonMsgInfo/CommonMsgInfo.swift index 7773ca0..fe2899e 100644 --- a/Source/TonSwift/Contracts/CommonMsgInfo.swift +++ b/Source/TonSwift/Contracts/CommonMsgInfo/CommonMsgInfo.swift @@ -22,32 +22,6 @@ import Foundation created_at:uint32 = CommonMsgInfo; */ -public struct CommonMsgInfoInternal { - let ihrDisabled: Bool - let bounce: Bool - let bounced: Bool - let src: Address - let dest: Address - let value: CurrencyCollection - let ihrFee: Coins - let forwardFee: Coins - let createdLt: UInt64 - let createdAt: UInt32 -} - -public struct CommonMsgInfoExternalIn { - let src: ExternalAddress? - let dest: Address - let importFee: Coins -} - -public struct CommonMsgInfoExternalOut { - let src: Address - let dest: ExternalAddress? - let createdLt: UInt64 - let createdAt: UInt32 -} - public enum CommonMsgInfo: CellCodable { case internalInfo(info: CommonMsgInfoInternal) case externalOutInfo(info: CommonMsgInfoExternalOut) diff --git a/Source/TonSwift/Contracts/CommonMsgInfo/CommonMsgInfoExternalIn.swift b/Source/TonSwift/Contracts/CommonMsgInfo/CommonMsgInfoExternalIn.swift new file mode 100644 index 0000000..e653251 --- /dev/null +++ b/Source/TonSwift/Contracts/CommonMsgInfo/CommonMsgInfoExternalIn.swift @@ -0,0 +1,12 @@ +// +// CommonMsgInfoExternalIn.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +public struct CommonMsgInfoExternalIn { + let src: ExternalAddress? + let dest: Address + let importFee: Coins +} diff --git a/Source/TonSwift/Contracts/CommonMsgInfo/CommonMsgInfoExternalOut.swift b/Source/TonSwift/Contracts/CommonMsgInfo/CommonMsgInfoExternalOut.swift new file mode 100644 index 0000000..880bdf4 --- /dev/null +++ b/Source/TonSwift/Contracts/CommonMsgInfo/CommonMsgInfoExternalOut.swift @@ -0,0 +1,13 @@ +// +// CommonMsgInfoExternalOut.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +public struct CommonMsgInfoExternalOut { + let src: Address + let dest: ExternalAddress? + let createdLt: UInt64 + let createdAt: UInt32 +} diff --git a/Source/TonSwift/Contracts/CommonMsgInfo/CommonMsgInfoInternal.swift b/Source/TonSwift/Contracts/CommonMsgInfo/CommonMsgInfoInternal.swift new file mode 100644 index 0000000..81ef746 --- /dev/null +++ b/Source/TonSwift/Contracts/CommonMsgInfo/CommonMsgInfoInternal.swift @@ -0,0 +1,19 @@ +// +// CommonMsgInfoInternal.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +public struct CommonMsgInfoInternal { + let ihrDisabled: Bool + let bounce: Bool + let bounced: Bool + let src: Address + let dest: Address + let value: CurrencyCollection + let ihrFee: Coins + let forwardFee: Coins + let createdLt: UInt64 + let createdAt: UInt32 +} diff --git a/Source/TonSwift/Contracts/CommonMsgInfoRelaxed.swift b/Source/TonSwift/Contracts/CommonMsgInfoRelaxed/CommonMsgInfoRelaxed.swift similarity index 89% rename from Source/TonSwift/Contracts/CommonMsgInfoRelaxed.swift rename to Source/TonSwift/Contracts/CommonMsgInfoRelaxed/CommonMsgInfoRelaxed.swift index c0e1461..dd5ac89 100644 --- a/Source/TonSwift/Contracts/CommonMsgInfoRelaxed.swift +++ b/Source/TonSwift/Contracts/CommonMsgInfoRelaxed/CommonMsgInfoRelaxed.swift @@ -22,26 +22,6 @@ import Foundation created_at:uint32 = CommonMsgInfoRelaxed; */ -public struct CommonMsgInfoRelaxedInternal { - let ihrDisabled: Bool - let bounce: Bool - let bounced: Bool - let src: AnyAddress - let dest: Address - let value: CurrencyCollection - let ihrFee: Coins - let forwardFee: Coins - let createdLt: UInt64 - let createdAt: UInt32 -} - -public struct CommonMsgInfoRelaxedExternalOut { - let src: AnyAddress - let dest: ExternalAddress? - let createdLt: UInt64 - let createdAt: UInt32 -} - public enum CommonMsgInfoRelaxed: CellCodable { case internalInfo(info: CommonMsgInfoRelaxedInternal) case externalOutInfo(info: CommonMsgInfoRelaxedExternalOut) diff --git a/Source/TonSwift/Contracts/CommonMsgInfoRelaxed/CommonMsgInfoRelaxedExternalOut.swift b/Source/TonSwift/Contracts/CommonMsgInfoRelaxed/CommonMsgInfoRelaxedExternalOut.swift new file mode 100644 index 0000000..2b35989 --- /dev/null +++ b/Source/TonSwift/Contracts/CommonMsgInfoRelaxed/CommonMsgInfoRelaxedExternalOut.swift @@ -0,0 +1,13 @@ +// +// CommonMsgInfoRelaxedExternalOut.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +public struct CommonMsgInfoRelaxedExternalOut { + let src: AnyAddress + let dest: ExternalAddress? + let createdLt: UInt64 + let createdAt: UInt32 +} diff --git a/Source/TonSwift/Contracts/CommonMsgInfoRelaxed/CommonMsgInfoRelaxedInternal.swift b/Source/TonSwift/Contracts/CommonMsgInfoRelaxed/CommonMsgInfoRelaxedInternal.swift new file mode 100644 index 0000000..9f84049 --- /dev/null +++ b/Source/TonSwift/Contracts/CommonMsgInfoRelaxed/CommonMsgInfoRelaxedInternal.swift @@ -0,0 +1,19 @@ +// +// CommonMsgInfoRelaxedInternal.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +public struct CommonMsgInfoRelaxedInternal { + let ihrDisabled: Bool + let bounce: Bool + let bounced: Bool + let src: AnyAddress + let dest: Address + let value: CurrencyCollection + let ihrFee: Coins + let forwardFee: Coins + let createdLt: UInt64 + let createdAt: UInt32 +} diff --git a/Source/TonSwift/Contracts/Contract.swift b/Source/TonSwift/Contracts/Contract.swift deleted file mode 100644 index 8e35f88..0000000 --- a/Source/TonSwift/Contracts/Contract.swift +++ /dev/null @@ -1,44 +0,0 @@ -import Foundation -import BigInt - -/// Common interface for all contracts that allows computing contract addresses and messages -public protocol Contract { - var workchain: Int8 { get } - var stateInit: StateInit { get } - func address() throws -> Address -} - -extension Contract { - public func address() throws -> Address { - let hash = try Builder().store(stateInit).endCell().hash() - return Address(workchain: workchain, hash: hash) - } -} - -public struct ContractState { - let balance: BigInt - let last: ContractStateLast? - let state: ContractStateStatus -} - -public struct ContractStateLast { - let lt: BigInt - let hash: Data -} - -public enum ContractStateStatus { - case uninit - case active(code: Data?, data: Data?) - case frozen(stateHash: Data?) -} - - -public struct OpaqueContract: Contract { - public let workchain: Int8 - public let stateInit: StateInit - - init(workchain: Int8, stateInit: StateInit) { - self.workchain = workchain - self.stateInit = stateInit - } -} diff --git a/Source/TonSwift/Contracts/Contract/Contract.swift b/Source/TonSwift/Contracts/Contract/Contract.swift new file mode 100644 index 0000000..1265c65 --- /dev/null +++ b/Source/TonSwift/Contracts/Contract/Contract.swift @@ -0,0 +1,16 @@ +import Foundation +import BigInt + +/// Common interface for all contracts that allows computing contract addresses and messages +public protocol Contract { + var workchain: Int8 { get } + var stateInit: StateInit { get } + func address() throws -> Address +} + +extension Contract { + public func address() throws -> Address { + let hash = try Builder().store(stateInit).endCell().hash() + return Address(workchain: workchain, hash: hash) + } +} diff --git a/Source/TonSwift/Contracts/Contract/ContractState.swift b/Source/TonSwift/Contracts/Contract/ContractState.swift new file mode 100644 index 0000000..f91651e --- /dev/null +++ b/Source/TonSwift/Contracts/Contract/ContractState.swift @@ -0,0 +1,14 @@ +// +// ContractState.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +import BigInt + +public struct ContractState { + let balance: BigInt + let last: ContractStateLast? + let state: ContractStateStatus +} diff --git a/Source/TonSwift/Contracts/Contract/ContractStateLast.swift b/Source/TonSwift/Contracts/Contract/ContractStateLast.swift new file mode 100644 index 0000000..e5273fd --- /dev/null +++ b/Source/TonSwift/Contracts/Contract/ContractStateLast.swift @@ -0,0 +1,14 @@ +// +// ContractStateLast.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +import Foundation +import BigInt + +public struct ContractStateLast { + let lt: BigInt + let hash: Data +} diff --git a/Source/TonSwift/Contracts/Contract/ContractStateStatus.swift b/Source/TonSwift/Contracts/Contract/ContractStateStatus.swift new file mode 100644 index 0000000..3141484 --- /dev/null +++ b/Source/TonSwift/Contracts/Contract/ContractStateStatus.swift @@ -0,0 +1,14 @@ +// +// ContractStateStatus.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +import Foundation + +public enum ContractStateStatus { + case uninit + case active(code: Data?, data: Data?) + case frozen(stateHash: Data?) +} diff --git a/Source/TonSwift/Contracts/Contract/OpaqueContract.swift b/Source/TonSwift/Contracts/Contract/OpaqueContract.swift new file mode 100644 index 0000000..df1797a --- /dev/null +++ b/Source/TonSwift/Contracts/Contract/OpaqueContract.swift @@ -0,0 +1,16 @@ +// +// OpaqueContract.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +public struct OpaqueContract: Contract { + public let workchain: Int8 + public let stateInit: StateInit + + init(workchain: Int8, stateInit: StateInit) { + self.workchain = workchain + self.stateInit = stateInit + } +} diff --git a/Source/TonSwift/Contracts/SendMode.swift b/Source/TonSwift/Contracts/SendMode.swift deleted file mode 100644 index 9fe9883..0000000 --- a/Source/TonSwift/Contracts/SendMode.swift +++ /dev/null @@ -1,126 +0,0 @@ -import Foundation - -/// Various raw options for sending a message that affect error handling, -/// paying fees and sending the value. -/// This struct is a wrapper around sendmode flags that prevents accidental misuse. -public struct SendMode { - /// Pays message fwd/ihr fees separately. - /// If the flag is not set, those fees are subtracted from the message value. - public let payMsgFees: Bool - - /// Ignores the errors in the action phase (due to invalid address, insufficient funds etc.) - /// That is, if this message cannot be sent, this does not fail the rest of the transaction. - public let ignoreErrors: Bool - - /// Options for sending the value. - public let value: SendValueOptions - - init(payMsgFees: Bool = false, ignoreErrors: Bool = false, value: SendValueOptions = .messageValue) { - self.payMsgFees = payMsgFees - self.ignoreErrors = ignoreErrors - self.value = value - } - - /// Standard flags for the wallet is to pay msg fees on behalf of the sender - /// and ignore errors so that sequence number can be bumped securely, - /// so that bad transactions cannot be replayed indefinitely. - static func walletDefault() -> Self { - SendMode(payMsgFees: true, ignoreErrors: true) - } -} - -extension SendMode: RawRepresentable { - public init?(rawValue: UInt8) { - if rawValue & SendModeInvalidFlags > 0 { - return nil // these bits are not used and must be set to zero - } - if let v = SendValueOptions(rawValue: rawValue) { - self.value = v - } else { - return nil - } - self.payMsgFees = (rawValue & SendModePayMsgFees > 0) - self.ignoreErrors = (rawValue & SendModeIgnoreErrors > 0) - } - - public var rawValue: UInt8 { - var m: UInt8 = value.rawValue - if payMsgFees { m |= SendModePayMsgFees } - if ignoreErrors { m |= SendModeIgnoreErrors } - return m - } -} - -public enum SendValueOptions { - /// Default choice: send the value specified for the message (minus the possible fees). - case messageValue - - /// In addition to the specified value, message gets the remaining value from the incoming value. - case addInboundValue - - /// Spend the entire balance (minus the fees). - case sendRemainingBalance - - /// Spend the entire balance (minus the fees) and destroy the contract. - case sendRemainingBalanceAndDestroy -} - -extension SendValueOptions: RawRepresentable { - public typealias RawValue = UInt8 - - public init?(rawValue: UInt8) { - if rawValue & SendModeConflictingFlags == SendModeConflictingFlags { - return nil // cannot set conflicting bits - } - if rawValue & SendModeSpendRemainingBalance > 0 { - if rawValue & SendModeDestroyWhenEmpty > 0 { - self = .sendRemainingBalanceAndDestroy - } else { - self = .sendRemainingBalance - } - } else if rawValue & SendModeAddRemainingInboundValue > 0 { - self = .addInboundValue - } else { - self = .messageValue - } - } - - public var rawValue: UInt8 { - switch self { - case .messageValue: return 0 - case .addInboundValue: return SendModeAddRemainingInboundValue - case .sendRemainingBalance: return SendModeSpendRemainingBalance - case .sendRemainingBalanceAndDestroy: return SendModeSpendRemainingBalance | SendModeDestroyWhenEmpty - } - } -} - -/// Pays message fwd/ihr fees separately. -/// If the flag is not set, those fees are subtracted from the message value. -/// IMPORTANT: do not use this flag directly, use `SendMode` type instead. -let SendModePayMsgFees: UInt8 = 1 - -/// Ignores the errors in the action phase (due to invalid address, insufficient funds etc.) -/// That is, if this message cannot be sent, this does not fail the rest of the transaction. -/// IMPORTANT: do not use this flag directly, use `SendMode` type instead. -let SendModeIgnoreErrors: UInt8 = 2 - -/// Account is destroyed when remaining balance is zero. -/// This flag comes into effect only when used together with `spendRemainingBalance`. -/// IMPORTANT: do not use this flag directly, use `SendMode` type instead. -let SendModeDestroyWhenEmpty: UInt8 = 32 - -/// In addition to the specified value, message gets the remaining value from the incoming value. -/// This flag cannot be used together with `spendRemainingBalance`. -/// IMPORTANT: do not use this flag directly, use `SendMode` type instead. -let SendModeAddRemainingInboundValue: UInt8 = 64 - -/// Message sends the entire remaining balance of the contract, -/// forwarding fees are subtracted from that amount. -/// This flag cannot be used together with `addRemainingInboundValue`. -/// IMPORTANT: do not use this flag directly, use `SendMode` type instead. -let SendModeSpendRemainingBalance: UInt8 = 128 - -private let SendModeInvalidFlags: UInt8 = 4+8+16 - -private let SendModeConflictingFlags: UInt8 = 128+64 diff --git a/Source/TonSwift/Contracts/SendMode/SendMode.swift b/Source/TonSwift/Contracts/SendMode/SendMode.swift new file mode 100644 index 0000000..24d2a31 --- /dev/null +++ b/Source/TonSwift/Contracts/SendMode/SendMode.swift @@ -0,0 +1,52 @@ +import Foundation + +/// Various raw options for sending a message that affect error handling, +/// paying fees and sending the value. +/// This struct is a wrapper around sendmode flags that prevents accidental misuse. +public struct SendMode: RawRepresentable { + /// Pays message fwd/ihr fees separately. + /// If the flag is not set, those fees are subtracted from the message value. + public let payMsgFees: Bool + + /// Ignores the errors in the action phase (due to invalid address, insufficient funds etc.) + /// That is, if this message cannot be sent, this does not fail the rest of the transaction. + public let ignoreErrors: Bool + + /// Options for sending the value. + public let value: SendValueOptions + + init(payMsgFees: Bool = false, ignoreErrors: Bool = false, value: SendValueOptions = .messageValue) { + self.payMsgFees = payMsgFees + self.ignoreErrors = ignoreErrors + self.value = value + } + + /// Standard flags for the wallet is to pay msg fees on behalf of the sender + /// and ignore errors so that sequence number can be bumped securely, + /// so that bad transactions cannot be replayed indefinitely. + static func walletDefault() -> Self { + SendMode(payMsgFees: true, ignoreErrors: true) + } + + // MARK: RawRepresentable + + public init?(rawValue: UInt8) { + if rawValue & SendModeInvalidFlags > 0 { + return nil // these bits are not used and must be set to zero + } + if let v = SendValueOptions(rawValue: rawValue) { + self.value = v + } else { + return nil + } + self.payMsgFees = (rawValue & SendModePayMsgFees > 0) + self.ignoreErrors = (rawValue & SendModeIgnoreErrors > 0) + } + + public var rawValue: UInt8 { + var m: UInt8 = value.rawValue + if payMsgFees { m |= SendModePayMsgFees } + if ignoreErrors { m |= SendModeIgnoreErrors } + return m + } +} diff --git a/Source/TonSwift/Contracts/SendMode/SendModeTypes.swift b/Source/TonSwift/Contracts/SendMode/SendModeTypes.swift new file mode 100644 index 0000000..b1cb62f --- /dev/null +++ b/Source/TonSwift/Contracts/SendMode/SendModeTypes.swift @@ -0,0 +1,36 @@ +// +// SendModeTypes.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +/// Pays message fwd/ihr fees separately. +/// If the flag is not set, those fees are subtracted from the message value. +/// IMPORTANT: do not use this flag directly, use `SendMode` type instead. +let SendModePayMsgFees: UInt8 = 1 + +/// Ignores the errors in the action phase (due to invalid address, insufficient funds etc.) +/// That is, if this message cannot be sent, this does not fail the rest of the transaction. +/// IMPORTANT: do not use this flag directly, use `SendMode` type instead. +let SendModeIgnoreErrors: UInt8 = 2 + +/// Account is destroyed when remaining balance is zero. +/// This flag comes into effect only when used together with `spendRemainingBalance`. +/// IMPORTANT: do not use this flag directly, use `SendMode` type instead. +let SendModeDestroyWhenEmpty: UInt8 = 32 + +/// In addition to the specified value, message gets the remaining value from the incoming value. +/// This flag cannot be used together with `spendRemainingBalance`. +/// IMPORTANT: do not use this flag directly, use `SendMode` type instead. +let SendModeAddRemainingInboundValue: UInt8 = 64 + +/// Message sends the entire remaining balance of the contract, +/// forwarding fees are subtracted from that amount. +/// This flag cannot be used together with `addRemainingInboundValue`. +/// IMPORTANT: do not use this flag directly, use `SendMode` type instead. +let SendModeSpendRemainingBalance: UInt8 = 128 + +let SendModeInvalidFlags: UInt8 = 4+8+16 + +let SendModeConflictingFlags: UInt8 = 128+64 diff --git a/Source/TonSwift/Contracts/SendMode/SendValueOptions.swift b/Source/TonSwift/Contracts/SendMode/SendValueOptions.swift new file mode 100644 index 0000000..5cb9481 --- /dev/null +++ b/Source/TonSwift/Contracts/SendMode/SendValueOptions.swift @@ -0,0 +1,50 @@ +// +// SendValueOptions.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +public enum SendValueOptions: RawRepresentable { + /// Default choice: send the value specified for the message (minus the possible fees). + case messageValue + + /// In addition to the specified value, message gets the remaining value from the incoming value. + case addInboundValue + + /// Spend the entire balance (minus the fees). + case sendRemainingBalance + + /// Spend the entire balance (minus the fees) and destroy the contract. + case sendRemainingBalanceAndDestroy + + // MARK: RawRepresentable + + public typealias RawValue = UInt8 + + public init?(rawValue: UInt8) { + if rawValue & SendModeConflictingFlags == SendModeConflictingFlags { + return nil // cannot set conflicting bits + } + if rawValue & SendModeSpendRemainingBalance > 0 { + if rawValue & SendModeDestroyWhenEmpty > 0 { + self = .sendRemainingBalanceAndDestroy + } else { + self = .sendRemainingBalance + } + } else if rawValue & SendModeAddRemainingInboundValue > 0 { + self = .addInboundValue + } else { + self = .messageValue + } + } + + public var rawValue: UInt8 { + switch self { + case .messageValue: return 0 + case .addInboundValue: return SendModeAddRemainingInboundValue + case .sendRemainingBalance: return SendModeSpendRemainingBalance + case .sendRemainingBalanceAndDestroy: return SendModeSpendRemainingBalance | SendModeDestroyWhenEmpty + } + } +} diff --git a/Source/TonSwift/Contracts/Sender/Sender.swift b/Source/TonSwift/Contracts/Sender/Sender.swift new file mode 100644 index 0000000..f7d9e5b --- /dev/null +++ b/Source/TonSwift/Contracts/Sender/Sender.swift @@ -0,0 +1,4 @@ +public struct Sender { + let address: Address? + let send: ((SenderArguments) async throws -> Void) +} diff --git a/Source/TonSwift/Contracts/Sender.swift b/Source/TonSwift/Contracts/Sender/SenderArguments.swift similarity index 60% rename from Source/TonSwift/Contracts/Sender.swift rename to Source/TonSwift/Contracts/Sender/SenderArguments.swift index 96165bd..f6826b5 100644 --- a/Source/TonSwift/Contracts/Sender.swift +++ b/Source/TonSwift/Contracts/Sender/SenderArguments.swift @@ -1,4 +1,10 @@ -import Foundation +// +// SenderArguments.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + import BigInt public struct SenderArguments { @@ -9,8 +15,3 @@ public struct SenderArguments { let stateInit: StateInit? let body: Cell } - -public struct Sender { - let address: Address? - let send: ((SenderArguments) async throws -> Void) -} diff --git a/Source/TonSwift/Contracts/SimpleLibrary.swift b/Source/TonSwift/Contracts/SimpleLibrary.swift new file mode 100644 index 0000000..0e69ec5 --- /dev/null +++ b/Source/TonSwift/Contracts/SimpleLibrary.swift @@ -0,0 +1,27 @@ +// +// File.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// + +// Source: https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L145 +// simple_lib$_ public:Bool root:^Cell = SimpleLib; + +public struct SimpleLibrary: Hashable, CellCodable { + var `public`: Bool + var root: Cell + + // MARK: CellCodable + + public func storeTo(builder: Builder) throws { + try builder.store(bit: self.public) + .store(ref: root) + } + public static func loadFrom(slice: Slice) throws -> SimpleLibrary { + Self( + public: try slice.loadBoolean(), + root: try slice.loadRef() + ) + } +} diff --git a/Source/TonSwift/Contracts/StateInit.swift b/Source/TonSwift/Contracts/StateInit.swift index 2bb5518..87cedc6 100644 --- a/Source/TonSwift/Contracts/StateInit.swift +++ b/Source/TonSwift/Contracts/StateInit.swift @@ -62,44 +62,3 @@ public struct StateInit: CellCodable { return StateInit(splitDepth: splitDepth, special: special, code: code, data: data, libraries: libraries) } } - -// Source: https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L139 -// tick_tock$_ tick:Bool tock:Bool = TickTock; - -struct TickTock: CellCodable { - var tick: Bool - var tock: Bool - - func storeTo(builder: Builder) throws { - try builder.store(bit: tick) - .store(bit: tock) - } - - static func loadFrom(slice: Slice) throws -> TickTock { - TickTock( - tick: try slice.loadBoolean(), - tock: try slice.loadBoolean() - ) - } -} - -// Source: https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L145 -// simple_lib$_ public:Bool root:^Cell = SimpleLib; - -public struct SimpleLibrary: Hashable { - var `public`: Bool - var root: Cell -} - -extension SimpleLibrary: CellCodable { - public func storeTo(builder: Builder) throws { - try builder.store(bit: self.public) - .store(ref: root) - } - public static func loadFrom(slice: Slice) throws -> SimpleLibrary { - Self( - public: try slice.loadBoolean(), - root: try slice.loadRef() - ) - } -} diff --git a/Source/TonSwift/Contracts/TickTock.swift b/Source/TonSwift/Contracts/TickTock.swift index fbf2875..bac8f23 100644 --- a/Source/TonSwift/Contracts/TickTock.swift +++ b/Source/TonSwift/Contracts/TickTock.swift @@ -1,2 +1,26 @@ -import Foundation +// +// TickTock.swift +// +// +// Created by Kirill Kirilenko on 20/07/2023. +// +// Source: https://github.com/ton-blockchain/ton/blob/24dc184a2ea67f9c47042b4104bbb4d82289fac1/crypto/block/block.tlb#L139 +// tick_tock$_ tick:Bool tock:Bool = TickTock; + +struct TickTock: CellCodable { + var tick: Bool + var tock: Bool + + func storeTo(builder: Builder) throws { + try builder.store(bit: tick) + .store(bit: tock) + } + + static func loadFrom(slice: Slice) throws -> TickTock { + TickTock( + tick: try slice.loadBoolean(), + tock: try slice.loadBoolean() + ) + } +} From ac7fedd565db8f02262eb7845aa1c5fdf050255b Mon Sep 17 00:00:00 2001 From: Kirill Kirilenko Date: Thu, 20 Jul 2023 17:38:47 +0400 Subject: [PATCH 11/11] remove useless extension in PublicKey --- Source/TonSwift/Keys/PublicKey.swift | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Source/TonSwift/Keys/PublicKey.swift b/Source/TonSwift/Keys/PublicKey.swift index a11eea9..627b387 100644 --- a/Source/TonSwift/Keys/PublicKey.swift +++ b/Source/TonSwift/Keys/PublicKey.swift @@ -7,27 +7,25 @@ import Foundation -public struct PublicKey: Key { +public struct PublicKey: Key, CellCodable { public let data: Data public init(data: Data) { self.data = data } -} -extension PublicKey: CellCodable { + private static let publicKeyLength = 32 + + // MARK: CellCodable + public func storeTo(builder: Builder) throws { try builder.store(data: data) } public static func loadFrom(slice: Slice) throws -> PublicKey { try slice.tryLoad { s in - let data = try s.loadBytes(.publicKeyLength) + let data = try s.loadBytes(publicKeyLength) return PublicKey(data: data) } } } - -private extension Int { - static let publicKeyLength = 32 -}