From 0f6d95f3bcf2ca2516542e944cc96d2134b5ff0a Mon Sep 17 00:00:00 2001 From: Dmitry Date: Mon, 3 Dec 2018 23:47:54 +0300 Subject: [PATCH] Now we are using CommonCrypto instead of CryptoSwift (up to 1000 times faster now) Optimized mnemonics generation (10 times faster) Added Transaction class (Work in progress) Added SignedTransaction class (Work in progress) --- Cartfile | 1 + Cartfile.resolved | 5 +- Package.swift | 1 + README.md | 8 +- Sources/ABIv2/ABIv2Decoding.swift | 38 ++-- Sources/ABIv2/ABIv2Encoding.swift | 8 +- Sources/ABIv2/ABIv2ParameterTypes.swift | 6 +- Sources/Contract/ComparisonExtensions.swift | 8 +- .../EthereumFilterEncodingExtensions.swift | 10 +- Sources/Contract/EventFiltering.swift | 2 +- Sources/Convenience/CryptoExtensions.swift | 10 +- Sources/Convenience/Data+Extension.swift | 9 + .../Convenience/LibSecp256k1Extension.swift | 3 +- Sources/Encryption/AES.swift | 198 ++++++++++++++++++ Sources/Encryption/DerivedKey.swift | 132 ++++++++---- Sources/Encryption/PrivateKey.swift | 113 ++++++++++ .../Web3+BrowserFunctions.swift | 4 +- Sources/Keystore/Accounts.swift | 198 ------------------ Sources/KeystoreManager/BIP32HDNode.swift | 10 +- Sources/KeystoreManager/BIP32Keystore.swift | 53 ++--- Sources/KeystoreManager/BIP39.swift | 14 +- Sources/KeystoreManager/EthereumAddress.swift | 15 +- .../KeystoreManager/EthereumKeystoreV3.swift | 45 ++-- Sources/Transaction/BloomFilter.swift | 3 +- Sources/Transaction/EthereumTransaction.swift | 17 +- .../SolidityDataReader.swift | 0 .../SolidityDataWriter.swift | 14 +- .../SolidityFunction.swift | 0 .../SolidityTypes.swift | 4 + Sources/Transactions/Transaction.swift | 123 +++++++++++ Sources/Utils/EIP67Code.swift | 4 +- Sources/Utils/RLP.swift | 6 +- Sources/Web3/Web3+Eth.swift | 6 +- Sources/Web3/Web3+Personal.swift | 2 +- .../Web3/Web3+TransactionIntermediate.swift | 7 +- Sources/Web3/Web3+Utils.swift | 12 +- Tests/ABITests.swift | 30 +-- Tests/BetterABI/BetterERC20Tests.swift | 2 +- Tests/ERC20Tests.swift | 2 +- Tests/KeystoreTests.swift | 32 +-- Tests/RLPTests.swift | 29 ++- Tests/RemoteParsingTests.swift | 4 +- Tests/RinkebyPersonalSignatureTests.swift | 12 +- Tests/SECP256K1Tests.swift | 3 - Tests/ScryptTests.swift | 1 - Tests/TransactionTests.swift | 2 +- web3swift.podspec | 3 +- web3swift.xcodeproj/project.pbxproj | 34 +-- 48 files changed, 784 insertions(+), 459 deletions(-) create mode 100644 Sources/Encryption/AES.swift create mode 100644 Sources/Encryption/PrivateKey.swift delete mode 100644 Sources/Keystore/Accounts.swift rename Sources/{ABIv2 => Transactions}/SolidityDataReader.swift (100%) rename Sources/{ABIv2 => Transactions}/SolidityDataWriter.swift (84%) rename Sources/{ABIv2 => Transactions}/SolidityFunction.swift (100%) rename Sources/{ABIv2 => Transactions}/SolidityTypes.swift (99%) create mode 100644 Sources/Transactions/Transaction.swift diff --git a/Cartfile b/Cartfile index 1751663..7176f40 100644 --- a/Cartfile +++ b/Cartfile @@ -2,3 +2,4 @@ github "mxcl/PromiseKit" ~> 6.0 github "attaswift/BigInt" ~> 3.1 github "krzyzanowskim/CryptoSwift" github "Boilertalk/secp256k1.swift" +github "v57/BlueCryptor" ~> 1.0 diff --git a/Cartfile.resolved b/Cartfile.resolved index b2d65b4..0738bbf 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,5 +1,6 @@ github "Boilertalk/secp256k1.swift" "0.1.4" github "attaswift/BigInt" "v3.1.0" github "attaswift/SipHash" "v1.2.2" -github "krzyzanowskim/CryptoSwift" "0.13.0" -github "mxcl/PromiseKit" "6.5.2" +github "krzyzanowskim/CryptoSwift" "0.13.1" +github "mxcl/PromiseKit" "6.6.1" +github "v57/BlueCryptor" "1.0.22" diff --git a/Package.swift b/Package.swift index 71fefc6..1af2d35 100644 --- a/Package.swift +++ b/Package.swift @@ -14,6 +14,7 @@ let package = Package( .package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", from: "0.12.0"), .package(url: "https://github.com/Boilertalk/secp256k1.swift.git", from: "0.1.1"), .package(url: "https://github.com/mxcl/PromiseKit.git", from: "6.4.0"), + .package(url: "https://github.com/v57/BlueCryptor.git", from: "1.0.0"), ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. diff --git a/README.md b/README.md index 4ce4bc3..3a4a8fb 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -# We just released web3swift 2.0 [check it out](https://github.com/BANKEX/web3swift/releases/tag/2.0.0) -### also check our [Discord Channel](https://discord.gg/3ETv2ST) + +### You can ask for help in our [Discord Channel](https://discord.gg/3ETv2ST)

Support @@ -57,7 +57,7 @@ Don't forget to set the iOS version in a Podfile, otherwise you get an error if Add this to the dependency section of your `Package.swift` manifest: ```Swift - .package(url: "https://github.com/BANKEX/web3swift.git", from: "2.0.0") + .package(url: "https://github.com/BANKEX/web3swift.git", from: "2.1.0") ``` - **CocoaPods:** Put this in your `Podfile`: @@ -69,7 +69,7 @@ Don't forget to set the iOS version in a Podfile, otherwise you get an error if - **Carthage:** Put this in your `Cartfile`: ``` - github "BANKEX/web3swift" ~> 2.0 + github "BANKEX/web3swift" ~> 2.1 ``` diff --git a/Sources/ABIv2/ABIv2Decoding.swift b/Sources/ABIv2/ABIv2Decoding.swift index bb25fe8..228cf5d 100644 --- a/Sources/ABIv2/ABIv2Decoding.swift +++ b/Sources/ABIv2/ABIv2Decoding.swift @@ -31,7 +31,7 @@ public struct ABIv2Decoder { /// - data: Data to decode /// - Returns: Array of decoded types public static func decode(types: [ABIv2.Element.ParameterType], data: Data) -> [AnyObject]? { -// print("Full data: \n" + data.toHexString()) +// print("Full data: \n" + data.hex) var toReturn = [AnyObject]() var consumed: UInt64 = 0 for i in 0 ..< types.count { @@ -59,7 +59,7 @@ public struct ABIv2Decoder { } switch type { case let .uint(bits): -// print("Uint256 element itself: \n" + elementItself.toHexString()) +// print("Uint256 element itself: \n" + elementItself.hex) guard elementItself.count >= 32 else { break } let mod = BigUInt(1) << bits let dataSlice = elementItself[0 ..< 32] @@ -67,7 +67,7 @@ public struct ABIv2Decoder { // print("Uint256 element is: \n" + String(v)) return (v as AnyObject, type.memoryUsage) case let .int(bits): -// print("Int256 element itself: \n" + elementItself.toHexString()) +// print("Int256 element itself: \n" + elementItself.hex) guard elementItself.count >= 32 else { break } let mod = BigInt(1) << bits let dataSlice = elementItself[0 ..< 32] @@ -75,14 +75,14 @@ public struct ABIv2Decoder { // print("Int256 element is: \n" + String(v)) return (v as AnyObject, type.memoryUsage) case .address: -// print("Address element itself: \n" + elementItself.toHexString()) +// print("Address element itself: \n" + elementItself.hex) guard elementItself.count >= 32 else { break } let dataSlice = elementItself[12 ..< 32] let address = Address(dataSlice) // print("Address element is: \n" + String(address.address)) return (address as AnyObject, type.memoryUsage) case .bool: -// print("Bool element itself: \n" + elementItself.toHexString()) +// print("Bool element itself: \n" + elementItself.hex) guard elementItself.count >= 32 else { break } let dataSlice = elementItself[0 ..< 32] let v = BigUInt(dataSlice) @@ -93,13 +93,13 @@ public struct ABIv2Decoder { return (false as AnyObject, type.memoryUsage) } case let .bytes(length): -// print("Bytes32 element itself: \n" + elementItself.toHexString()) +// print("Bytes32 element itself: \n" + elementItself.hex) guard elementItself.count >= 32 else { break } let dataSlice = elementItself[0 ..< length] -// print("Bytes32 element is: \n" + String(dataSlice.toHexString())) +// print("Bytes32 element is: \n" + String(dataSlice.hex)) return (dataSlice as AnyObject, type.memoryUsage) case .string: -// print("String element itself: \n" + elementItself.toHexString()) +// print("String element itself: \n" + elementItself.hex) guard elementItself.count >= 32 else { break } var dataSlice = elementItself[0 ..< 32] let length = UInt64(BigUInt(dataSlice)) @@ -109,18 +109,18 @@ public struct ABIv2Decoder { // print("String element is: \n" + String(string)) return (string as AnyObject, type.memoryUsage) case .dynamicBytes: -// print("Bytes element itself: \n" + elementItself.toHexString()) +// print("Bytes element itself: \n" + elementItself.hex) guard elementItself.count >= 32 else { break } var dataSlice = elementItself[0 ..< 32] let length = UInt64(BigUInt(dataSlice)) guard elementItself.count >= 32 + length else { break } dataSlice = elementItself[32 ..< 32 + length] -// print("Bytes element is: \n" + String(dataSlice.toHexString())) +// print("Bytes element is: \n" + String(dataSlice.hex)) return (dataSlice as AnyObject, type.memoryUsage) case let .array(type: subType, length: length): switch type.arraySize { case .dynamicSize: -// print("Dynamic array element itself: \n" + elementItself.toHexString()) +// print("Dynamic array element itself: \n" + elementItself.hex) if subType.isStatic { // uint[] like, expect length and elements guard elementItself.count >= 32 else { break } @@ -146,7 +146,7 @@ public struct ABIv2Decoder { dataSlice = Data(elementItself[32 ..< elementItself.count]) var subpointer: UInt64 = 0 var toReturn = [AnyObject]() -// print("Dynamic array sub element itself: \n" + dataSlice.toHexString()) +// print("Dynamic array sub element itself: \n" + dataSlice.hex) for _ in 0 ..< length { let (v, c) = decodeSignleType(type: subType, data: dataSlice, pointer: subpointer) guard let valueUnwrapped = v, let consumedUnwrapped = c else { break } @@ -156,7 +156,7 @@ public struct ABIv2Decoder { return (toReturn as AnyObject, nextElementPointer) } case let .staticSize(staticLength): -// print("Static array element itself: \n" + elementItself.toHexString()) +// print("Static array element itself: \n" + elementItself.hex) guard length == staticLength else { break } var toReturn = [AnyObject]() var consumed: UInt64 = 0 @@ -175,7 +175,7 @@ public struct ABIv2Decoder { break } case let .tuple(types: subTypes): -// print("Tuple element itself: \n" + elementItself.toHexString()) +// print("Tuple element itself: \n" + elementItself.hex) var toReturn = [AnyObject]() var consumed: UInt64 = 0 for i in 0 ..< subTypes.count { @@ -191,23 +191,23 @@ public struct ABIv2Decoder { return (toReturn as AnyObject, nextElementPointer) } case .function: -// print("Function element itself: \n" + elementItself.toHexString()) +// print("Function element itself: \n" + elementItself.hex) guard elementItself.count >= 32 else { break } let dataSlice = elementItself[8 ..< 32] -// print("Function element is: \n" + String(dataSlice.toHexString())) +// print("Function element is: \n" + String(dataSlice.hex)) return (dataSlice as AnyObject, type.memoryUsage) } return (nil, nil) } fileprivate static func followTheData(type: ABIv2.Element.ParameterType, data: Data, pointer: UInt64 = 0) -> (elementEncoding: Data?, nextElementPointer: UInt64?) { -// print("Follow the data: \n" + data.toHexString()) +// print("Follow the data: \n" + data.hex) // print("At pointer: \n" + String(pointer)) if type.isStatic { guard data.count >= pointer + type.memoryUsage else { return (nil, nil) } let elementItself = data[pointer ..< pointer + type.memoryUsage] let nextElement = pointer + type.memoryUsage -// print("Got element itself: \n" + elementItself.toHexString()) +// print("Got element itself: \n" + elementItself.hex) // print("Next element pointer: \n" + String(nextElement)) return (Data(elementItself), nextElement) } else { @@ -230,7 +230,7 @@ public struct ABIv2Decoder { let elementPointer = UInt64(bn) let elementItself = data[elementPointer ..< UInt64(data.count)] let nextElement = pointer + type.memoryUsage -// print("Got element itself: \n" + elementItself.toHexString()) +// print("Got element itself: \n" + elementItself.hex) // print("Next element pointer: \n" + String(nextElement)) return (Data(elementItself), nextElement) } diff --git a/Sources/ABIv2/ABIv2Encoding.swift b/Sources/ABIv2/ABIv2Encoding.swift index be62181..f0c2713 100644 --- a/Sources/ABIv2/ABIv2Encoding.swift +++ b/Sources/ABIv2/ABIv2Encoding.swift @@ -259,7 +259,7 @@ public struct ABIv2Encoder { toReturn.append(encoding) } let total = lengthEncoding + toReturn -// print("Dynamic array of static types encoding :\n" + String(total.toHexString())) +// print("Dynamic array of static types encoding :\n" + String(total.hex)) return total } else { // create new context @@ -292,7 +292,7 @@ public struct ABIv2Encoder { } } let total = lengthEncoding + headsConcatenated + tailsConcatenated -// print("Dynamic array of dynamic types encoding :\n" + String(total.toHexString())) +// print("Dynamic array of dynamic types encoding :\n" + String(total.hex)) return total } case let .staticSize(staticLength): @@ -307,7 +307,7 @@ public struct ABIv2Encoder { guard let encoding = enc else { break } toReturn.append(encoding) } -// print("Static array of static types encoding :\n" + String(toReturn.toHexString())) +// print("Static array of static types encoding :\n" + String(toReturn.hex)) let total = toReturn return total } else { @@ -335,7 +335,7 @@ public struct ABIv2Encoder { tailsPointer = tailsPointer + BigUInt(tail.count) } let total = headsConcatenated + tailsConcatenated -// print("Static array of dynamic types encoding :\n" + String(total.toHexString())) +// print("Static array of dynamic types encoding :\n" + String(total.hex)) return total } case .notArray: diff --git a/Sources/ABIv2/ABIv2ParameterTypes.swift b/Sources/ABIv2/ABIv2ParameterTypes.swift index d988d1d..7eaf160 100644 --- a/Sources/ABIv2/ABIv2ParameterTypes.swift +++ b/Sources/ABIv2/ABIv2ParameterTypes.swift @@ -179,12 +179,12 @@ extension ABIv2.Element.Function { /// Function hash in hex public var methodString: String { - return String(signature.sha3(.keccak256).prefix(8)) + return signature.keccak256().hex } /// Function hash public var methodEncoding: Data { - return signature.data(using: .ascii)!.sha3(.keccak256)[0..<4] + return signature.data(using: .ascii)!.keccak256()[0..<4] } } @@ -198,7 +198,7 @@ extension ABIv2.Element.Event { /// Event hash public var topic: Data { - return signature.data(using: .ascii)!.sha3(.keccak256) + return signature.data(using: .ascii)!.keccak256() } } diff --git a/Sources/Contract/ComparisonExtensions.swift b/Sources/Contract/ComparisonExtensions.swift index f050b39..d016fda 100644 --- a/Sources/Contract/ComparisonExtensions.swift +++ b/Sources/Contract/ComparisonExtensions.swift @@ -39,9 +39,9 @@ extension String: EventFilterComparable { public func isEqualTo(_ other: AnyObject) -> Bool { switch other { case let oth as String: - return data.sha3(.keccak256) == oth.data.sha3(.keccak256) + return data.keccak256() == oth.data.keccak256() case let oth as Data: - return data.sha3(.keccak256) == oth.sha3(.keccak256) + return data.keccak256() == oth.keccak256() default: return false } @@ -56,13 +56,13 @@ extension Data: EventFilterComparable { if self == data { return true } - let hash = data.sha3(.keccak256) + let hash = data.keccak256() return self == hash case let oth as Data: if self == oth { return true } - let hash = oth.sha3(.keccak256) + let hash = oth.keccak256() return self == hash default: return false diff --git a/Sources/Contract/EthereumFilterEncodingExtensions.swift b/Sources/Contract/EthereumFilterEncodingExtensions.swift index 5dd0316..560fff7 100644 --- a/Sources/Contract/EthereumFilterEncodingExtensions.swift +++ b/Sources/Contract/EthereumFilterEncodingExtensions.swift @@ -11,32 +11,32 @@ import Foundation extension BigUInt: EventFilterEncodable { public func eventFilterEncoded() -> String? { - return abiEncode(bits: 256)?.toHexString().withHex + return abiEncode(bits: 256)?.hex.withHex } } extension BigInt: EventFilterEncodable { public func eventFilterEncoded() -> String? { - return abiEncode(bits: 256)?.toHexString().withHex + return abiEncode(bits: 256)?.hex.withHex } } extension Data: EventFilterEncodable { public func eventFilterEncoded() -> String? { guard let padded = self.setLengthLeft(32) else { return nil } - return padded.toHexString().withHex + return padded.hex.withHex } } extension Address: EventFilterEncodable { public func eventFilterEncoded() -> String? { guard let padded = self.addressData.setLengthLeft(32) else { return nil } - return padded.toHexString().withHex + return padded.hex.withHex } } extension String: EventFilterEncodable { public func eventFilterEncoded() -> String? { - return data.sha3(.keccak256).toHexString().withHex + return data.keccak256().hex.withHex } } diff --git a/Sources/Contract/EventFiltering.swift b/Sources/Contract/EventFiltering.swift index 934cb8f..0aa7f54 100644 --- a/Sources/Contract/EventFiltering.swift +++ b/Sources/Contract/EventFiltering.swift @@ -68,7 +68,7 @@ internal func encodeTopicToGetLogs(contract: ContractV2, eventName: String?, fil } var topics = [[String?]?]() if eventTopic != nil { - topics.append([eventTopic!.toHexString().withHex]) + topics.append([eventTopic!.hex.withHex]) } else { topics.append(nil as [String?]?) } diff --git a/Sources/Convenience/CryptoExtensions.swift b/Sources/Convenience/CryptoExtensions.swift index 7e4da1a..45af02a 100644 --- a/Sources/Convenience/CryptoExtensions.swift +++ b/Sources/Convenience/CryptoExtensions.swift @@ -6,7 +6,7 @@ // Copyright © 2017 Alexander Vlasov. All rights reserved. // -import CryptoSwift +import Cryptor import Foundation /** @@ -98,8 +98,11 @@ private class OldScrypt { V.deallocate() } + /* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */ - let barray = try PKCS5.PBKDF2(password: password, salt: [UInt8](salt), iterations: 1, keyLength: p * 128 * r, variant: .sha256).calculate() + + let pw = String(data: Data(password), encoding: .utf8)! + let barray = try PBKDF.deriveKey(fromPassword: pw, salt: salt, prf: .sha256, rounds: 1, derivedKeyLength: UInt(p * 128 * r)) barray.withUnsafeBytes { p in B.copyMemory(from: p.baseAddress!, byteCount: barray.count) @@ -115,7 +118,8 @@ private class OldScrypt { let pointer = B.assumingMemoryBound(to: UInt8.self) let bufferPointer = UnsafeBufferPointer(start: pointer, count: p * 128 * r) let block = [UInt8](bufferPointer) - return try PKCS5.PBKDF2(password: password, salt: block, iterations: 1, keyLength: dkLen, variant: .sha256).calculate() + return try PBKDF.deriveKey(fromPassword: pw, salt: block, prf: .sha256, rounds: 1, derivedKeyLength: UInt(dkLen)) +// return try PKCS5.PBKDF2(password: password, salt: block, iterations: 1, keyLength: dkLen, variant: .sha256).calculate() } /// Computes `B = SMix_r(B, N)`. diff --git a/Sources/Convenience/Data+Extension.swift b/Sources/Convenience/Data+Extension.swift index a00295a..b7f5c25 100644 --- a/Sources/Convenience/Data+Extension.swift +++ b/Sources/Convenience/Data+Extension.swift @@ -7,6 +7,9 @@ // import Foundation +import CryptoSwift +import Cryptor + /// Data errors public enum DataError: Error { @@ -21,6 +24,12 @@ public enum DataError: Error { } } +extension Data { + func sha256() -> Data { + return digest(using: .sha256) + } +} + public extension Data { /// Inits with array of type init(fromArray values: [T]) { diff --git a/Sources/Convenience/LibSecp256k1Extension.swift b/Sources/Convenience/LibSecp256k1Extension.swift index 96f53af..2c66dae 100644 --- a/Sources/Convenience/LibSecp256k1Extension.swift +++ b/Sources/Convenience/LibSecp256k1Extension.swift @@ -266,7 +266,6 @@ struct SECP256K1 { https://github.com/MaiaVictor/eth-lib/blob/d959c54faa1e1ac8d474028ed1568c5dce27cc7a/src/account.js#L60 */ v = v < 2 ? v : 1 - (v % 2) - let result = serializedSignature.withUnsafeBytes { (serPtr: UnsafePointer) -> Int32 in withUnsafeMutablePointer(to: &recoverableSignature, { (signaturePointer: UnsafeMutablePointer) in secp256k1_ecdsa_recoverable_signature_parse_compact(context!, signaturePointer, serPtr, v) @@ -324,7 +323,7 @@ struct SECP256K1 { static func recoverSender(hash: Data, signature: Data) throws -> Address { let pubKey = try SECP256K1.recoverPublicKey(hash: hash, signature: signature, compressed: false) try pubKey.checkPublicKeySize() - let addressData = Data(pubKey.sha3(.keccak256)[12 ..< 32]) + let addressData = Data(pubKey.keccak256()[12 ..< 32]) return Address(addressData) } diff --git a/Sources/Encryption/AES.swift b/Sources/Encryption/AES.swift new file mode 100644 index 0000000..dfc0397 --- /dev/null +++ b/Sources/Encryption/AES.swift @@ -0,0 +1,198 @@ +// +// AES.swift +// web3swift +// +// Created by Dmitry on 30/11/2018. +// + +import Foundation +import CommonCrypto + +//extension Data { +// var bytes: Array { +// return Array(self) +// } +//} + +enum AesMode { + case ctr + case cbc + var cc: CCMode { + switch self { + case .cbc: + return CCMode(kCCModeCBC) + case .ctr: + return CCMode(kCCModeCTR) + } + } + enum Error: Swift.Error { + case invalidType(String) + } + init(_ string: String) throws { + switch string { + case "aes-128-ctr": self = .ctr + case "aes-128-cbc": self = .cbc + default: throw Error.invalidType(string) + } + } + func blockMode(_ iv: Data) -> BlockMode { + switch self { + case .ctr: return CTR(iv: iv.bytes) + case .cbc: return CBC(iv: iv.bytes) + } + } +} + +enum AESPadding { + case noPadding, pkcs5, pkcs7 + var cc: CCPadding { + switch self { + case .noPadding: + return CCPadding(ccNoPadding) + case .pkcs5: + return CCPadding(ccPKCS7Padding) + case .pkcs7: + return CCPadding(ccPKCS7Padding) + } + } +} +struct BlockMode { + var mode: AesMode + var iv: Data +} + +func CBC(iv: [UInt8]) -> BlockMode { + return BlockMode(mode: .cbc, iv: Data(iv)) +} +func CTR(iv: [UInt8]) -> BlockMode { + return BlockMode(mode: .ctr, iv: Data(iv)) +} + +class AES { + var blockMode: BlockMode + var padding: AESPadding + var key: Data + init(key: [UInt8], blockMode: BlockMode, padding: AESPadding) { + self.blockMode = blockMode + self.padding = padding + self.key = Data(key) + } + + func encrypt(_ digest: [UInt8]) throws -> [UInt8] { + return try AES128(key: key, iv: blockMode.iv).encrypt(Data(digest), padding: padding, mode: blockMode.mode).bytes + } + func encrypt(_ digest: Data) throws -> Data { + return try AES128(key: key, iv: blockMode.iv).encrypt(digest, padding: padding, mode: blockMode.mode) + } + + func decrypt(_ digest: [UInt8]) throws -> [UInt8] { + return try AES128(key: key, iv: blockMode.iv).decrypt(Data(digest), padding: padding, mode: blockMode.mode).bytes + } + func decrypt(_ digest: Data) throws -> Data { + return try AES128(key: key, iv: blockMode.iv).decrypt(digest, padding: padding, mode: blockMode.mode) + } +} + +private extension CCCryptorStatus { + func check() throws { + guard self == kCCSuccess else { throw AES128.Error.cryptoFailed(status: self) } + } +} +private extension Data { + func pointer(_ body: (UnsafePointer?) throws -> ()) rethrows { + try withUnsafeBytes(body) + } +} + +//PBKDF.deriveKey(fromPassword: mnemonics.decomposedStringWithCompatibilityMapping, salt: saltData, prf: .sha512, rounds: 2048, derivedKeyLength: 64) +struct AES128 { + private var key: Data + private var iv: Data + + init(key: Data, iv: Data) throws { + guard key.count == kCCKeySizeAES128 else { + throw Error.badKeyLength + } + guard iv.count == kCCBlockSizeAES128 else { + throw Error.badInputVectorLength + } + self.key = key + self.iv = iv + } + + enum Error: Swift.Error { + case keyGeneration(status: Int) + case cryptoFailed(status: CCCryptorStatus) + case badKeyLength + case badInputVectorLength + } + + func encrypt(_ digest: Data, padding: AESPadding, mode: AesMode) throws -> Data { + return try crypt(input: digest, operation: CCOperation(kCCEncrypt), padding: padding, mode: mode) + } + + func decrypt(_ encrypted: Data, padding: AESPadding, mode: AesMode) throws -> Data { + return try crypt(input: encrypted, operation: CCOperation(kCCDecrypt), padding: padding, mode: mode) + } + + private func crypt(input: Data, operation: CCOperation, padding: AESPadding, mode: AesMode) throws -> Data { + var outLength = Int(0) + var outBytes = [UInt8](repeating: 0, count: input.count + kCCBlockSizeAES128) + var length = 0 + + var cryptor: CCCryptorRef! + try iv.pointer { ivBytes in + try key.pointer { keyBytes in + try CCCryptorCreateWithMode(operation, mode.cc, CCAlgorithm(kCCAlgorithmAES128), padding.cc, ivBytes, keyBytes, key.count, nil, 0, 0, CCModeOptions(kCCModeOptionCTR_BE), &cryptor).check() + } + } + try input.pointer { encryptedBytes in + try CCCryptorUpdate(cryptor, encryptedBytes, input.count, &outBytes, outBytes.count, &outLength).check() + } + length += outLength + try CCCryptorFinal(cryptor, &outBytes + outLength, outBytes.count, &outLength).check() + length += outLength + + return Data(bytes: UnsafePointer(outBytes), count: length) + } + + static func createKey(password: Data, salt: Data) throws -> Data { + let length = kCCKeySizeAES256 + var status = Int32(0) + var derivedBytes = [UInt8](repeating: 0, count: length) + password.withUnsafeBytes { (passwordBytes: UnsafePointer!) in + salt.withUnsafeBytes { (saltBytes: UnsafePointer!) in + status = CCKeyDerivationPBKDF(CCPBKDFAlgorithm(kCCPBKDF2), // algorithm + passwordBytes, // password + password.count, // passwordLen + saltBytes, // salt + salt.count, // saltLen + CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA1), // prf + 10000, // rounds + &derivedBytes, // derivedKey + length) // derivedKeyLen + } + } + guard status == 0 else { + throw Error.keyGeneration(status: Int(status)) + } + return Data(bytes: UnsafePointer(derivedBytes), count: length) + } + + static func randomIv() -> Data { + return randomData(length: kCCBlockSizeAES128) + } + + static func randomSalt() -> Data { + return randomData(length: 8) + } + + static func randomData(length: Int) -> Data { + var data = Data(count: length) + let status = data.withUnsafeMutableBytes { mutableBytes in + SecRandomCopyBytes(kSecRandomDefault, length, mutableBytes) + } + assert(status == Int32(0)) + return data + } +} diff --git a/Sources/Encryption/DerivedKey.swift b/Sources/Encryption/DerivedKey.swift index a495eb3..a23c093 100644 --- a/Sources/Encryption/DerivedKey.swift +++ b/Sources/Encryption/DerivedKey.swift @@ -7,28 +7,7 @@ // import Foundation -import CryptoSwift - -enum AesMode { - case ctr - case cbc - enum Error: Swift.Error { - case invalidType(String) - } - init(_ string: String) throws { - switch string { - case "aes-128-ctr": self = .ctr - case "aes-128-cbc": self = .cbc - default: throw Error.invalidType(string) - } - } - func blockMode(_ iv: Data) -> BlockMode { - switch self { - case .ctr: return CTR(iv: iv.bytes) - case .cbc: return CBC(iv: iv.bytes) - } - } -} +import Cryptor protocol DerivedKey { func calculate(password: Data) throws -> Data @@ -54,41 +33,111 @@ enum DerivedKeyType { func derivedKey(_ json: DictionaryReader) throws -> DerivedKey { switch self { case .scrypt: return try Scrypt(json: json) - case .pbkdf2: return try PBKDF2(json: json) + case .pbkdf2: return try PBKDF2Object(json: json) } } } -extension HMAC.Variant { +//extension HMAC.Variant { +// init(_ string: String) throws { +// switch string { +// case "hmac-sha256": +// self = HMAC.Variant.sha256 +// case "hmac-sha384": +// self = HMAC.Variant.sha384 +// case "hmac-sha512": +// self = HMAC.Variant.sha512 +// default: +// throw PBKDF2Object.Error.unknownHmacAlgorithm(string) +// } +// } +// var digestLength: Int { +// switch self { +// case .sha1: +// return 20 +// case .sha256: +// return SHA2.Variant.sha256.digestLength +// case .sha384: +// return SHA2.Variant.sha384.digestLength +// case .sha512: +// return SHA2.Variant.sha512.digestLength +// case .md5: +// return 16 +// } +// } +//} + + +enum HmacVariant { + case sha1, sha224, sha256, sha384, sha512 + var cc: PBKDF.PseudoRandomAlgorithm { + switch self { + case .sha1: return .sha1 + case .sha224: return .sha224 + case .sha256: return .sha256 + case .sha384: return .sha384 + case .sha512: return .sha512 + } + } + var c: HMAC.Algorithm { + switch self { + case .sha1: return .sha1 + case .sha224: return .sha224 + case .sha256: return .sha256 + case .sha384: return .sha384 + case .sha512: return .sha512 + } + } + init(_ string: String) throws { switch string { case "hmac-sha256": - self = HMAC.Variant.sha256 + self = HmacVariant.sha256 case "hmac-sha384": - self = HMAC.Variant.sha384 + self = HmacVariant.sha384 case "hmac-sha512": - self = HMAC.Variant.sha512 + self = HmacVariant.sha512 default: - throw PBKDF2.Error.unknownHmacAlgorithm(string) + throw PBKDF2Object.Error.unknownHmacAlgorithm(string) } } var digestLength: Int { switch self { case .sha1: - return 20 + return 160 / 8 + case .sha224: + return 224 / 8 case .sha256: - return SHA2.Variant.sha256.digestLength + return 256 / 8 case .sha384: - return SHA2.Variant.sha384.digestLength + return 384 / 8 case .sha512: - return SHA2.Variant.sha512.digestLength - case .md5: - return 16 + return 512 / 8 } } } -class PBKDF2: DerivedKey { +extension HMAC { + enum HMACError: Error { + case authenticationFailed + } + convenience init(key: [UInt8], variant: HmacVariant) { + self.init(using: variant.c, key: Data(key)) + } + func authenticate(_ bytes: [UInt8]) throws -> [UInt8] { + if let data = update(byteArray: bytes)?.final() { + return data + } else { + throw HMACError.authenticationFailed + } + } +} +func BetterPBKDF(password: [UInt8], salt: [UInt8], iterations: Int, keyLength: Int, variant: HmacVariant) throws -> [UInt8] { + let string = String(bytes: password, encoding: .utf8)! + return try PBKDF.deriveKey(fromPassword: string, salt: salt, prf: variant.cc, rounds: UInt32(iterations), derivedKeyLength: UInt(keyLength)) +} + +class PBKDF2Object: DerivedKey { enum Error: Swift.Error { case unknownHmacAlgorithm(String) case invalidParameters @@ -101,19 +150,19 @@ class PBKDF2: DerivedKey { } } } - let variant: HMAC.Variant + let variant: HmacVariant let keyLength: Int let iterations: Int let salt: [UInt8] - init(salt: Data, iterations: Int, keyLength: Int, variant: HMAC.Variant) { + init(salt: Data, iterations: Int, keyLength: Int, variant: HmacVariant) { self.salt = Array(salt) self.keyLength = keyLength self.iterations = iterations self.variant = variant } init(json: DictionaryReader) throws { - variant = try HMAC.Variant(json.at("prf").string()) + variant = try HmacVariant(json.at("prf").string()) keyLength = try json.at("dklen").int() iterations = try json.at("c").int() salt = try Array(json.at("salt").data()) @@ -124,9 +173,8 @@ class PBKDF2: DerivedKey { } func calculate(password: Data) throws -> Data { - let derivedKey = try! PKCS5.PBKDF2(password: Array(password), salt: Array(salt), iterations: iterations, keyLength: keyLength, variant: variant) do { - return try Data(derivedKey.calculate()) + return try Data(BetterPBKDF(password: Array(password), salt: Array(salt), iterations: iterations, keyLength: keyLength, variant: variant)) } catch { throw DecryptionError.invalidPassword } @@ -208,7 +256,7 @@ class Scrypt: DerivedKey { } /* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */ - let barray = try PBKDF2(salt: salt, iterations: 1, keyLength: p * 128 * r, variant: .sha256).calculate(password: password) + let barray = try PBKDF2Object(salt: salt, iterations: 1, keyLength: p * 128 * r, variant: .sha256).calculate(password: password) Array(barray).withUnsafeBytes { p in B.copyMemory(from: p.baseAddress!, byteCount: barray.count) @@ -224,7 +272,7 @@ class Scrypt: DerivedKey { let pointer = B.assumingMemoryBound(to: UInt8.self) let bufferPointer = UnsafeBufferPointer(start: pointer, count: p * 128 * r) let block = Data(buffer: bufferPointer) - return try PBKDF2(salt: block, iterations: 1, keyLength: dkLen, variant: .sha256).calculate(password: password) + return try PBKDF2Object(salt: block, iterations: 1, keyLength: dkLen, variant: .sha256).calculate(password: password) } /// Computes `B = SMix_r(B, N)`. diff --git a/Sources/Encryption/PrivateKey.swift b/Sources/Encryption/PrivateKey.swift new file mode 100644 index 0000000..057f2a3 --- /dev/null +++ b/Sources/Encryption/PrivateKey.swift @@ -0,0 +1,113 @@ +// +// PrivateKey.swift +// web3swift +// +// Created by Dmitry on 29/11/2018. +// Copyright © 2018 Bankex Foundation. All rights reserved. +// + +import Foundation +import BigInt + +/** + Secp256k1 private key. + + Used in ethereum accounts. You can get public key, address and sign some data + + ## Performance + + > Operations per second in debug and release build mode + ``` + Generate Private key: + release debug + 175772 160180 + + PrivateKey -> Public Key: + release debug + 26642 9036 + + PrivateKey -> Address: + release debug + 11894 2058 + ``` + */ +public class PrivateKey { + /// Private key data + public var privateKey: Data + + /// Singleton that generates public key from private key + public lazy var publicKey: Data = try! SECP256K1.privateToPublic(privateKey: privateKey) + + /// Singleton that generates address from public key + public lazy var address: Address = try! Web3Utils.publicToAddress(publicKey) + + /// Generates random private key. All generated keys are verified + public init() { + self.privateKey = .random(length: 32) + } + + /// Init with private key data. run .verify() to verify it + public init(_ privateKey: Data) { + self.privateKey = privateKey + } + + + /// Signs hash with private key signature + /// + /// - Parameter hash: 32 bytes hash. To get hash call data.keccak256() + /// - Returns: Signature that you can use in your transactions + /// - Throws: If hash size invalid hash size or private key. Call privateKey.verify() + public func sign(hash: Data) throws -> Signature { + let signature = try SECP256K1.signForRecovery(hash: hash, privateKey: privateKey).serializedSignature + return Signature(data: signature) + } + + + /// Verifies the private key. Also every 32 byte private keys are valid + /// + /// - Throws: SECP256K1Error.invalidPrivateKey + public func verify() throws { + try SECP256K1.verifyPrivateKey(privateKey: privateKey) + } +} + + +/// Signature of some hash. You can get it by calling PrivateKey.sign(hash:) +public class Signature { + /// Signature data + public let data: Data + + /// Init with data. Don't forget to call .check(compressed:) if you want to init with custom data + /// + /// - Parameter data: Signature data + public init(data: Data) { + self.data = data + } + + + /// Checks for signature + /// + /// - Parameter compressed: Checks for compressed signature (33 bytes long) + /// - Throws: SECP256K1Error.invalidSignatureSize or SECP256DataError.signatureCorrupted + public func check(compressed: Bool = false) throws { + if compressed { + guard data.count == 33 else { throw SECP256K1Error.invalidSignatureSize } + } else { + guard data.count == 65 else { throw SECP256K1Error.invalidSignatureSize } + } + guard v < 4 else { throw SECP256DataError.signatureCorrupted } + } + + /// Signature first 32 bytes + public lazy var r = BigUInt(data[0..<32]) + /// Signature next 32 bytes + public lazy var s = BigUInt(data[32..<64]) + /// Last signature byte. Should be less than 4 + public lazy var v: UInt8 = { + var v = data.last! + if v >= 27 { + v = v - 27 + } + return v + }() +} diff --git a/Sources/HookedFunctions/Web3+BrowserFunctions.swift b/Sources/HookedFunctions/Web3+BrowserFunctions.swift index 7190f31..3a2694d 100644 --- a/Sources/HookedFunctions/Web3+BrowserFunctions.swift +++ b/Sources/HookedFunctions/Web3+BrowserFunctions.swift @@ -75,7 +75,7 @@ public class Web3BrowserFunctions: Web3OptionsInheritable { public func sign(_ personalMessage: Data, account: String, password: String = "BANKEXFOUNDATION") -> String? { let keystoreManager = self.web3.provider.attachedKeystoreManager guard let signature = try? Web3Signer.signPersonalMessage(personalMessage, keystore: keystoreManager, account: Address(account), password: password) else { return nil } - return signature.toHexString().withHex + return signature.hex.withHex } /** @@ -244,7 +244,7 @@ public class Web3BrowserFunctions: Web3OptionsInheritable { guard let keystore = keystoreManager.walletForAddress(from) else { throw TransactionError.privateKeyNotFound(forAddress: from) } try Web3Signer.signTX(transaction: &transaction, keystore: keystore, account: from, password: password) - guard let signedData = transaction.encode(forSignature: false, chainId: nil)?.toHexString().withHex else { throw TransactionError.cannotEncodeTransaction } + guard let signedData = transaction.encode(forSignature: false, chainId: nil)?.hex.withHex else { throw TransactionError.cannotEncodeTransaction } return signedData } } diff --git a/Sources/Keystore/Accounts.swift b/Sources/Keystore/Accounts.swift deleted file mode 100644 index 8103555..0000000 --- a/Sources/Keystore/Accounts.swift +++ /dev/null @@ -1,198 +0,0 @@ -// -// Accounts.swift -// web3swift -// -// Created by Dmitry on 27/11/2018. -// Copyright © 2018 Bankex Foundation. All rights reserved. -// - -import Foundation -import CryptoSwift - -/* -## Version 3 - -{ - "crypto" : { - "cipher" : "aes-128-ctr", - "cipherparams" : { - "iv" : "83dbcc02d8ccb40e466191a123791e0e" - }, - "ciphertext" : "d172bf743a674da9cdad04534d56926ef8358534d458fffccd4e6ad2fbde479c", - "kdf" : "scrypt", - "kdfparams" : { - "dklen" : 32, - "n" : 262144, - "r" : 1, - "p" : 8, - "salt" : "ab0c7876052600dd703518d6fc3fe8984592145b591fc8fb5c6d43190334ba19" - }, - "mac" : "2103ac29920d71da29f15d75b4a16dbe95cfd7ff8faea1056c33131d846e3097" - }, - "id" : "3198bc9c-6672-5ab3-d995-4942343ae5b6", - "version" : 3 -} - -## Version 2 - -``` -{ - "crypto": { - "cipher": "aes-128-cbc", - "ciphertext": "07533e172414bfa50e99dba4a0ce603f654ebfa1ff46277c3e0c577fdc87f6bb4e4fe16c5a94ce6ce14cfa069821ef9b", - "cipherparams": { - "iv": "16d67ba0ce5a339ff2f07951253e6ba8" - }, - "kdf": "scrypt", - "kdfparams": { - "dklen": 32, - "n": 262144, - "p": 1, - "r": 8, - "salt": "06870e5e6a24e183a5c807bd1c43afd86d573f7db303ff4853d135cd0fd3fe91" - }, - "mac": "8ccded24da2e99a11d48cda146f9cc8213eb423e2ea0d8427f41c3be414424dd", - "version": 1 - }, - "id": "0498f19a-59db-4d54-ac95-33901b4f1870", - "version": 2 -} -``` - -## Version 1 - -``` -{ - "Address": "d4584b5f6229b7be90727b0fc8c6b91bb427821f", - "Crypto": { - "CipherText": "07533e172414bfa50e99dba4a0ce603f654ebfa1ff46277c3e0c577fdc87f6bb4e4fe16c5a94ce6ce14cfa069821ef9b", - "IV": "16d67ba0ce5a339ff2f07951253e6ba8", - "KeyHeader": { - "Kdf": "scrypt", - "KdfParams": { - "DkLen": 32, - "N": 262144, - "P": 1, - "R": 8, - "SaltLen": 32 - }, - "Version": "1" - }, - "MAC": "8ccded24da2e99a11d48cda146f9cc8213eb423e2ea0d8427f41c3be414424dd", - "Salt": "06870e5e6a24e183a5c807bd1c43afd86d573f7db303ff4853d135cd0fd3fe91" - }, - "Id": "0498f19a-59db-4d54-ac95-33901b4f1870", - "Version": "1" -} -``` -*/ - -/* -public struct KeystoreParamsV3: Decodable, Encodable { - var address: String? - var crypto: CryptoParamsV3 - var id: String? - var version: Int - - /// Init with all params - public init(address ad: String?, crypto cr: CryptoParamsV3, id i: String, version ver: Int) { - address = ad - crypto = cr - id = i - version = ver - } -} -/// Keystore encryption info -public struct CryptoParamsV3: Decodable, Encodable { - var ciphertext: String - var cipher: String - var cipherparams: CipherParamsV3 - var kdf: String - var kdfparams: KdfParamsV3 - var mac: String - var version: String? -} -*/ - - - - - -/* -class Accounts { - var accounts: [LockedAccount] - init(json: DictionaryReader) throws { - let version = try json.at("version").int() - let id = try json.at("id").string() - - - /* - { - "crypto" : { - "cipher" : "aes-128-ctr", - "cipherparams" : { - "iv" : "83dbcc02d8ccb40e466191a123791e0e" - }, - "ciphertext" : "d172bf743a674da9cdad04534d56926ef8358534d458fffccd4e6ad2fbde479c", - "kdf" : "scrypt", - "kdfparams" : { - "dklen" : 32, - "n" : 262144, - "r" : 1, - "p" : 8, - "salt" : "ab0c7876052600dd703518d6fc3fe8984592145b591fc8fb5c6d43190334ba19" - }, - "mac" : "2103ac29920d71da29f15d75b4a16dbe95cfd7ff8faea1056c33131d846e3097" - }, - "id" : "3198bc9c-6672-5ab3-d995-4942343ae5b6", - "version" : 3 - } - */ - } -} -*/ - -class JsonLockedAccount { - enum ParsingError: Swift.Error { - case invalidCipherText(String) - } - - let derivedKey: DerivedKey - let encryptedPrivateKey: Data - let blockMode: BlockMode - let mac: Data - init(json: DictionaryReader) throws { - let crypto = try json.at("crypto") - derivedKey = try DerivedKeyType(crypto.at("kdf").string()).derivedKey(crypto.at("kdfparams")) - - encryptedPrivateKey = try crypto.at("ciphertext").data() - guard encryptedPrivateKey.count == 32 else { throw ParsingError.invalidCipherText(encryptedPrivateKey.hex) } - blockMode = try AesMode(crypto.at("cipher").string()).blockMode(crypto.at("cipherparams").at("iv").data()) - mac = try crypto.at("mac").data() - } - - func unlock(password: Data) throws -> Account { - let derivedKey = try self.derivedKey.calculate(password: password) - var dataForMAC = Data() - dataForMAC.append(derivedKey.suffix(16)) - dataForMAC.append(encryptedPrivateKey) - let mac = dataForMAC.sha3(.keccak256) - guard self.mac.constantTimeComparisonTo(mac) else { throw DecryptionError.invalidPassword } - - let decryptionKey = derivedKey.suffix(16) - let aesCipher = try AES(key: decryptionKey.bytes, blockMode: blockMode, padding: .noPadding) - let privateKey = try aesCipher.decrypt(encryptedPrivateKey.bytes) - return Account(privateKey: Data(privateKey)) - } -} - -class LockedAccount { - -} - -class Account { - var privateKey: Data - init(privateKey: Data) { - self.privateKey = privateKey - } -} diff --git a/Sources/KeystoreManager/BIP32HDNode.swift b/Sources/KeystoreManager/BIP32HDNode.swift index 109116a..c7c0385 100644 --- a/Sources/KeystoreManager/BIP32HDNode.swift +++ b/Sources/KeystoreManager/BIP32HDNode.swift @@ -7,7 +7,7 @@ // import BigInt -import CryptoSwift +import Cryptor import Foundation extension UInt32 { @@ -143,7 +143,7 @@ public class HDNode { public init(seed: Data) throws { guard seed.count >= 16 else { throw Error.invalidSeedSize } let hmacKey = "Bitcoin seed".data(using: .ascii)! - let hmac: Authenticator = HMAC(key: hmacKey.bytes, variant: HMAC.Variant.sha512) + let hmac = HMAC(key: hmacKey.bytes, variant: .sha512) let entropy = try hmac.authenticate(seed.bytes) try entropy.checkEntropySize() let I_L = entropy[0 ..< 32] @@ -216,7 +216,7 @@ public class HDNode { if trueIndex < HDNode.hardenedIndexPrefix { trueIndex = trueIndex + HDNode.hardenedIndexPrefix } - let hmac: Authenticator = HMAC(key: chaincode.bytes, variant: .sha512) + let hmac = HMAC(key: chaincode.bytes, variant: .sha512) var inputForHMAC = Data() inputForHMAC.append(Data([UInt8(0x00)])) inputForHMAC.append(privateKey!) @@ -225,7 +225,7 @@ public class HDNode { try entropy.checkEntropySize() } else { trueIndex = index - let hmac: Authenticator = HMAC(key: chaincode.bytes, variant: .sha512) + let hmac = HMAC(key: chaincode.bytes, variant: .sha512) var inputForHMAC = Data() inputForHMAC.append(publicKey) inputForHMAC.append(trueIndex.serialize32()) @@ -269,7 +269,7 @@ public class HDNode { return newNode } else { // deriving only the public key guard !(index >= HDNode.hardenedIndexPrefix || hardened) else { throw DeriveError.noHardenedDerivation } - let hmac: Authenticator = HMAC(key: self.chaincode.bytes, variant: .sha512) + let hmac = HMAC(key: self.chaincode.bytes, variant: .sha512) var inputForHMAC = Data() inputForHMAC.append(publicKey) inputForHMAC.append(index.serialize32()) diff --git a/Sources/KeystoreManager/BIP32Keystore.swift b/Sources/KeystoreManager/BIP32Keystore.swift index 0e53123..0d12dfa 100644 --- a/Sources/KeystoreManager/BIP32Keystore.swift +++ b/Sources/KeystoreManager/BIP32Keystore.swift @@ -6,7 +6,6 @@ // Copyright © 2018 Bankex Foundation. All rights reserved. // -import CryptoSwift import Foundation private extension Dictionary where Value: Equatable { @@ -71,18 +70,18 @@ public class BIP32Keystore: AbstractKeystore { /// Init with json file public init?(_ jsonData: Data) { - guard var keystorePars = try? JSONDecoder().decode(KeystoreParamsBIP32.self, from: jsonData) else { return nil } - if keystorePars.version != 3 { return nil } - if keystorePars.crypto.version != nil && keystorePars.crypto.version != "1" { return nil } - if !keystorePars.isHDWallet { return nil } - for (p, ad) in keystorePars.pathToAddress { + guard var keystoreParams = try? JSONDecoder().decode(KeystoreParamsBIP32.self, from: jsonData) else { return nil } + if keystoreParams.version != 3 { return nil } + if keystoreParams.crypto.version != nil && keystoreParams.crypto.version != "1" { return nil } + if !keystoreParams.isHDWallet { return nil } + for (p, ad) in keystoreParams.pathToAddress { paths[p] = Address(ad) } - if keystorePars.rootPath == nil { - keystorePars.rootPath = HDNode.defaultPathPrefix + if keystoreParams.rootPath == nil { + keystoreParams.rootPath = HDNode.defaultPathPrefix } - keystoreParams = keystorePars - rootPrefix = keystoreParams!.rootPath! + self.keystoreParams = keystoreParams + rootPrefix = keystoreParams.rootPath! } /// Init with mnemonics @@ -194,12 +193,13 @@ public class BIP32Keystore: AbstractKeystore { let last16bytes = derivedKey[(derivedKey.count - 16) ... (derivedKey.count - 1)] let encryptionKey = derivedKey[0 ... 15] let IV = Data.random(length: 16) + var aesCipher: AES? switch aesMode { case "aes-128-cbc": - aesCipher = try? AES(key: encryptionKey.bytes, blockMode: CBC(iv: IV.bytes), padding: .pkcs7) + aesCipher = AES(key: encryptionKey.bytes, blockMode: CBC(iv: IV.bytes), padding: .pkcs7) case "aes-128-ctr": - aesCipher = try? AES(key: encryptionKey.bytes, blockMode: CTR(iv: IV.bytes), padding: .pkcs7) + aesCipher = AES(key: encryptionKey.bytes, blockMode: CTR(iv: IV.bytes), padding: .pkcs7) default: aesCipher = nil } @@ -211,10 +211,10 @@ public class BIP32Keystore: AbstractKeystore { var dataForMAC = Data() dataForMAC.append(last16bytes) dataForMAC.append(encryptedKeyData) - let mac = dataForMAC.sha3(.keccak256) - let kdfparams = KdfParamsV3(salt: saltData.toHexString(), dklen: dkLen, n: N, p: P, r: R, c: nil, prf: nil) - let cipherparams = CipherParamsV3(iv: IV.toHexString()) - let crypto = CryptoParamsV3(ciphertext: encryptedKeyData.toHexString(), cipher: aesMode, cipherparams: cipherparams, kdf: "scrypt", kdfparams: kdfparams, mac: mac.toHexString(), version: nil) + let mac = dataForMAC.keccak256() + let kdfparams = KdfParamsV3(salt: saltData.hex, dklen: dkLen, n: N, p: P, r: R, c: nil, prf: nil) + let cipherparams = CipherParamsV3(iv: IV.hex) + let crypto = CryptoParamsV3(ciphertext: encryptedKeyData.hex, cipher: aesMode, cipherparams: cipherparams, kdf: "scrypt", kdfparams: kdfparams, mac: mac.hex, version: nil) var pathToAddress = [String: String]() for (path, address) in paths { pathToAddress[path] = address.address @@ -248,20 +248,9 @@ public class BIP32Keystore: AbstractKeystore { passwordDerivedKey = scrypt(password: password, salt: saltData, length: derivedLen, N: N, R: R, P: P) case "pbkdf2": guard let algo = keystorePars.crypto.kdfparams.prf else { return nil } - var hashVariant: HMAC.Variant? - switch algo { - case "hmac-sha256": - hashVariant = HMAC.Variant.sha256 - case "hmac-sha384": - hashVariant = HMAC.Variant.sha384 - case "hmac-sha512": - hashVariant = HMAC.Variant.sha512 - default: - hashVariant = nil - } - guard hashVariant != nil else { return nil } + let hashVariant = try HmacVariant(algo) guard let c = keystorePars.crypto.kdfparams.c else { return nil } - guard let derivedArray = try? PKCS5.PBKDF2(password: Array(password.utf8), salt: saltData.bytes, iterations: c, keyLength: derivedLen, variant: hashVariant!).calculate() else { return nil } + guard let derivedArray = try? BetterPBKDF(password: Array(password.utf8), salt: saltData.bytes, iterations: c, keyLength: derivedLen, variant: hashVariant) else { return nil } passwordDerivedKey = Data(bytes: derivedArray) default: return nil @@ -273,7 +262,7 @@ public class BIP32Keystore: AbstractKeystore { guard let cipherText = Data.fromHex(keystorePars.crypto.ciphertext) else { return nil } guard cipherText.count % 32 == 0 else { return nil } dataForMAC.append(cipherText) - let mac = dataForMAC.sha3(.keccak256) + let mac = dataForMAC.keccak256() guard let calculatedMac = Data.fromHex(keystorePars.crypto.mac), mac.constantTimeComparisonTo(calculatedMac) else { return nil } let cipher = keystorePars.crypto.cipher let decryptionKey = derivedKey[0 ... 15] @@ -281,10 +270,10 @@ public class BIP32Keystore: AbstractKeystore { var decryptedPK: Array? switch cipher { case "aes-128-ctr": - guard let aesCipher = try? AES(key: decryptionKey.bytes, blockMode: CTR(iv: IV.bytes), padding: .pkcs7) else { return nil } + let aesCipher = AES(key: decryptionKey.bytes, blockMode: CTR(iv: IV.bytes), padding: .pkcs7) decryptedPK = try aesCipher.decrypt(cipherText.bytes) case "aes-128-cbc": - guard let aesCipher = try? AES(key: decryptionKey.bytes, blockMode: CBC(iv: IV.bytes), padding: .pkcs7) else { return nil } + let aesCipher = AES(key: decryptionKey.bytes, blockMode: CBC(iv: IV.bytes), padding: .pkcs7) decryptedPK = try? aesCipher.decrypt(cipherText.bytes) default: return nil diff --git a/Sources/KeystoreManager/BIP39.swift b/Sources/KeystoreManager/BIP39.swift index 3a65ebf..3f8e8a3 100644 --- a/Sources/KeystoreManager/BIP39.swift +++ b/Sources/KeystoreManager/BIP39.swift @@ -6,7 +6,7 @@ // Copyright © 2018 Bankex Foundation. All rights reserved. // -import CryptoSwift +import Cryptor import Foundation /// Mnemonics language @@ -157,7 +157,6 @@ public class Mnemonics { /// Generate seed from mnemonics string. This function will ignore dictionary and won't check for mnemonics error public static func seed(from mnemonics: String, password: String) -> Data { - let mnemData = Array(mnemonics.decomposedStringWithCompatibilityMapping.utf8) let salt = "mnemonic" + password let saltData = Array(salt.decomposedStringWithCompatibilityMapping.utf8) @@ -165,7 +164,7 @@ public class Mnemonics { // or keyLength > variant.digestLength * 256 // and .calculate() won't throw any errors // so i feel free to use "try!" - let seed = try! PKCS5.PBKDF2(password: mnemData, salt: saltData, iterations: 2048, keyLength: 64, variant: .sha512).calculate() + let seed = try! PBKDF.deriveKey(fromPassword: mnemonics.decomposedStringWithCompatibilityMapping, salt: saltData, prf: .sha512, rounds: 2048, derivedKeyLength: 64) return Data(bytes: seed) } @@ -221,14 +220,15 @@ public class Mnemonics { var fullEntropy = Data() fullEntropy.append(entropy) fullEntropy.append(checksum[0 ..< (checksumBits + 7) / 8]) - var wordList = [String]() + let separator = language.separator + let words = language.words + var indexes = [Int]() for i in 0 ..< fullEntropy.count * 8 / 11 { let bits = fullEntropy.bitsInRange(i * 11, 11) let index = Int(bits) - let word = language.words[index] - wordList.append(word) + indexes.append(index) } - self.string = wordList.joined(separator: language.separator) + self.string = indexes.map { words[$0] }.joined(separator: separator) self.language = language } diff --git a/Sources/KeystoreManager/EthereumAddress.swift b/Sources/KeystoreManager/EthereumAddress.swift index 04bc160..cc35efa 100644 --- a/Sources/KeystoreManager/EthereumAddress.swift +++ b/Sources/KeystoreManager/EthereumAddress.swift @@ -84,10 +84,7 @@ public struct Address { public var addressData: Data { switch type { case .normal: - guard let dataArray = Data.fromHex(_address) else { return Data() } - return dataArray - // guard let d = dataArray.setLengthLeft(20) else { return Data()} - // return d + return Data.fromHex(_address) ?? Data() case .contractDeployment: return Data() } @@ -107,7 +104,7 @@ public struct Address { /// Converts address to checksum address public static func toChecksumAddress(_ addr: String) -> String? { let address = addr.lowercased().withoutHex - guard let hash = address.data(using: .ascii)?.sha3(.keccak256).toHexString() else { return nil } + guard let hash = address.data(using: .ascii)?.keccak256().hex else { return nil } var ret = "0x" for (i, char) in address.enumerated() { @@ -145,7 +142,7 @@ public struct Address { /// - Parameter type: Address type. default: .normal /// - Important: addressData is not the utf8 format of hex string public init(_ addressData: Data, type: AddressType = .normal) { - _address = addressData.toHexString().withHex + _address = addressData.hex.withHex self.type = type } @@ -172,6 +169,12 @@ extension Address: Equatable { } } +extension Address: Hashable { + public var hashValue: Int { + return address.hashValue + } +} + extension Address: CustomStringConvertible { /// - Returns: Address hex string formatted to checksum public var description: String { diff --git a/Sources/KeystoreManager/EthereumKeystoreV3.swift b/Sources/KeystoreManager/EthereumKeystoreV3.swift index b099a85..fdcb0fc 100644 --- a/Sources/KeystoreManager/EthereumKeystoreV3.swift +++ b/Sources/KeystoreManager/EthereumKeystoreV3.swift @@ -6,7 +6,6 @@ // Copyright © 2017 Bankex Foundation. All rights reserved. // -import CryptoSwift import Foundation /** @@ -107,9 +106,9 @@ public class EthereumKeystoreV3: AbstractKeystore { var aesCipher: AES! switch aesMode { case "aes-128-cbc": - aesCipher = try? AES(key: encryptionKey.bytes, blockMode: CBC(iv: IV.bytes), padding: .noPadding) + aesCipher = AES(key: encryptionKey.bytes, blockMode: CBC(iv: IV.bytes), padding: .noPadding) case "aes-128-ctr": - aesCipher = try? AES(key: encryptionKey.bytes, blockMode: CTR(iv: IV.bytes), padding: .noPadding) + aesCipher = AES(key: encryptionKey.bytes, blockMode: CTR(iv: IV.bytes), padding: .noPadding) default: aesCipher = nil } @@ -119,10 +118,10 @@ public class EthereumKeystoreV3: AbstractKeystore { var dataForMAC = Data() dataForMAC.append(last16bytes) dataForMAC.append(encryptedKeyData) - let mac = dataForMAC.sha3(.keccak256) - let kdfparams = KdfParamsV3(salt: saltData.toHexString(), dklen: dkLen, n: N, p: P, r: R, c: nil, prf: nil) - let cipherparams = CipherParamsV3(iv: IV.toHexString()) - let crypto = CryptoParamsV3(ciphertext: encryptedKeyData.toHexString(), cipher: aesMode, cipherparams: cipherparams, kdf: "scrypt", kdfparams: kdfparams, mac: mac.toHexString(), version: nil) + let mac = dataForMAC.keccak256() + let kdfparams = KdfParamsV3(salt: saltData.hex, dklen: dkLen, n: N, p: P, r: R, c: nil, prf: nil) + let cipherparams = CipherParamsV3(iv: IV.hex) + let crypto = CryptoParamsV3(ciphertext: encryptedKeyData.hex, cipher: aesMode, cipherparams: cipherparams, kdf: "scrypt", kdfparams: kdfparams, mac: mac.hex, version: nil) let pubKey = try Web3Utils.privateToPublic(keyData!) let addr = try Web3Utils.publicToAddress(pubKey) address = addr @@ -153,44 +152,32 @@ public class EthereumKeystoreV3: AbstractKeystore { passwordDerivedKey = scrypt(password: password, salt: saltData, length: derivedLen, N: N, R: R, P: P) case "pbkdf2": guard let algo = keystoreParams.crypto.kdfparams.prf else { return nil } - var hashVariant: HMAC.Variant? - switch algo { - case "hmac-sha256": - hashVariant = HMAC.Variant.sha256 - case "hmac-sha384": - hashVariant = HMAC.Variant.sha384 - case "hmac-sha512": - hashVariant = HMAC.Variant.sha512 - default: - hashVariant = nil - } - guard hashVariant != nil else { return nil } + let hashVariant = try HmacVariant(algo) guard let c = keystoreParams.crypto.kdfparams.c else { return nil } - guard let derivedArray = try? PKCS5.PBKDF2(password: Array(password.utf8), salt: saltData.bytes, iterations: c, keyLength: derivedLen, variant: hashVariant!).calculate() else { return nil } + guard let derivedArray = try? BetterPBKDF(password: Array(password.utf8), salt: saltData.bytes, iterations: c, keyLength: derivedLen, variant: hashVariant) else { return nil } passwordDerivedKey = Data(bytes: derivedArray) default: return nil } guard let derivedKey = passwordDerivedKey else { return nil } var dataForMAC = Data() - let derivedKeyLast16bytes = Data(derivedKey[(derivedKey.count - 16) ... (derivedKey.count - 1)]) - dataForMAC.append(derivedKeyLast16bytes) + dataForMAC.append(derivedKey.suffix(16)) guard let cipherText = Data.fromHex(keystoreParams.crypto.ciphertext) else { return nil } if cipherText.count != 32 { return nil } dataForMAC.append(cipherText) - let mac = dataForMAC.sha3(.keccak256) + let mac = dataForMAC.keccak256() guard let calculatedMac = Data.fromHex(keystoreParams.crypto.mac), mac.constantTimeComparisonTo(calculatedMac) else { return nil } let cipher = keystoreParams.crypto.cipher - let decryptionKey = derivedKey[0 ... 15] + let decryptionKey = derivedKey.prefix(16) guard let IV = Data.fromHex(keystoreParams.crypto.cipherparams.iv) else { return nil } var decryptedPK: Array? switch cipher { case "aes-128-ctr": - guard let aesCipher = try? AES(key: decryptionKey.bytes, blockMode: CTR(iv: IV.bytes), padding: .noPadding) else { return nil } + let aesCipher = AES(key: decryptionKey.bytes, blockMode: CTR(iv: IV.bytes), padding: .noPadding) decryptedPK = try aesCipher.decrypt(cipherText.bytes) case "aes-128-cbc": - guard let aesCipher = try? AES(key: decryptionKey.bytes, blockMode: CBC(iv: IV.bytes), padding: .noPadding) else { return nil } - decryptedPK = try? aesCipher.decrypt(cipherText.bytes) + let aesCipher = AES(key: decryptionKey.bytes, blockMode: CBC(iv: IV.bytes), padding: .noPadding) + decryptedPK = try aesCipher.decrypt(cipherText.bytes) default: return nil } @@ -201,7 +188,9 @@ public class EthereumKeystoreV3: AbstractKeystore { /// Returns json file encoded with v3 standard public func serialize() throws -> Data? { guard let params = self.keystoreParams else { return nil } - let data = try JSONEncoder().encode(params) + let encoder = JSONEncoder() + encoder.outputFormatting = .prettyPrinted + let data = try encoder.encode(params) return data } } diff --git a/Sources/Transaction/BloomFilter.swift b/Sources/Transaction/BloomFilter.swift index 0b86158..dc15db1 100644 --- a/Sources/Transaction/BloomFilter.swift +++ b/Sources/Transaction/BloomFilter.swift @@ -7,7 +7,6 @@ // import BigInt -import CryptoSwift import Foundation /// Ethereum bloom filter @@ -39,7 +38,7 @@ public struct EthereumBloomFilter { } static func bloom9(_ data: Data) -> BigUInt { - var b = data.sha3(.keccak256) + var b = data.keccak256() var r = BigUInt(0) let mask = BigUInt(2047) for i in stride(from: 0, to: 6, by: 2) { diff --git a/Sources/Transaction/EthereumTransaction.swift b/Sources/Transaction/EthereumTransaction.swift index 5309dbe..ff6cb52 100644 --- a/Sources/Transaction/EthereumTransaction.swift +++ b/Sources/Transaction/EthereumTransaction.swift @@ -56,7 +56,7 @@ public struct EthereumTransaction: CustomStringConvertible { public var hash: Data? { let chainId = inferedChainID ?? self.chainID guard let encoded = self.encode(forSignature: false, chainId: chainId) else { return nil } - return encoded.sha3(.keccak256) + return encoded.keccak256() } /// Init with transaction parameters @@ -169,7 +169,7 @@ hash: \(String(describing: hash)) public var txhash: String? { guard sender != nil else { return nil } guard let hash = self.hash else { return nil } - let txid = hash.toHexString().withHex.lowercased() + let txid = hash.hex.withHex.lowercased() return txid } @@ -209,13 +209,13 @@ hash: \(String(describing: hash)) var params = TransactionParameters(from: from?.address.lowercased(), to: toString) let gasEncoding = gasLimit.abiEncode(bits: 256) - params.gas = gasEncoding?.toHexString().withHex.stripLeadingZeroes() + params.gas = gasEncoding?.hex.withHex.stripLeadingZeroes() let gasPriceEncoding = gasPrice.abiEncode(bits: 256) - params.gasPrice = gasPriceEncoding?.toHexString().withHex.stripLeadingZeroes() + params.gasPrice = gasPriceEncoding?.hex.withHex.stripLeadingZeroes() let valueEncoding = value.abiEncode(bits: 256) - params.value = valueEncoding?.toHexString().withHex.stripLeadingZeroes() + params.value = valueEncoding?.hex.withHex.stripLeadingZeroes() if data != Data() { - params.data = data.toHexString().withHex + params.data = data.hex.withHex } else { params.data = "0x" } @@ -229,8 +229,7 @@ hash: \(String(describing: hash)) /// - Returns: Transaction hash public func hashForSignature(chainID: NetworkId? = nil) -> Data? { guard let encoded = self.encode(forSignature: true, chainId: chainID) else { return nil } - let hash = encoded.sha3(.keccak256) - return hash + return encoded.keccak256() } init(_ json: [String: Any]) throws { @@ -323,7 +322,7 @@ hash: \(String(describing: hash)) static func createRawTransaction(transaction: EthereumTransaction) -> JsonRpcRequest? { guard transaction.sender != nil else { return nil } guard let encodedData = transaction.encode() else { return nil } - let hex = encodedData.toHexString().withHex.lowercased() + let hex = encodedData.hex.withHex.lowercased() var request = JsonRpcRequest(method: .sendRawTransaction) let params = [hex] as Array let pars = JsonRpcParams(params: params) diff --git a/Sources/ABIv2/SolidityDataReader.swift b/Sources/Transactions/SolidityDataReader.swift similarity index 100% rename from Sources/ABIv2/SolidityDataReader.swift rename to Sources/Transactions/SolidityDataReader.swift diff --git a/Sources/ABIv2/SolidityDataWriter.swift b/Sources/Transactions/SolidityDataWriter.swift similarity index 84% rename from Sources/ABIv2/SolidityDataWriter.swift rename to Sources/Transactions/SolidityDataWriter.swift index 9afda0f..50ada3d 100644 --- a/Sources/ABIv2/SolidityDataWriter.swift +++ b/Sources/Transactions/SolidityDataWriter.swift @@ -35,7 +35,7 @@ private extension Data { } } class SolidityDataWriter { - private var data: Data + private(set) var data: Data private var dynamicData = [SolidityDataPointer]() var offset = 0 init() { @@ -78,7 +78,19 @@ class SolidityDataWriter { self.data.append(data) } } + func setLength() { + data.replaceSubrange(0..<0, with: length(data.count, offset: 0xc0)) + } + + private func length(_ l: Int, offset: Int) -> Data { + func byte(_ value: Int) -> Data { return Data([UInt8(value)]) } + guard l + offset > 0xf7 else { return byte(l+offset) } + let serialized = BigUInt(l).serialize() + return byte(0xf7+serialized.count) + serialized + } + func done() -> Data { + guard !dynamicData.isEmpty else { return data } for pointer in dynamicData { data.write(data: (data.count - offset).solidityData, at: pointer.position) if pointer.data.count == 0 { diff --git a/Sources/ABIv2/SolidityFunction.swift b/Sources/Transactions/SolidityFunction.swift similarity index 100% rename from Sources/ABIv2/SolidityFunction.swift rename to Sources/Transactions/SolidityFunction.swift diff --git a/Sources/ABIv2/SolidityTypes.swift b/Sources/Transactions/SolidityTypes.swift similarity index 99% rename from Sources/ABIv2/SolidityTypes.swift rename to Sources/Transactions/SolidityTypes.swift index 9994413..dd030d7 100644 --- a/Sources/ABIv2/SolidityTypes.swift +++ b/Sources/Transactions/SolidityTypes.swift @@ -41,6 +41,10 @@ public enum AbiError: Error { ``` */ public class SolidityType: Equatable, CustomStringConvertible { + static let uint256 = SUInt(bits: 256) + static let address = SAddress() + + /// SolidityType array size public enum ArraySize { /// returns number of elements in a static array diff --git a/Sources/Transactions/Transaction.swift b/Sources/Transactions/Transaction.swift new file mode 100644 index 0000000..3c62a7b --- /dev/null +++ b/Sources/Transactions/Transaction.swift @@ -0,0 +1,123 @@ +// +// Transaction.swift +// web3swift +// +// Created by Dmitry on 30/11/2018. +// Copyright © 2018 Bankex Foundation. All rights reserved. +// + +import Foundation +import BigInt + +/// WIP +class Transaction { + var nonce: BigUInt = 0 + var gasPrice: BigUInt + var gasLimit: BigUInt + var to: Address + var value: BigUInt + var data: Data + + init(gasPrice: BigUInt, gasLimit: BigUInt, to: Address, value: BigUInt, data: Data) { + self.gasPrice = gasPrice + self.gasLimit = gasLimit + self.to = to + self.value = value + self.data = data + } + + func write(to data: TransactionDataWriter) { + data.append(nonce) + data.append(gasPrice) + data.append(gasLimit) + data.append(to) + data.append(value) + data.append(self.data) + } + + func write(networkId: NetworkId, to data: TransactionDataWriter) { + data.append(networkId.rawValue) + data.append(0) + data.append(0) + } + + func sign(using privateKey: PrivateKey, networkId: NetworkId? = nil) throws -> SignedTransaction { + let data = TransactionDataWriter() + write(to: data) + if let networkId = networkId { + write(networkId: networkId, to: data) + } + let hash = data.done().keccak256() + let signature = try privateKey.sign(hash: hash) + return SignedTransaction(transaction: self, signature: signature, networkId: networkId) + } +} + +/// WIP +class SignedTransaction { + let transaction: Transaction + let signature: Signature + let networkId: NetworkId? + init(transaction: Transaction, signature: Signature, networkId: NetworkId?) { + self.transaction = transaction + self.signature = signature + self.networkId = networkId + } + + func data() -> Data { + let data = TransactionDataWriter() + transaction.write(to: data) + if let networkId = networkId?.rawValue { + data.append(BigUInt(signature.v) + 35 + networkId + networkId) + } else { + data.append(BigUInt(signature.v) + 27) + } + data.append(signature.r) + data.append(signature.s) + return data.done() + } +} + +extension Data { + private func byte(_ value: Int) -> Data { + return Data([UInt8(value)]) + } + func length(offset: Int) -> Data { + guard !(count == 1 && self[0] < UInt8(offset)) else { return Data() } + let max = 0x37 + offset + guard count + offset > max else { return byte(count + offset) } + let serialized = BigUInt(count).serialize() + return byte(max + serialized.count) + serialized + } +} + +/// WIP +class TransactionDataWriter { + private(set) var data: Data + init() { + self.data = Data() + } + init(data: Data) { + self.data = data + } + + func append(_ value: BigUInt) { + _append(value.serialize()) + } + func append(_ value: Address) { + _append(value.addressData) + } + func _append(_ value: Data) { + data.append(value.length(offset: 0x80)) + data.append(value) + } + func append(_ value: Data) { + data.append(value.length(offset: 0x80)) + data.append(value) + } + + func done() -> Data { + data.replaceSubrange(0..<0, with: data.length(offset: 0xc0)) + return data + } +} diff --git a/Sources/Utils/EIP67Code.swift b/Sources/Utils/EIP67Code.swift index f8e7441..a3d233b 100644 --- a/Sources/Utils/EIP67Code.swift +++ b/Sources/Utils/EIP67Code.swift @@ -51,7 +51,7 @@ public struct EIP67Code { } else if let number = el.1 as? BigInt { return el.0.abiRepresentation + " " + String(number, radix: 10) } else if let data = el.1 as? Data { - return el.0.abiRepresentation + " " + data.toHexString().withHex + return el.0.abiRepresentation + " " + data.hex.withHex } return "" }).joined(separator: ", ") + ")" @@ -108,7 +108,7 @@ public struct EIP67Code { if let data = self.data { switch data { case let .data(d): - queryItems.append(URLQueryItem(name: "data", value: d.toHexString().withHex)) + queryItems.append(URLQueryItem(name: "data", value: d.hex.withHex)) case let .function(f): if let enc = f.toString() { queryItems.append(URLQueryItem(name: "function", value: enc)) diff --git a/Sources/Utils/RLP.swift b/Sources/Utils/RLP.swift index 556766e..28f3979 100644 --- a/Sources/Utils/RLP.swift +++ b/Sources/Utils/RLP.swift @@ -13,8 +13,8 @@ protocol ArrayType {} extension Array: ArrayType {} struct RLP { - static var length56 = BigUInt(UInt(56)) - static var lengthMax = (BigUInt(UInt(1)) << 256) + static var length56 = BigUInt(56) + static var lengthMax = (BigUInt(1) << 256) static func encode(_ element: AnyObject) -> Data? { if let string = element as? String { @@ -65,7 +65,7 @@ struct RLP { if data.count == 1 && data.bytes[0] < UInt8(0x80) { return data } else { - guard let length = encodeLength(data.count, offset: UInt8(0x80)) else { return nil } + guard let length = encodeLength(data.count, offset: 0x80) else { return nil } var encoded = Data() encoded.append(length) encoded.append(data) diff --git a/Sources/Web3/Web3+Eth.swift b/Sources/Web3/Web3+Eth.swift index 6ec65e1..43b8aba 100644 --- a/Sources/Web3/Web3+Eth.swift +++ b/Sources/Web3/Web3+Eth.swift @@ -266,7 +266,7 @@ public class Web3Eth: Web3OptionsInheritable { /// /// - Returns: Found Block public func getBlockByHashPromise(_ hash: Data, fullTransactions: Bool = false) -> Promise { - let hashString = hash.toHexString().withHex + let hashString = hash.hex.withHex return getBlockByHashPromise(hashString, fullTransactions: fullTransactions) } @@ -294,7 +294,7 @@ public class Web3Eth: Web3OptionsInheritable { /// - Returns: Transaction details for particular transaction hash. Details indicate position of the transaction in a particular block, /// as well as original transaction details such as value, gas limit, gas price, etc. public func getTransactionDetailsPromise(_ txhash: Data) -> Promise { - let hashString = txhash.toHexString().withHex + let hashString = txhash.hex.withHex return getTransactionDetailsPromise(hashString) } @@ -385,7 +385,7 @@ public class Web3Eth: Web3OptionsInheritable { /// was included in block, so it contains logs and status, such as succesful or failed transaction. /// - Important: This function is synchronous! public func getTransactionReceiptPromise(_ txhash: Data) -> Promise { - let hashString = txhash.toHexString().withHex + let hashString = txhash.hex.withHex return getTransactionReceiptPromise(hashString) } diff --git a/Sources/Web3/Web3+Personal.swift b/Sources/Web3/Web3+Personal.swift index 2d15af9..11f9c86 100644 --- a/Sources/Web3/Web3+Personal.swift +++ b/Sources/Web3/Web3+Personal.swift @@ -82,7 +82,7 @@ public class Web3Personal: Web3OptionsInheritable { let queue = web3.requestDispatcher.queue do { if web3.provider.attachedKeystoreManager.isEmpty { - let hexData = message.toHexString().withHex + let hexData = message.hex.withHex let request = JsonRpcRequest(method: .personalSign, parameters: from.address.lowercased(), hexData) return web3.dispatch(request).map(on: queue) { response in guard let value: Data = response.getValue() else { diff --git a/Sources/Web3/Web3+TransactionIntermediate.swift b/Sources/Web3/Web3+TransactionIntermediate.swift index 55833ce..6edbbea 100644 --- a/Sources/Web3/Web3+TransactionIntermediate.swift +++ b/Sources/Web3/Web3+TransactionIntermediate.swift @@ -298,11 +298,10 @@ public class TransactionIntermediate { callPromise.done(on: queue) { data in do { if self.method == "fallback" { - let resultHex = data.toHexString().withHex + let resultHex = data.hex.withHex let response = Web3Response(["result": resultHex as Any]) seal.fulfill(response) } else { - print(data.toHexString()) guard let decodedData = self.contract.decodeReturnData(self.method, data: data) else { throw Web3Error.processingError("Can not decode returned parameters") } @@ -311,9 +310,7 @@ public class TransactionIntermediate { } catch { seal.reject(error) } - }.catch(on: queue) { err in - seal.reject(err) - } + }.catch(on: queue, seal.reject) } return returnPromise } diff --git a/Sources/Web3/Web3+Utils.swift b/Sources/Web3/Web3+Utils.swift index 438077a..ee14e33 100644 --- a/Sources/Web3/Web3+Utils.swift +++ b/Sources/Web3/Web3+Utils.swift @@ -113,7 +113,7 @@ extension Web3Utils { stipped = stipped[1 ... 64] } guard stipped.count == 64 else { throw PublicKeyToAddressError.invalidPublicKeySize } - let sha3 = stipped.sha3(.keccak256) + let sha3 = stipped.keccak256() let addressData = sha3[12 ..< 32] return addressData } @@ -125,7 +125,7 @@ extension Web3Utils { /// Returns the Address object. public static func publicToAddress(_ publicKey: Data) throws -> Address { let addressData = try Web3Utils.publicToAddressData(publicKey) - let address = addressData.toHexString().withHex.lowercased() + let address = addressData.hex return Address(address) } @@ -135,7 +135,7 @@ extension Web3Utils { /// Returns a 0x prefixed hex string. public static func publicToAddressString(_ publicKey: Data) throws -> String { let addressData = try Web3Utils.publicToAddressData(publicKey) - let address = addressData.toHexString().withHex.lowercased() + let address = addressData.hex.withHex.lowercased() return address } @@ -159,7 +159,7 @@ extension Web3Utils { data.append(prefixData) data.append(personalMessage) } - return data.sha3(.keccak256) + return data.keccak256() } /// Parse a user-supplied string using the number of decimals for particular Ethereum unit. @@ -226,13 +226,13 @@ extension Web3Utils { /// returns Ethereum variant of sha3 (keccak256) of data. Returns nil is data is empty public static func keccak256(_ data: Data) -> Data? { if data.count == 0 { return nil } - return data.sha3(.keccak256) + return data.keccak256() } /// returns Ethereum variant of sha3 (keccak256) of data. Returns nil is data is empty public static func sha3(_ data: Data) -> Data? { if data.count == 0 { return nil } - return data.sha3(.keccak256) + return data.keccak256() } /// returns sha256 of data. Returns nil is data is empty diff --git a/Tests/ABITests.swift b/Tests/ABITests.swift index af302f0..c560c05 100644 --- a/Tests/ABITests.swift +++ b/Tests/ABITests.swift @@ -76,8 +76,8 @@ class ABITests: XCTestCase { let data = ABIv2Encoder.encode(types: types, values: [BigUInt(69), true] as [AnyObject]) XCTAssert(data != nil, "failed to encode") let expected = "0x00000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000000000001" - print(data!.toHexString().lowercased()) - XCTAssert(data?.toHexString().lowercased().withHex == expected, "failed to encode") + print(data!.hex.lowercased()) + XCTAssert(data?.hex.lowercased().withHex == expected, "failed to encode") } func testABIv2encoding2() { @@ -87,8 +87,8 @@ class ABITests: XCTestCase { let data = ABIv2Encoder.encode(types: types, values: ["dave"] as [AnyObject]) XCTAssert(data != nil, "failed to encode") let expected = "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000046461766500000000000000000000000000000000000000000000000000000000" - print(data!.toHexString().lowercased()) - XCTAssert(data?.toHexString().lowercased().withHex == expected, "failed to encode") + print(data!.hex.lowercased()) + XCTAssert(data?.hex.lowercased().withHex == expected, "failed to encode") } func testABIv2encoding3() { @@ -103,8 +103,8 @@ class ABITests: XCTestCase { let data = ABIv2Encoder.encode(types: types, values: ["dave".data, true, [BigUInt(1), BigUInt(2), BigUInt(3)]] as [AnyObject]) XCTAssert(data != nil, "failed to encode") let expected = "0x0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000464617665000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003" - print(data!.toHexString().lowercased()) - XCTAssert(data?.toHexString().lowercased().withHex == expected, "failed to encode") + print(data!.hex.lowercased()) + XCTAssert(data?.hex.lowercased().withHex == expected, "failed to encode") } func testABIv2encoding4() { @@ -117,7 +117,7 @@ class ABITests: XCTestCase { values: [number!] as [AnyObject]) XCTAssertNotNil(data, "failed to encode") let expected = "0xfffffffffffff38dd0f10627f5529bdb2c52d4846810af0ac000000000000001" - let result = data!.toHexString().lowercased().withHex + let result = data!.hex.lowercased().withHex print(result) XCTAssert(result == expected, "failed to encode") } @@ -132,8 +132,8 @@ class ABITests: XCTestCase { values: [string] as [AnyObject]) XCTAssertNotNil(data, "failed to encode") let expected = "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c22068656c6c6f20776f726c642068656c6c6f20776f726c642068656c6c6f20776f726c642068656c6c6f20776f726c64202068656c6c6f20776f726c642068656c6c6f20776f726c642068656c6c6f20776f726c642068656c6c6f20776f726c64202068656c6c6f20776f726c642068656c6c6f20776f726c642068656c6c6f20776f726c642068656c6c6f20776f726c642068656c6c6f20776f726c642068656c6c6f20776f726c642068656c6c6f20776f726c642068656c6c6f20776f726c64000000000000000000000000000000000000000000000000000000000000" - print(data!.toHexString().lowercased().withHex) - XCTAssert(data?.toHexString().lowercased().withHex == expected, "failed to encode") + print(data!.hex.lowercased().withHex) + XCTAssert(data?.hex.lowercased().withHex == expected, "failed to encode") } func testABIv2encoding6() { @@ -150,8 +150,8 @@ class ABITests: XCTestCase { "Hello, world!"] as [AnyObject]) XCTAssert(data != nil, "failed to encode") let expected = "0x00000000000000000000000000000000000000000000000000000000000001230000000000000000000000000000000000000000000000000000000000000080313233343536373839300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000004560000000000000000000000000000000000000000000000000000000000000789000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20776f726c642100000000000000000000000000000000000000" - print(data!.toHexString().lowercased()) - XCTAssert(data?.toHexString().lowercased().withHex == expected, "failed to encode") + print(data!.hex.lowercased()) + XCTAssert(data?.hex.lowercased().withHex == expected, "failed to encode") } func testABIv2encoding7() { @@ -162,8 +162,8 @@ class ABITests: XCTestCase { values: [["Hello", "World"]] as [AnyObject]) XCTAssert(data != nil, "failed to encode") let expected = "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000" - print(data!.toHexString().lowercased()) - XCTAssert(data?.toHexString().lowercased() == expected, "failed to encode") + print(data!.hex.lowercased()) + XCTAssert(data?.hex.lowercased() == expected, "failed to encode") } func testABIv2encoding8() { @@ -174,8 +174,8 @@ class ABITests: XCTestCase { values: [["Hello", "World"]] as [AnyObject]) XCTAssert(data != nil, "failed to encode") let expected = "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000" - print(data!.toHexString().lowercased()) - XCTAssert(data?.toHexString().lowercased() == expected, "failed to encode") + print(data!.hex.lowercased()) + XCTAssert(data?.hex.lowercased() == expected, "failed to encode") } func testABIv2Decoding1() { diff --git a/Tests/BetterABI/BetterERC20Tests.swift b/Tests/BetterABI/BetterERC20Tests.swift index e631dd0..a57d13f 100644 --- a/Tests/BetterABI/BetterERC20Tests.swift +++ b/Tests/BetterABI/BetterERC20Tests.swift @@ -24,7 +24,7 @@ class BetterERC20Tests: XCTestCase { let amount = BigUInt(10).power(18) let arguments: [SolidityDataRepresentable] = [user, amount] let request = arguments.data(function: "transfer(address,uint256)") - XCTAssertEqual(request.toHexString(), "a9059cbb0000000000000000000000006a6a0b4aaa60e97386f94c5414522159b45dede80000000000000000000000000000000000000000000000000de0b6b3a7640000") + XCTAssertEqual(request.hex, "a9059cbb0000000000000000000000006a6a0b4aaa60e97386f94c5414522159b45dede80000000000000000000000000000000000000000000000000de0b6b3a7640000") let response = BigUInt(1).solidityData let success = try SolidityDataReader(response).bool() diff --git a/Tests/ERC20Tests.swift b/Tests/ERC20Tests.swift index 0a2675f..e69b044 100644 --- a/Tests/ERC20Tests.swift +++ b/Tests/ERC20Tests.swift @@ -32,7 +32,7 @@ class ERC20Tests: XCTestCase { let parameters = [address, amount] as [AnyObject] let result = method[0].encodeParameters(parameters) print(abiNative) - let hex = result!.toHexString() + let hex = result!.hex print(hex) XCTAssert(hex == "a9059cbb000000000000000000000000e6877a4d8806e9a9f12eb2e8561ea6c1db19978d0000000000000000000000000000000000000000000000000de0b6b3a7640000", "Failed to encode ERC20") let dummyTrue = BigUInt(1).abiEncode(bits: 256) diff --git a/Tests/KeystoreTests.swift b/Tests/KeystoreTests.swift index 0d58b40..8419c66 100644 --- a/Tests/KeystoreTests.swift +++ b/Tests/KeystoreTests.swift @@ -6,8 +6,8 @@ // Copyright © 2018 Bankex Foundation. All rights reserved. // -import CryptoSwift import XCTest +import Cryptor @testable import web3swift @@ -20,13 +20,13 @@ class KeystoresTests: XCTestCase { XCTAssertEqual(mnemonics.string, "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about") mnemonics.password = "TREZOR" var seed = mnemonics.seed() - XCTAssert(seed.toHexString() == "c55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f09a6987599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04") + XCTAssert(seed.hex == "c55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f09a6987599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04") entropy = Data.fromHex("68a79eaca2324873eacc50cb9c6eca8cc68ea5d936f98787c60c7ebc74e6ce7c")! mnemonics = try Mnemonics(entropy: entropy) XCTAssertEqual(mnemonics.string, "hamster diagram private dutch cause delay private meat slide toddler razor book happy fancy gospel tennis maple dilemma loan word shrug inflict delay length") mnemonics.password = "TREZOR" seed = mnemonics.seed() - XCTAssert(seed.toHexString() == "64c87cde7e12ecf6704ab95bb1408bef047c22db4cc7491c4271d170a1b213d20b385bc1588d9c7b38f1b39d415665b8a9030c9ec653d75e65f847d8fc1fc440") + XCTAssert(seed.hex == "64c87cde7e12ecf6704ab95bb1408bef047c22db4cc7491c4271d170a1b213d20b385bc1588d9c7b38f1b39d415665b8a9030c9ec653d75e65f847d8fc1fc440") } func testImportAndExport() throws { @@ -35,11 +35,11 @@ class KeystoresTests: XCTestCase { """ let keystore = EthereumKeystoreV3(json)! let data = try keystore.serialize()! - let key = try keystore.UNSAFE_getPrivateKeyData(password: "hello world", account: keystore.addresses[0]).toHexString() + let key = try keystore.UNSAFE_getPrivateKeyData(password: "hello world", account: keystore.addresses[0]).hex let keystore2 = EthereumKeystoreV3(data)! let data2 = try keystore2.serialize()! - let key2 = try keystore2.UNSAFE_getPrivateKeyData(password: "hello world", account: keystore.addresses[0]).toHexString() + let key2 = try keystore2.UNSAFE_getPrivateKeyData(password: "hello world", account: keystore.addresses[0]).hex XCTAssertEqual(data,data2) XCTAssertEqual(key,key2) @@ -49,8 +49,8 @@ class KeystoresTests: XCTestCase { // 0.0021849870681762695 sec to complete let seed = Data.fromHex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b")! let data = Data.fromHex("4869205468657265")! - let hmac = try! HMAC(key: seed.bytes, variant: HMAC.Variant.sha512).authenticate(data.bytes) - XCTAssert(Data(hmac).toHexString() == "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854") + let hmac = try! HMAC(key: seed.bytes, variant: .sha512).authenticate(data.bytes) + XCTAssert(Data(hmac).hex == "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854") } func testMnemonicsWithAllLanguagesAndEntropySizes() { @@ -84,11 +84,11 @@ class KeystoresTests: XCTestCase { func testBIP32keystoreExportPrivateKey() throws { // 6.153380036354065 sec to complete - let mnemonics = try Mnemonics("normal dune pole key case cradle unfold require tornado mercy hospital buyer") - let keystore = try BIP32Keystore(mnemonics: mnemonics, password: "") + let mnemonics = try! Mnemonics("normal dune pole key case cradle unfold require tornado mercy hospital buyer") + let keystore = try! BIP32Keystore(mnemonics: mnemonics, password: "") XCTAssertNotNil(keystore) let account = keystore.addresses[0] - _ = try keystore.UNSAFE_getPrivateKeyData(password: "", account: account) + _ = try! keystore.UNSAFE_getPrivateKeyData(password: "", account: account) } func testBIP32keystoreMatching() throws { @@ -99,7 +99,7 @@ class KeystoresTests: XCTestCase { let account = keystore.addresses[0] let privateKey = try keystore.UNSAFE_getPrivateKeyData(password: "", account: account) let publicKey = try Web3Utils.privateToPublic(privateKey, compressed: true) - XCTAssertEqual(publicKey.toHexString(), "027160bd3a4d938cac609ff3a11fe9233de7b76c22a80d2b575e202cbf26631659") + XCTAssertEqual(publicKey.hex, "027160bd3a4d938cac609ff3a11fe9233de7b76c22a80d2b575e202cbf26631659") } func testBIP32keystoreMatchingRootNode() throws { @@ -120,7 +120,7 @@ class KeystoresTests: XCTestCase { let account = keystore.addresses[0] let key = try keystore.UNSAFE_getPrivateKeyData(password: "", account: account) let pubKey = try Web3Utils.privateToPublic(key, compressed: true) - XCTAssertEqual(pubKey.toHexString(), "027160bd3a4d938cac609ff3a11fe9233de7b76c22a80d2b575e202cbf26631659") + XCTAssertEqual(pubKey.hex, "027160bd3a4d938cac609ff3a11fe9233de7b76c22a80d2b575e202cbf26631659") } func testByBIP32keystoreCreateChildAccount() throws { @@ -169,14 +169,14 @@ class KeystoresTests: XCTestCase { // let pass = "passDATAb00AB7YxDTTl".data // let salt = "saltKEYbcTcXHCBxtjD2".data // let dataArray = try? PKCS5.PBKDF2(password: pass.bytes, salt: salt.bytes, iterations: 100000, keyLength: 65, variant: HMAC.Variant.sha512).calculate() - // XCTAssert(Data(dataArray!).toHexString().withHex.lowercased() == "0x594256B0BD4D6C9F21A87F7BA5772A791A10E6110694F44365CD94670E57F1AECD797EF1D1001938719044C7F018026697845EB9AD97D97DE36AB8786AAB5096E7".lowercased()) + // XCTAssert(Data(dataArray!).hex.withHex.lowercased() == "0x594256B0BD4D6C9F21A87F7BA5772A791A10E6110694F44365CD94670E57F1AECD797EF1D1001938719044C7F018026697845EB9AD97D97DE36AB8786AAB5096E7".lowercased()) // } func testRIPEMD() { // sec to complete let data = "message digest".data(using: .ascii) let hash = RIPEMD160.hash(message: data!) - XCTAssert(hash.toHexString() == "5d0689ef49d2fae572b881b123a85ffa21595f36") + XCTAssert(hash.hex == "5d0689ef49d2fae572b881b123a85ffa21595f36") } func testHD32() throws { @@ -204,7 +204,7 @@ class KeystoresTests: XCTestCase { XCTAssert(nextNode.index == UInt32(0)) XCTAssert(nextNode.isHardened == false) XCTAssert(nextNode.parentFingerprint == Data.fromHex("3442193e")) - XCTAssert(nextNode.publicKey.toHexString() == "027c4b09ffb985c298afe7e5813266cbfcb7780b480ac294b0b43dc21f2be3d13c") + XCTAssert(nextNode.publicKey.hex == "027c4b09ffb985c298afe7e5813266cbfcb7780b480ac294b0b43dc21f2be3d13c") XCTAssert(nextNode.serializeToString() == "xpub68Gmy5EVb2BdFbj2LpWrk1M7obNuaPTpT5oh9QCCo5sRfqSHVYWex97WpDZzszdzHzxXDAzPLVSwybe4uPYkSk4G3gnrPqqkV9RyNzAcNJ1") XCTAssert(nextNode.serializeToString(serializePublic: false) == "xprv9uHRZZhbkedL37eZEnyrNsQPFZYRAvjy5rt6M1nbEkLSo378x1CQQLo2xxBvREwiK6kqf7GRNvsNEchwibzXaV6i5GcsgyjBeRguXhKsi4R") @@ -213,7 +213,7 @@ class KeystoresTests: XCTestCase { XCTAssert(nextNodeHardened.index == UInt32(0)) XCTAssert(nextNodeHardened.isHardened == true) XCTAssert(nextNodeHardened.parentFingerprint == Data.fromHex("3442193e")) - XCTAssert(nextNodeHardened.publicKey.toHexString() == "035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56") + XCTAssert(nextNodeHardened.publicKey.hex == "035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56") XCTAssert(nextNodeHardened.serializeToString() == "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw") XCTAssert(nextNodeHardened.serializeToString(serializePublic: false) == "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7") diff --git a/Tests/RLPTests.swift b/Tests/RLPTests.swift index 52ee26e..b7ff541 100644 --- a/Tests/RLPTests.swift +++ b/Tests/RLPTests.swift @@ -8,9 +8,36 @@ import BigInt import XCTest - @testable import web3swift + class RLPTests: XCTestCase { + func testNewRlp() throws { + let data = try SolidityFunction(function: "doSome(uint256,uint256)").encode(0x123,0x456) + + let address: Address = "0x6a6a0b4aaa60E97386F94c5414522159b45DEdE8" + var transaction = EthereumTransaction(gasPrice: 0x12345, gasLimit: 0x123123, to: address, value: 0, data: data) + transaction.chainID = .mainnet + + let keystore = try! EthereumKeystoreV3(password: "")! + let privateKey = try! keystore.UNSAFE_getPrivateKeyData(password: "", account: keystore.address!) + + let transaction2 = Transaction(gasPrice: 0x12345, gasLimit: 0x123123, to: address, value: 0, data: data) + let dataWriter = TransactionDataWriter() + transaction2.write(to: dataWriter) + transaction2.write(networkId: .mainnet, to: dataWriter) + + + let unsigned1 = transaction.encode(forSignature: false, chainId: .mainnet)!.hex + let unsigned2 = dataWriter.done().hex + XCTAssertEqual(unsigned1, unsigned2) + + try! Web3Signer.signTX(transaction: &transaction, keystore: keystore, account: keystore.address!, password: "") + + let signed1 = transaction.encode(forSignature: false, chainId: .mainnet)!.hex + let signed2 = try! transaction2.sign(using: PrivateKey(privateKey), networkId: .mainnet).data().hex + XCTAssertEqual(signed1, signed2) + } + func testRLPencodeShortString() { let testString = "dog" let encoded = RLP.encode(testString) diff --git a/Tests/RemoteParsingTests.swift b/Tests/RemoteParsingTests.swift index 496cdb8..e8d7d5c 100644 --- a/Tests/RemoteParsingTests.swift +++ b/Tests/RemoteParsingTests.swift @@ -28,7 +28,7 @@ class RemoteParsingTests: XCTestCase { XCTAssert(decoded["_from"] as! Address == "0xdbf493e8d7db835192c02b992bd1ab72e96fd2e3") XCTAssert(decoded["_value"] as! BigUInt == BigUInt("3946fe37ffce3a0000", radix: 16)!) XCTAssert(pres[0].contractAddress == "0x45245bc59219eeaaf6cd3f382e078a461ff9de7b") - XCTAssert(pres[0].transactionReceipt!.transactionHash.toHexString().withHex == "0xcb235e8c6ecda032bc82c1084d2159ab82e7e4de35be703da6e80034bc577673") + XCTAssert(pres[0].transactionReceipt!.transactionHash.hex.withHex == "0xcb235e8c6ecda032bc82c1084d2159ab82e7e4de35be703da6e80034bc577673") } func testEventParsing2usingABIv2() throws { @@ -52,7 +52,7 @@ class RemoteParsingTests: XCTestCase { for p in present { print("Block " + String(i) + "\n") print("Emitted by contract " + p.contractAddress.address + "\n") - print("TX hash " + p.transactionReceipt!.transactionHash.toHexString().withHex + "\n") + print("TX hash " + p.transactionReceipt!.transactionHash.hex.withHex + "\n") print("From " + (p.decodedResult["_from"] as! Address).address + "\n") print("From " + (p.decodedResult["_to"] as! Address).address + "\n") print("Value " + String(p.decodedResult["_value"] as! BigUInt) + "\n") diff --git a/Tests/RinkebyPersonalSignatureTests.swift b/Tests/RinkebyPersonalSignatureTests.swift index 926f09a..caec04c 100644 --- a/Tests/RinkebyPersonalSignatureTests.swift +++ b/Tests/RinkebyPersonalSignatureTests.swift @@ -24,9 +24,9 @@ class RinkebyPersonalSignatureTests: XCTestCase { let signature = try web3.personal.signPersonalMessage(message: message, from: expectedAddress, password: "") let unmarshalledSignature = try SECP256K1.unmarshalSignature(signatureData: signature) print("V = " + String(unmarshalledSignature.v)) - print("R = " + Data(unmarshalledSignature.r).toHexString()) - print("S = " + Data(unmarshalledSignature.s).toHexString()) - try! print("Personal hash = " + Web3Utils.hashPersonalMessage(message).toHexString()) + print("R = " + Data(unmarshalledSignature.r).hex) + print("S = " + Data(unmarshalledSignature.s).hex) + try! print("Personal hash = " + Web3Utils.hashPersonalMessage(message).hex) let signer = try web3.personal.ecrecover(personalMessage: message, signature: signature) XCTAssert(expectedAddress == signer, "Failed to sign personal message") } @@ -43,9 +43,9 @@ class RinkebyPersonalSignatureTests: XCTestCase { let signature = try web3.personal.signPersonalMessage(message: messageData, from: expectedAddress, password: "") let unmarshalledSignature = try SECP256K1.unmarshalSignature(signatureData: signature) print("V = " + String(unmarshalledSignature.v)) - print("R = " + Data(unmarshalledSignature.r).toHexString()) - print("S = " + Data(unmarshalledSignature.s).toHexString()) - try print("Personal hash = " + Web3Utils.hashPersonalMessage(messageData).toHexString()) + print("R = " + Data(unmarshalledSignature.r).hex) + print("S = " + Data(unmarshalledSignature.s).hex) + try print("Personal hash = " + Web3Utils.hashPersonalMessage(messageData).hex) let jsonString = "[{\"inputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"constant\":true,\"inputs\":[{\"name\":\"_message\",\"type\":\"string\"}],\"name\":\"hashPersonalMessage\",\"outputs\":[{\"name\":\"hash\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_message\",\"type\":\"string\"},{\"name\":\"v\",\"type\":\"uint8\"},{\"name\":\"r\",\"type\":\"bytes32\"},{\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"recoverSigner\",\"outputs\":[{\"name\":\"signer\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"pure\",\"type\":\"function\"}]" let contract = try web3.contract(jsonString, at: "0x6f1745a39059268e8e4572e97897b50e4aab62a8") var options = Web3Options.default diff --git a/Tests/SECP256K1Tests.swift b/Tests/SECP256K1Tests.swift index 7f7e017..e695287 100644 --- a/Tests/SECP256K1Tests.swift +++ b/Tests/SECP256K1Tests.swift @@ -6,10 +6,7 @@ // Copyright © 2018 Bankex Foundation. All rights reserved. // -import XCTest - import BigInt -import CryptoSwift import XCTest @testable import web3swift diff --git a/Tests/ScryptTests.swift b/Tests/ScryptTests.swift index 12e4053..480291b 100644 --- a/Tests/ScryptTests.swift +++ b/Tests/ScryptTests.swift @@ -6,7 +6,6 @@ // Copyright © 2018 Bankex Foundation. All rights reserved. // -import CryptoSwift import Foundation import XCTest diff --git a/Tests/TransactionTests.swift b/Tests/TransactionTests.swift index 6143a37..0de685c 100644 --- a/Tests/TransactionTests.swift +++ b/Tests/TransactionTests.swift @@ -31,7 +31,7 @@ class TransactionsTests: XCTestCase { print(transaction) let hash = transaction.hashForSignature(chainID: 1) let expectedHash = "0xdaf5a779ae972f972197303d7b574746c7ef83eadac0f2791ad23db92e4c8e53".withoutHex - XCTAssert(hash!.toHexString() == expectedHash, "Transaction signature failed") + XCTAssert(hash!.hex == expectedHash, "Transaction signature failed") try Web3Signer.EIP155Signer.sign(transaction: &transaction, privateKey: privateKeyData, useExtraEntropy: false) print(transaction) XCTAssert(transaction.v == 37, "Transaction signature failed") diff --git a/web3swift.podspec b/web3swift.podspec index 51cf47a..8602a01 100644 --- a/web3swift.podspec +++ b/web3swift.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |spec| spec.name = 'web3swift' - spec.version = '2.0.9' + spec.version = '2.1' spec.ios.deployment_target = "8.0" spec.osx.deployment_target = "10.10" spec.tvos.deployment_target = "9.0" @@ -15,4 +15,5 @@ Pod::Spec.new do |spec| spec.dependency 'BigInt', '~> 3.1' spec.dependency 'CryptoSwift', '~> 0.12' spec.dependency 'secp256k1.swift' + spec.dependency 'BlueCryptor', :git => 'https://github.com/v57/BlueCryptor.git' end diff --git a/web3swift.xcodeproj/project.pbxproj b/web3swift.xcodeproj/project.pbxproj index adc9995..9ed4bae 100644 --- a/web3swift.xcodeproj/project.pbxproj +++ b/web3swift.xcodeproj/project.pbxproj @@ -119,8 +119,10 @@ 13975204219AFA6D0044D2B0 /* ERC20Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1317BDE7218C526C00D6D095 /* ERC20Tests.swift */; }; 13975205219AFA6D0044D2B0 /* TransactionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1317BDE9218C526C00D6D095 /* TransactionTests.swift */; }; 13975206219AFA6D0044D2B0 /* SECP256K1Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1317BDEA218C526C00D6D095 /* SECP256K1Tests.swift */; }; + 139947F221B0586200EA5DB6 /* PrivateKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 139947F121B0586200EA5DB6 /* PrivateKey.swift */; }; + 13A93F5A21B0A36900F115E4 /* AES.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13A93F5921B0A36900F115E4 /* AES.swift */; }; + 13A93F6021B172D900F115E4 /* Transaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13A93F5F21B172D900F115E4 /* Transaction.swift */; }; 13AC5D3E21A5D109009D0309 /* SolidityDataReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13AC5D3D21A5D109009D0309 /* SolidityDataReader.swift */; }; - 13BBADB121AD38DA0017F55F /* Accounts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13BBADB021AD38DA0017F55F /* Accounts.swift */; }; 13D2576E21ADCD4400D382D1 /* DerivedKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13D2576D21ADCD4400D382D1 /* DerivedKey.swift */; }; /* End PBXBuildFile section */ @@ -246,8 +248,10 @@ 13765D6921964A9B005C483C /* W3Transaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = W3Transaction.swift; sourceTree = ""; }; 139751B7219AF76D0044D2B0 /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 139751B9219AF76D0044D2B0 /* Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests.swift; sourceTree = ""; }; + 139947F121B0586200EA5DB6 /* PrivateKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivateKey.swift; sourceTree = ""; }; + 13A93F5921B0A36900F115E4 /* AES.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AES.swift; sourceTree = ""; }; + 13A93F5F21B172D900F115E4 /* Transaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Transaction.swift; sourceTree = ""; }; 13AC5D3D21A5D109009D0309 /* SolidityDataReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SolidityDataReader.swift; sourceTree = ""; }; - 13BBADB021AD38DA0017F55F /* Accounts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Accounts.swift; sourceTree = ""; }; 13D2576D21ADCD4400D382D1 /* DerivedKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DerivedKey.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -298,8 +302,8 @@ children = ( 133E673A219EFE630054758F /* Guides.swift */, 1317BD58218C526300D6D095 /* Migration.swift */, + 13A93F5D21B172A800F115E4 /* Transactions */, 133E696521A6B8940054758F /* Migration-iOS.swift */, - 13BBADAF21AD38C20017F55F /* Keystore */, 13D2577021ADEE3D00D382D1 /* Encryption */, 133970582199D22A0027E27C /* Deprecated */, 1317BCFE218C526300D6D095 /* Transaction */, @@ -413,10 +417,6 @@ 1317BD41218C526300D6D095 /* ABIv2Decoding.swift */, 1317BD42218C526300D6D095 /* ABIv2ParameterTypes.swift */, 1317BD44218C526300D6D095 /* ABIv2Encoding.swift */, - 1317BD43218C526300D6D095 /* SolidityFunction.swift */, - 13AC5D3D21A5D109009D0309 /* SolidityDataReader.swift */, - 1317BD45218C526300D6D095 /* SolidityDataWriter.swift */, - 1317BD46218C526300D6D095 /* SolidityTypes.swift */, ); path = ABIv2; sourceTree = ""; @@ -444,9 +444,9 @@ children = ( 1317BD5A218C526300D6D095 /* RIPEMD160+StackOveflow.swift */, 1317BD5B218C526300D6D095 /* UInt256.swift */, - 1317BD5C218C526300D6D095 /* CryptoExtensions.swift */, 1317BD5D218C526300D6D095 /* String+Extension.swift */, 1317BD5E218C526300D6D095 /* LibSecp256k1Extension.swift */, + 1317BD5C218C526300D6D095 /* CryptoExtensions.swift */, 1317BD5F218C526300D6D095 /* NSRegularExpressionExtension.swift */, 1317BD61218C526300D6D095 /* Data+Extension.swift */, 1317BD62218C526300D6D095 /* NativeTypesEncoding+Extensions.swift */, @@ -527,18 +527,24 @@ path = Deprecated; sourceTree = ""; }; - 13BBADAF21AD38C20017F55F /* Keystore */ = { + 13A93F5D21B172A800F115E4 /* Transactions */ = { isa = PBXGroup; children = ( - 13BBADB021AD38DA0017F55F /* Accounts.swift */, + 1317BD43218C526300D6D095 /* SolidityFunction.swift */, + 13AC5D3D21A5D109009D0309 /* SolidityDataReader.swift */, + 1317BD45218C526300D6D095 /* SolidityDataWriter.swift */, + 1317BD46218C526300D6D095 /* SolidityTypes.swift */, + 13A93F5F21B172D900F115E4 /* Transaction.swift */, ); - path = Keystore; + path = Transactions; sourceTree = ""; }; 13D2577021ADEE3D00D382D1 /* Encryption */ = { isa = PBXGroup; children = ( + 13A93F5921B0A36900F115E4 /* AES.swift */, 13D2576D21ADCD4400D382D1 /* DerivedKey.swift */, + 139947F121B0586200EA5DB6 /* PrivateKey.swift */, ); path = Encryption; sourceTree = ""; @@ -691,6 +697,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 13A93F6021B172D900F115E4 /* Transaction.swift in Sources */, 133E696621A6B8940054758F /* Migration-iOS.swift in Sources */, 1317BDBF218C526300D6D095 /* UInt256.swift in Sources */, 13765D6F21964A9B005C483C /* W3Web3.swift in Sources */, @@ -738,8 +745,8 @@ 1317BDA7218C526300D6D095 /* ABIv2TypeParser.swift in Sources */, 1317BDAF218C526300D6D095 /* BlockExplorer.swift in Sources */, 1317BDAC218C526300D6D095 /* SolidityDataWriter.swift in Sources */, - 13BBADB121AD38DA0017F55F /* Accounts.swift in Sources */, 1317BD83218C526300D6D095 /* Web3+Protocols.swift in Sources */, + 13A93F5A21B0A36900F115E4 /* AES.swift in Sources */, 1317BD85218C526300D6D095 /* Web3+Eth.swift in Sources */, 1317BD70218C526300D6D095 /* TransactionSigner.swift in Sources */, 13397055219984480027E27C /* SecurityToken.swift in Sources */, @@ -770,6 +777,7 @@ 1317BDBE218C526300D6D095 /* RIPEMD160+StackOveflow.swift in Sources */, 1317BDC2218C526300D6D095 /* LibSecp256k1Extension.swift in Sources */, 1317BD71218C526300D6D095 /* EthereumTransaction.swift in Sources */, + 139947F221B0586200EA5DB6 /* PrivateKey.swift in Sources */, 1317BD8B218C526300D6D095 /* EIP67Code.swift in Sources */, 1317BD88218C526300D6D095 /* Web3+Structures.swift in Sources */, 1317BD81218C526300D6D095 /* Web3+Contract.swift in Sources */, @@ -875,6 +883,7 @@ MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; + SUPPORTED_PLATFORMS = "macosx iphonesimulator iphoneos appletvsimulator appletvos watchos watchsimulator"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 4.2; @@ -935,6 +944,7 @@ MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = macosx; + SUPPORTED_PLATFORMS = "macosx iphonesimulator iphoneos appletvsimulator appletvos watchos watchsimulator"; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 4.2;