From db0ac33c3d04c27aa456d1b03b7fce0c7776bad8 Mon Sep 17 00:00:00 2001 From: "pharms.eth" <100330083+pharms-eth@users.noreply.github.com> Date: Fri, 11 Nov 2022 12:44:46 -0800 Subject: [PATCH 01/18] bug fix --- Sources/Core/EthereumABI/ABIElements.swift | 2 +- Sources/web3swift/Operations/ReadTransaction.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Core/EthereumABI/ABIElements.swift b/Sources/Core/EthereumABI/ABIElements.swift index 2e6b5d94f..be9bfa685 100755 --- a/Sources/Core/EthereumABI/ABIElements.swift +++ b/Sources/Core/EthereumABI/ABIElements.swift @@ -325,7 +325,7 @@ extension ABI.Element.Function { // set a flag to detect the request succeeded } - if returnArray.isEmpty { + if returnArray.isEmpty && !outputs.isEmpty && data.isEmpty{ return nil } diff --git a/Sources/web3swift/Operations/ReadTransaction.swift b/Sources/web3swift/Operations/ReadTransaction.swift index 5c721db19..a5b38f436 100755 --- a/Sources/web3swift/Operations/ReadTransaction.swift +++ b/Sources/web3swift/Operations/ReadTransaction.swift @@ -34,7 +34,7 @@ public class ReadOperation { // TODO: Remove type erasing here, some broad wide protocol should be added instead public func callContractMethod() async throws -> [String: Any] { - await transaction.resolve(provider: web3.provider) + try await transaction.resolve(provider: web3.provider) // MARK: Read data from ABI flow // FIXME: This should be dropped, and after `execute()` call, just to decode raw data. let data: Data = try await self.web3.eth.callTransaction(transaction) From d9fd03c95c405ef4b398600603556149930ae8d2 Mon Sep 17 00:00:00 2001 From: "pharms.eth" <100330083+pharms-eth@users.noreply.github.com> Date: Fri, 11 Nov 2022 12:46:45 -0800 Subject: [PATCH 02/18] cleanup --- .../APIRequest+ComputedProperties.swift | 6 ++-- .../KeystoreManager/KeystoreManager.swift | 8 +++-- .../Core/Utility/Encodable+Extensions.swift | 2 ++ .../Ethereum/Eth+GetBlockByHash.swift | 1 - Sources/web3swift/Utils/ENS/ENSResolver.swift | 31 +++++-------------- Sources/web3swift/Web3/Web3+Contract.swift | 2 +- Sources/web3swift/Web3/Web3.swift | 4 +-- 7 files changed, 19 insertions(+), 35 deletions(-) diff --git a/Sources/Core/EthereumNetwork/Request/APIRequest+ComputedProperties.swift b/Sources/Core/EthereumNetwork/Request/APIRequest+ComputedProperties.swift index 9b6fabf23..1b53a2b8e 100644 --- a/Sources/Core/EthereumNetwork/Request/APIRequest+ComputedProperties.swift +++ b/Sources/Core/EthereumNetwork/Request/APIRequest+ComputedProperties.swift @@ -9,13 +9,11 @@ import Foundation extension APIRequest { var method: REST { - switch self { - default: return .POST - } + .POST } public var encodedBody: Data { - let request = RequestBody(method: self.call, params: self.parameters) + let request = RequestBody(method: call, params: parameters) // this is safe to force try this here // Because request must failed to compile if it not conformable with `Encodable` protocol return try! JSONEncoder().encode(request) diff --git a/Sources/Core/KeystoreManager/KeystoreManager.swift b/Sources/Core/KeystoreManager/KeystoreManager.swift index 8d6a09fc5..78ec4a608 100755 --- a/Sources/Core/KeystoreManager/KeystoreManager.swift +++ b/Sources/Core/KeystoreManager/KeystoreManager.swift @@ -42,9 +42,11 @@ public class KeystoreManager: AbstractKeystore { } public func UNSAFE_getPrivateKeyData(password: String, account: EthereumAddress) throws -> Data { - guard let keystore = self.walletForAddress(account) else {throw AbstractKeystoreError.invalidAccountError} - return try keystore.UNSAFE_getPrivateKeyData(password: password, account: account) - } + guard let keystore = walletForAddress(account) else { + throw AbstractKeystoreError.invalidAccountError + } + return try keystore.UNSAFE_getPrivateKeyData(password: password, account: account) + } public static var allManagers = [KeystoreManager]() public static var defaultManager: KeystoreManager? { diff --git a/Sources/Core/Utility/Encodable+Extensions.swift b/Sources/Core/Utility/Encodable+Extensions.swift index 96167e516..e4eb88eb5 100644 --- a/Sources/Core/Utility/Encodable+Extensions.swift +++ b/Sources/Core/Utility/Encodable+Extensions.swift @@ -64,3 +64,5 @@ public extension EncodableToHex where Self: BinaryInteger { extension BigUInt: EncodableToHex { } extension UInt: EncodableToHex { } + +extension Int: EncodableToHex { } diff --git a/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+GetBlockByHash.swift b/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+GetBlockByHash.swift index ac1453ef7..00fb0edb8 100755 --- a/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+GetBlockByHash.swift +++ b/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+GetBlockByHash.swift @@ -10,7 +10,6 @@ import Core extension Web3.Eth { public func block(by hash: Data, fullTransactions: Bool = false) async throws -> Block { - guard let hexString = String(data: hash, encoding: .utf8)?.addHexPrefix() else { throw Web3Error.dataError } let requestCall: APIRequest = .getBlockByHash(hash.toHexString().addHexPrefix(), fullTransactions) return try await APIRequest.sendRequest(with: self.provider, for: requestCall).result } diff --git a/Sources/web3swift/Utils/ENS/ENSResolver.swift b/Sources/web3swift/Utils/ENS/ENSResolver.swift index 0830d0bcf..4fb34eb0a 100755 --- a/Sources/web3swift/Utils/ENS/ENSResolver.swift +++ b/Sources/web3swift/Utils/ENS/ENSResolver.swift @@ -19,30 +19,13 @@ public extension ENS { case URI = 8 } - public enum InterfaceName { - case addr - case name - case content - case ABI - case pubkey - case text - - func hash() -> String { - switch self { - case .addr: - return "0x3b3b57de" - case .name: - return "0x691f3431" - case .content: - return "0xbc1c58d1" - case .ABI: - return "0x2203ab56" - case .pubkey: - return "0xc8690233" - case .text: - return "0x59d1d43c" - } - } + public enum InterfaceName: String { + case addr = "0x3b3b57de" + case name = "0x691f3431" + case content = "0xbc1c58d1" + case ABI = "0x2203ab56" + case pubkey = "0xc8690233" + case text = "0x59d1d43c" } lazy var resolverContract: Web3.Contract = { diff --git a/Sources/web3swift/Web3/Web3+Contract.swift b/Sources/web3swift/Web3/Web3+Contract.swift index dc2d135f0..abefbea29 100755 --- a/Sources/web3swift/Web3/Web3+Contract.swift +++ b/Sources/web3swift/Web3/Web3+Contract.swift @@ -105,7 +105,7 @@ extension Web3 { /// /// Returns a "Transaction intermediate" object. public func createWriteOperation(_ method: String = "fallback", parameters: [AnyObject] = [AnyObject](), extraData: Data = Data()) -> WriteOperation? { - guard var data = self.contract.method(method, parameters: parameters, extraData: extraData) else {return nil} + guard let data = self.contract.method(method, parameters: parameters, extraData: extraData) else {return nil} transaction.data = data if let network = self.web3.provider.network { transaction.chainID = network.chainID diff --git a/Sources/web3swift/Web3/Web3.swift b/Sources/web3swift/Web3/Web3.swift index ab2731a6e..cd09e2a37 100755 --- a/Sources/web3swift/Web3/Web3.swift +++ b/Sources/web3swift/Web3/Web3.swift @@ -12,9 +12,9 @@ extension Web3 { /// Initialized provider-bound Web3 instance using a provider's URL. Under the hood it performs a synchronous call to get /// the Network ID for EIP155 purposes - public static func new(_ providerURL: URL) async throws -> Web3 { + public static func new(_ providerURL: URL, network net: Networks = .Mainnet) async throws -> Web3 { // FIXME: Change this hardcoded value to dynamicly fethed from a Node - guard let provider = await Web3HttpProvider(providerURL, network: .Mainnet) else { + guard let provider = await Web3HttpProvider(providerURL, network: net) else { throw Web3Error.inputError(desc: "Wrong provider - should be Web3HttpProvider with endpoint scheme http or https") } return Web3(provider: provider) From bcebcb05c05ff9c61f82e66d83667c9e5ea62909 Mon Sep 17 00:00:00 2001 From: "pharms.eth" <100330083+pharms-eth@users.noreply.github.com> Date: Fri, 11 Nov 2022 12:55:01 -0800 Subject: [PATCH 03/18] cleanup --- .../Core/Transaction/CodableTransaction.swift | 18 ++++++------------ .../Transaction/Envelope/EIP1559Envelope.swift | 6 ++++-- .../Transaction/Envelope/EIP2930Envelope.swift | 6 ++++-- .../Transaction/Envelope/LegacyEnvelope.swift | 3 ++- Sources/Core/Utility/Utilities.swift | 17 ++++++++--------- 5 files changed, 24 insertions(+), 26 deletions(-) diff --git a/Sources/Core/Transaction/CodableTransaction.swift b/Sources/Core/Transaction/CodableTransaction.swift index 288a82d5d..448cfcaeb 100644 --- a/Sources/Core/Transaction/CodableTransaction.swift +++ b/Sources/Core/Transaction/CodableTransaction.swift @@ -188,18 +188,18 @@ public struct CodableTransaction { return self.envelope.encode(for: type) } - public mutating func resolve(provider: Web3Provider) async { + public mutating func resolve(provider: Web3Provider) async throws { // FIXME: Delete force try - self.gasLimit = try! await self.gasLimitPolicy.resolve(provider: provider, transaction: self) + self.gasLimit = try await self.gasLimitPolicy.resolve(provider: provider, transaction: self) if from != nil || sender != nil { - self.nonce = try! await self.resolveNonce(provider: provider) + self.nonce = try await self.resolveNonce(provider: provider) } if case .eip1559 = type { - self.maxFeePerGas = try! await self.maxFeePerGasPolicy.resolve(provider: provider) - self.maxPriorityFeePerGas = try! await self.maxPriorityFeePerGasPolicy.resolve(provider: provider) + self.maxFeePerGas = try await self.maxFeePerGasPolicy.resolve(provider: provider) + self.maxPriorityFeePerGas = try await self.maxPriorityFeePerGasPolicy.resolve(provider: provider) } else { - self.gasPrice = try! await self.gasPricePolicy.resolve(provider: provider) + self.gasPrice = try await self.gasPricePolicy.resolve(provider: provider) } } @@ -380,12 +380,6 @@ extension CodableTransaction { return value } } - - - - - - } diff --git a/Sources/Core/Transaction/Envelope/EIP1559Envelope.swift b/Sources/Core/Transaction/Envelope/EIP1559Envelope.swift index 2da61bf59..705d27492 100644 --- a/Sources/Core/Transaction/Envelope/EIP1559Envelope.swift +++ b/Sources/Core/Transaction/Envelope/EIP1559Envelope.swift @@ -249,8 +249,10 @@ extension EIP1559Envelope { let list = accessList.map { $0.encodeAsList() as AnyObject } switch type { - case .transaction: fields = [chainID, nonce, maxPriorityFeePerGas, maxFeePerGas, gasLimit, to.addressData, value, data, list, v, r, s] as [AnyObject] - case .signature: fields = [chainID, nonce, maxPriorityFeePerGas, maxFeePerGas, gasLimit, to.addressData, value, data, list] as [AnyObject] + case .transaction: + fields = [chainID, nonce, maxPriorityFeePerGas, maxFeePerGas, gasLimit, to.addressData, value, data, list, v, r, s] as [AnyObject] + case .signature: + fields = [chainID, nonce, maxPriorityFeePerGas, maxFeePerGas, gasLimit, to.addressData, value, data, list] as [AnyObject] } guard var result = RLP.encode(fields) else { return nil } result.insert(UInt8(self.type.rawValue), at: 0) diff --git a/Sources/Core/Transaction/Envelope/EIP2930Envelope.swift b/Sources/Core/Transaction/Envelope/EIP2930Envelope.swift index e58d3d176..191c4498d 100644 --- a/Sources/Core/Transaction/Envelope/EIP2930Envelope.swift +++ b/Sources/Core/Transaction/Envelope/EIP2930Envelope.swift @@ -210,8 +210,10 @@ extension EIP2930Envelope { let list = accessList.map { $0.encodeAsList() as AnyObject } switch type { - case .transaction: fields = [chainID, nonce, gasPrice, gasLimit, to.addressData, value, data, list, v, r, s] as [AnyObject] - case .signature: fields = [chainID, nonce, gasPrice, gasLimit, to.addressData, value, data, list] as [AnyObject] + case .transaction: + fields = [chainID, nonce, gasPrice, gasLimit, to.addressData, value, data, list, v, r, s] as [AnyObject] + case .signature: + fields = [chainID, nonce, gasPrice, gasLimit, to.addressData, value, data, list] as [AnyObject] } guard var result = RLP.encode(fields) else { return nil } result.insert(UInt8(self.type.rawValue), at: 0) diff --git a/Sources/Core/Transaction/Envelope/LegacyEnvelope.swift b/Sources/Core/Transaction/Envelope/LegacyEnvelope.swift index 40c133ada..6ef5351b5 100644 --- a/Sources/Core/Transaction/Envelope/LegacyEnvelope.swift +++ b/Sources/Core/Transaction/Envelope/LegacyEnvelope.swift @@ -194,7 +194,8 @@ extension LegacyEnvelope { public func encode(for type: EncodeType = .transaction) -> Data? { let fields: [AnyObject] switch type { - case .transaction: fields = [self.nonce, self.gasPrice, self.gasLimit, self.to.addressData, self.value, self.data, v, r, s] as [AnyObject] + case .transaction: + fields = [self.nonce, self.gasPrice, self.gasLimit, self.to.addressData, self.value, self.data, v, r, s] as [AnyObject] case .signature: if let chainID = self.chainID, chainID != 0 { fields = [self.nonce, self.gasPrice, self.gasLimit, self.to.addressData, self.value, self.data, chainID, BigUInt(0), BigUInt(0)] as [AnyObject] diff --git a/Sources/Core/Utility/Utilities.swift b/Sources/Core/Utility/Utilities.swift index 218db9027..8525baef6 100644 --- a/Sources/Core/Utility/Utilities.swift +++ b/Sources/Core/Utility/Utilities.swift @@ -162,19 +162,18 @@ public struct Utilities { if bigNumber == 0 { return "0" } - let unitDecimals = numberDecimals var toDecimals = formattingDecimals - if unitDecimals < toDecimals { - toDecimals = unitDecimals + if numberDecimals < toDecimals { + toDecimals = numberDecimals } - let divisor = BigUInt(10).power(unitDecimals) + let divisor = BigUInt(10).power(numberDecimals) let (quotient, remainder) = bigNumber.quotientAndRemainder(dividingBy: divisor) - var fullRemainder = String(remainder) - let fullPaddedRemainder = fullRemainder.leftPadding(toLength: unitDecimals, withPad: "0") + var fullRemainder = "\(remainder)" + let fullPaddedRemainder = fullRemainder.leftPadding(toLength: numberDecimals, withPad: "0") let remainderPadded = fullPaddedRemainder[0.. Date: Sun, 13 Nov 2022 13:24:10 -0800 Subject: [PATCH 04/18] fix bug --- .../EthereumAPICalls/Ethereum/Eth+SendRawTransaction.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+SendRawTransaction.swift b/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+SendRawTransaction.swift index 250d4be9e..29011c66f 100755 --- a/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+SendRawTransaction.swift +++ b/Sources/web3swift/EthereumAPICalls/Ethereum/Eth+SendRawTransaction.swift @@ -9,7 +9,8 @@ import Core extension Web3.Eth { public func send(raw data: Data) async throws -> TransactionSendingResult { - guard let hexString = String(data: data, encoding: .utf8)?.addHexPrefix() else { throw Web3Error.dataError } + let hexString = data.toHexString().addHexPrefix() + let request: APIRequest = .sendRawTransaction(hexString) let response: APIResponse = try await APIRequest.sendRequest(with: self.provider, for: request) return try TransactionSendingResult(data: data, hash: response.result) From 915bcd91e1046ebe6ecbbe74caef0de1acf84dda Mon Sep 17 00:00:00 2001 From: "pharms.eth" <100330083+pharms-eth@users.noreply.github.com> Date: Tue, 15 Nov 2022 17:57:32 -0800 Subject: [PATCH 05/18] Mnemonic Wallet Creation Cleanup --- .../Core/KeystoreManager/BIP32HDNode.swift | 228 +++++++++--------- .../Core/KeystoreManager/BIP32Keystore.swift | 78 +++--- Sources/Core/KeystoreManager/BIP39.swift | 99 +++++--- .../KeystoreManager/EthereumKeystoreV3.swift | 15 +- .../Core/KeystoreManager/KeystoreParams.swift | 33 ++- Sources/Core/Utility/Data+Extension.swift | 31 +-- Sources/Core/Utility/String+Extension.swift | 22 +- .../web3swift/Operations/WriteOperation.swift | 30 ++- Sources/web3swift/Utils/ENS/ENS.swift | 34 +-- 9 files changed, 346 insertions(+), 224 deletions(-) diff --git a/Sources/Core/KeystoreManager/BIP32HDNode.swift b/Sources/Core/KeystoreManager/BIP32HDNode.swift index e442e90f9..1eba04a85 100755 --- a/Sources/Core/KeystoreManager/BIP32HDNode.swift +++ b/Sources/Core/KeystoreManager/BIP32HDNode.swift @@ -1,3 +1,4 @@ +// web3swift // // Created by Alex Vlasov. // Copyright © 2018 Alex Vlasov. All rights reserved. @@ -23,12 +24,11 @@ extension UInt32 { } public class HDNode { - public struct HDversion{ - public var privatePrefix: Data = Data.fromHex("0x0488ADE4")! - public var publicPrefix: Data = Data.fromHex("0x0488B21E")! - public init() { - } + private struct HDversion{ + public static var privatePrefix: Data? = Data.fromHex("0x0488ADE4") + public static var publicPrefix: Data? = Data.fromHex("0x0488B21E") + } public var path: String? = "m" public var privateKey: Data? = nil @@ -51,11 +51,6 @@ public class HDNode { } } } - public var hasPrivate: Bool { - get { - return privateKey != nil - } - } init() { publicKey = Data() @@ -72,7 +67,7 @@ public class HDNode { guard data.count == 82 else {return nil} let header = data[0..<4] var serializePrivate = false - if header == HDNode.HDversion().privatePrefix { + if header == HDversion.privatePrefix { serializePrivate = true } depth = data[4..<5].bytes[0] @@ -94,8 +89,10 @@ public class HDNode { public init?(seed: Data) { guard seed.count >= 16 else {return nil} - let hmacKey = "Bitcoin seed".data(using: .ascii)! - let hmac: Authenticator = HMAC(key: hmacKey.bytes, variant: HMAC.Variant.sha2(.sha512)) + + guard let hmacKey = "Bitcoin seed".data(using: .ascii) else {return nil} + let hmac:Authenticator = HMAC(key: hmacKey.bytes, variant: HMAC.Variant.sha2(.sha512)) + guard let entropy = try? hmac.authenticate(seed.bytes) else {return nil} guard entropy.count == 64 else { return nil} let I_L = entropy[0..<32] @@ -104,7 +101,7 @@ public class HDNode { let privKeyCandidate = Data(I_L) guard SECP256K1.verifyPrivateKey(privateKey: privKeyCandidate) else {return nil} guard let pubKeyCandidate = SECP256K1.privateToPublic(privateKey: privKeyCandidate, compressed: true) else {return nil} - guard pubKeyCandidate.bytes[0] == 0x02 || pubKeyCandidate.bytes[0] == 0x03 else {return nil} + guard pubKeyCandidate.bytes.first == 0x02 || pubKeyCandidate.bytes.first == 0x03 else {return nil} publicKey = pubKeyCandidate privateKey = privKeyCandidate depth = 0x00 @@ -120,91 +117,99 @@ public class HDNode { } extension HDNode { - public func derive (index: UInt32, derivePrivateKey: Bool, hardened: Bool = false) -> HDNode? { - if derivePrivateKey { - if self.hasPrivate { // derive private key when is itself extended private key - var entropy: Array - var trueIndex: UInt32 - if index >= (UInt32(1) << 31) || hardened { - trueIndex = index - if trueIndex < (UInt32(1) << 31) { - trueIndex = trueIndex + (UInt32(1) << 31) - } - let hmac: Authenticator = HMAC(key: self.chaincode.bytes, variant: .sha2(.sha512)) - var inputForHMAC = Data() - inputForHMAC.append(Data([UInt8(0x00)])) - inputForHMAC.append(self.privateKey!) - inputForHMAC.append(trueIndex.serialize32()) - guard let ent = try? hmac.authenticate(inputForHMAC.bytes) else {return nil } - guard ent.count == 64 else { return nil } - entropy = ent - } else { - trueIndex = index - let hmac: Authenticator = HMAC(key: self.chaincode.bytes, variant: .sha2(.sha512)) - var inputForHMAC = Data() - inputForHMAC.append(self.publicKey) - inputForHMAC.append(trueIndex.serialize32()) - guard let ent = try? hmac.authenticate(inputForHMAC.bytes) else {return nil } - guard ent.count == 64 else { return nil } - entropy = ent - } - let I_L = entropy[0..<32] - let I_R = entropy[32..<64] - let cc = Data(I_R) - let bn = BigUInt(Data(I_L)) - if bn > HDNode.curveOrder { - if trueIndex < UInt32.max { - return self.derive(index: index+1, derivePrivateKey: derivePrivateKey, hardened: hardened) - } - return nil - } - let newPK = (bn + BigUInt(self.privateKey!)) % HDNode.curveOrder - if newPK == BigUInt(0) { - if trueIndex < UInt32.max { - return self.derive(index: index+1, derivePrivateKey: derivePrivateKey, hardened: hardened) - } - return nil - } - guard let privKeyCandidate = newPK.serialize().setLengthLeft(32) else {return nil} - guard SECP256K1.verifyPrivateKey(privateKey: privKeyCandidate) else {return nil } - guard let pubKeyCandidate = SECP256K1.privateToPublic(privateKey: privKeyCandidate, compressed: true) else {return nil} - guard pubKeyCandidate.bytes[0] == 0x02 || pubKeyCandidate.bytes[0] == 0x03 else {return nil} - guard self.depth < UInt8.max else {return nil} - let newNode = HDNode() - newNode.chaincode = cc - newNode.depth = self.depth + 1 - newNode.publicKey = pubKeyCandidate - newNode.privateKey = privKeyCandidate - newNode.childNumber = trueIndex - guard let fprint = try? RIPEMD160.hash(message: self.publicKey.sha256())[0..<4] else { - return nil - } - newNode.parentFingerprint = fprint - var newPath = String() - if newNode.isHardened { - newPath = self.path! + "/" - newPath += String(newNode.index % HDNode.hardenedIndexPrefix) + "'" - } else { - newPath = self.path! + "/" + String(newNode.index) - } - newNode.path = newPath - return newNode - } else { - return nil // derive private key when is itself extended public key (impossible) + public func derive(index: UInt32, derivePrivateKey:Bool, hardened: Bool = false) -> HDNode? { + derivePrivateKey ? + deriveAlongPrivateKey(index: index, derivePrivateKey: derivePrivateKey, hardened: hardened) + : + deriveWithoutPrivateKey(index: index, derivePrivateKey: derivePrivateKey, hardened: hardened) + } + public func deriveAlongPrivateKey(index: UInt32, derivePrivateKey:Bool, hardened: Bool = false) -> HDNode? { + guard let privateKey = self.privateKey else { + return nil + }// derive private key when is itself extended private key + var entropy:Array + var trueIndex: UInt32 + if index >= (UInt32(1) << 31) || hardened { + trueIndex = index + if trueIndex < (UInt32(1) << 31) { + trueIndex = trueIndex + (UInt32(1) << 31) } - } else { // deriving only the public key - var entropy: Array // derive public key when is itself public key - if index >= (UInt32(1) << 31) || hardened { - return nil // no derivation of hardened public key from extended public key - } else { - let hmac: Authenticator = HMAC(key: self.chaincode.bytes, variant: .sha2(.sha512)) - var inputForHMAC = Data() - inputForHMAC.append(self.publicKey) - inputForHMAC.append(index.serialize32()) - guard let ent = try? hmac.authenticate(inputForHMAC.bytes) else {return nil } - guard ent.count == 64 else { return nil } - entropy = ent + let hmac:Authenticator = HMAC(key: self.chaincode.bytes, variant: .sha2(.sha512)) + var inputForHMAC = Data() + inputForHMAC.append(Data([UInt8(0x00)])) + inputForHMAC.append(privateKey) + inputForHMAC.append(trueIndex.serialize32()) + guard let ent = try? hmac.authenticate(inputForHMAC.bytes) else {return nil } + guard ent.count == 64 else { return nil } + entropy = ent + } else { + trueIndex = index + let hmac:Authenticator = HMAC(key: self.chaincode.bytes, variant: .sha2(.sha512)) + var inputForHMAC = Data() + inputForHMAC.append(self.publicKey) + inputForHMAC.append(trueIndex.serialize32()) + guard let ent = try? hmac.authenticate(inputForHMAC.bytes) else {return nil } + guard ent.count == 64 else { return nil } + entropy = ent + } + let I_L = entropy[0..<32] + let I_R = entropy[32..<64] + let cc = Data(I_R) + let bn = BigUInt(Data(I_L)) + if bn > HDNode.curveOrder { + if trueIndex < UInt32.max { + return self.derive(index:index+1, derivePrivateKey: derivePrivateKey, hardened:hardened) } + return nil + } + let newPK = (bn + BigUInt(privateKey)) % HDNode.curveOrder + if newPK == BigUInt(0) { + if trueIndex < UInt32.max { + return self.derive(index:index+1, derivePrivateKey: derivePrivateKey, hardened:hardened) + } + return nil + } + guard let privKeyCandidate = newPK.serialize().setLengthLeft(32) else {return nil} + guard SECP256K1.verifyPrivateKey(privateKey: privKeyCandidate) else {return nil } + guard let pubKeyCandidate = SECP256K1.privateToPublic(privateKey: privKeyCandidate, compressed: true) else {return nil} + guard pubKeyCandidate.bytes.first == 0x02 || pubKeyCandidate.bytes.first == 0x03 else {return nil} + guard self.depth < UInt8.max else {return nil} + let newNode = HDNode() + newNode.chaincode = cc + newNode.depth = self.depth + 1 + newNode.publicKey = pubKeyCandidate + newNode.privateKey = privKeyCandidate + newNode.childNumber = trueIndex + guard let fprint = try? RIPEMD160.hash(message: self.publicKey.sha256())[0..<4] else { + return nil + } + newNode.parentFingerprint = fprint + var newPath = String() + if newNode.isHardened { + newPath = (self.path ?? "") + "/" + newPath += String(newNode.index % HDNode.hardenedIndexPrefix) + "'" + } else { + newPath = (self.path ?? "") + "/" + String(newNode.index) + } + newNode.path = newPath + return newNode + } + + public func deriveWithoutPrivateKey(index: UInt32, derivePrivateKey:Bool, hardened: Bool = false) -> HDNode? { + // deriving only the public key + var entropy:Array // derive public key when is itself public key + guard !hardened && index < (UInt32(1) << 31) else { + return nil // no derivation of hardened public key from extended public key + } + + let hmac:Authenticator = HMAC(key: self.chaincode.bytes, variant: .sha2(.sha512)) + var inputForHMAC = Data() + inputForHMAC.append(self.publicKey) + inputForHMAC.append(index.serialize32()) + guard let ent = try? hmac.authenticate(inputForHMAC.bytes) else {return nil } + guard ent.count == 64 else { return nil } + entropy = ent + let I_L = entropy[0..<32] let I_R = entropy[32..<64] let cc = Data(I_R) @@ -218,9 +223,9 @@ extension HDNode { guard let tempKey = bn.serialize().setLengthLeft(32) else {return nil} guard SECP256K1.verifyPrivateKey(privateKey: tempKey) else {return nil } guard let pubKeyCandidate = SECP256K1.privateToPublic(privateKey: tempKey, compressed: true) else {return nil} - guard pubKeyCandidate.bytes[0] == 0x02 || pubKeyCandidate.bytes[0] == 0x03 else {return nil} + guard pubKeyCandidate.bytes.first == 0x02 || pubKeyCandidate.bytes.first == 0x03 else {return nil} guard let newPublicKey = SECP256K1.combineSerializedPublicKeys(keys: [self.publicKey, pubKeyCandidate], outputCompressed: true) else {return nil} - guard newPublicKey.bytes[0] == 0x02 || newPublicKey.bytes[0] == 0x03 else {return nil} + guard newPublicKey.bytes.first == 0x02 || newPublicKey.bytes.first == 0x03 else {return nil} guard self.depth < UInt8.max else {return nil} let newNode = HDNode() newNode.chaincode = cc @@ -233,17 +238,17 @@ extension HDNode { newNode.parentFingerprint = fprint var newPath = String() if newNode.isHardened { - newPath = self.path! + "/" + newPath = (self.path ?? "") + "/" newPath += String(newNode.index % HDNode.hardenedIndexPrefix) + "'" } else { - newPath = self.path! + "/" + String(newNode.index) + newPath = (self.path ?? "") + "/" + String(newNode.index) } newNode.path = newPath return newNode - } } - public func derive (path: String, derivePrivateKey: Bool = true) -> HDNode? { + public func derive(path: String, derivePrivateKey: Bool = true) -> HDNode? { + let components = path.components(separatedBy: "/") var currentNode: HDNode = self var firstComponent = 0 @@ -262,19 +267,24 @@ extension HDNode { return currentNode } - public func serializeToString(serializePublic: Bool = true, version: HDversion = HDversion()) -> String? { - guard let data = self.serialize(serializePublic: serializePublic, version: version) else {return nil} + public func serializeToString(serializePublic: Bool = true) -> String? { + guard let data = self.serialize(serializePublic: serializePublic) else {return nil} let encoded = Base58.base58FromBytes(data.bytes) return encoded } - public func serialize(serializePublic: Bool = true, version: HDversion = HDversion()) -> Data? { + public func serialize(serializePublic: Bool = true) -> Data? { + var data = Data() - if (!serializePublic && !self.hasPrivate) {return nil} + + guard serializePublic || privateKey != nil else { + return nil + } + if serializePublic { - data.append(version.publicPrefix) + data.append(HDversion.publicPrefix!) } else { - data.append(version.privatePrefix) + data.append(HDversion.privatePrefix!) } data.append(contentsOf: [self.depth]) data.append(self.parentFingerprint) diff --git a/Sources/Core/KeystoreManager/BIP32Keystore.swift b/Sources/Core/KeystoreManager/BIP32Keystore.swift index 0059bed91..bf256c8ba 100755 --- a/Sources/Core/KeystoreManager/BIP32Keystore.swift +++ b/Sources/Core/KeystoreManager/BIP32Keystore.swift @@ -1,3 +1,4 @@ +// web3swift // // Created by Alex Vlasov. // Copyright © 2018 Alex Vlasov. All rights reserved. @@ -44,7 +45,6 @@ public class BIP32Keystore: AbstractKeystore { guard let decryptedRootNode = try? self.getPrefixNodeData(password) else {throw AbstractKeystoreError.encryptionError("Failed to decrypt a keystore")} guard let rootNode = HDNode(decryptedRootNode) else {throw AbstractKeystoreError.encryptionError("Failed to deserialize a root node")} guard rootNode.depth == (self.rootPrefix.components(separatedBy: "/").count - 1) else {throw AbstractKeystoreError.encryptionError("Derivation depth mismatch")} -// guard rootNode.depth == HDNode.defaultPathPrefix.components(separatedBy: "/").count - 1 else {throw AbstractKeystoreError.encryptionError("Derivation depth mismatch")} guard let index = UInt32(key.components(separatedBy: "/").last!) else { throw AbstractKeystoreError.encryptionError("Derivation depth mismatch") } @@ -63,7 +63,7 @@ public class BIP32Keystore: AbstractKeystore { private static let KeystoreParamsBIP32Version = 4 - public private (set) var addressStorage: PathAddressStorage + private (set) var addressStorage: PathAddressStorage public convenience init?(_ jsonString: String) { let lowercaseJSON = jsonString.lowercased() @@ -88,8 +88,15 @@ public class BIP32Keystore: AbstractKeystore { rootPrefix = keystoreParams!.rootPath! } - public convenience init?(mnemonics: String, password: String, mnemonicsPassword: String = "", language: BIP39Language = BIP39Language.english, prefixPath: String = HDNode.defaultPathMetamaskPrefix, aesMode: String = "aes-128-cbc") throws { - guard var seed = BIP39.seedFromMmemonics(mnemonics, password: mnemonicsPassword, language: language) else { + public convenience init?(mnemonics: String, password: String = "web3swift", mnemonicsPassword: String = "", language: BIP39Language = .english, prefixPath: String = HDNode.defaultPathMetamaskPrefix, aesMode: String = "aes-128-cbc") throws { + do { + try self.init(mnemonicsPhrase: mnemonics.components(separatedBy: language.separator)) + } catch { + return nil + } + } + public convenience init(mnemonicsPhrase: [String], password: String = "web3swift", mnemonicsPassword: String = "", language: BIP39Language = .english, prefixPath: String = HDNode.defaultPathMetamaskPrefix, aesMode: String = "aes-128-cbc") throws { + guard var seed = BIP39.seedFromMmemonics(mnemonicsPhrase, password: mnemonicsPassword, language: language) else { throw AbstractKeystoreError.noEntropyError } defer{ @@ -98,9 +105,12 @@ public class BIP32Keystore: AbstractKeystore { try self.init(seed: seed, password: password, prefixPath: prefixPath, aesMode: aesMode) } - public init? (seed: Data, password: String, prefixPath: String = HDNode.defaultPathMetamaskPrefix, aesMode: String = "aes-128-cbc") throws { + public init(seed: Data, password: String = "web3swift", prefixPath: String = HDNode.defaultPathMetamaskPrefix, aesMode: String = "aes-128-cbc") throws { + addressStorage = PathAddressStorage() - guard let rootNode = HDNode(seed: seed)?.derive(path: prefixPath, derivePrivateKey: true) else {return nil} + guard let rootNode = HDNode(seed: seed)?.derive(path: prefixPath, derivePrivateKey: true) else { + throw AbstractKeystoreError.keyDerivationError + } self.rootPrefix = prefixPath try createNewAccount(parentNode: rootNode, password: password) guard let serializedRootNode = rootNode.serialize(serializePublic: false) else { @@ -109,7 +119,7 @@ public class BIP32Keystore: AbstractKeystore { try encryptDataToStorage(password, data: serializedRootNode, aesMode: aesMode) } - public func createNewChildAccount(password: String) throws { + public func createNewChildAccount(password: String = "web3swift") throws { guard let decryptedRootNode = try? self.getPrefixNodeData(password) else { throw AbstractKeystoreError.encryptionError("Failed to decrypt a keystore") } @@ -127,14 +137,21 @@ public class BIP32Keystore: AbstractKeystore { try encryptDataToStorage(password, data: serializedRootNode, aesMode: self.keystoreParams!.crypto.cipher) } - func createNewAccount(parentNode: HDNode, password: String ) throws { - var newIndex = UInt32(0) - for p in addressStorage.paths { - guard let idx = UInt32(p.components(separatedBy: "/").last!) else {continue} - if idx >= newIndex { - newIndex = idx + 1 - } + func createNewAccount(parentNode: HDNode, password: String = "web3swift") throws { + + let maxIndex = addressStorage.paths + .compactMap { $0.components(separatedBy: "/").last } + .compactMap { UInt32($0) } + .max() + + let newIndex: UInt32 + + if let idx = maxIndex { + newIndex = idx + 1 + } else { + newIndex = UInt32.zero } + guard let newNode = parentNode.derive(index: newIndex, derivePrivateKey: true, hardened: false) else { throw AbstractKeystoreError.keyDerivationError } @@ -151,7 +168,7 @@ public class BIP32Keystore: AbstractKeystore { addressStorage.add(address: newAddress, for: newPath) } - public func createNewCustomChildAccount(password: String, path: String) throws {guard let decryptedRootNode = try? self.getPrefixNodeData(password) else { + public func createNewCustomChildAccount(password: String = "web3swift", path: String) throws {guard let decryptedRootNode = try? self.getPrefixNodeData(password) else { throw AbstractKeystoreError.encryptionError("Failed to decrypt a keystore") } guard let rootNode = HDNode(decryptedRootNode) else { @@ -203,38 +220,31 @@ public class BIP32Keystore: AbstractKeystore { try encryptDataToStorage(password, data: serializedRootNode, aesMode: self.keystoreParams!.crypto.cipher) } - fileprivate func encryptDataToStorage(_ password: String, data: Data?, dkLen: Int = 32, N: Int = 4096, R: Int = 6, P: Int = 1, aesMode: String = "aes-128-cbc") throws { - if (data == nil) { - throw AbstractKeystoreError.encryptionError("Encryption without key data") - } - if (data!.count != 82) { + fileprivate func encryptDataToStorage(_ password: String, data: Data, dkLen: Int = 32, N: Int = 4096, R: Int = 6, P: Int = 1, aesMode: String = "aes-128-cbc") throws { + guard data.count == 82 else { throw AbstractKeystoreError.encryptionError("Invalid expected data length") } - let saltLen = 32 - guard let saltData = Data.randomBytes(length: saltLen) else { - throw AbstractKeystoreError.noEntropyError - } - guard let derivedKey = scrypt(password: password, salt: saltData, length: dkLen, N: N, R: R, P: P) else { + let saltData = Data.randomBytes(length: 32) + + guard let derivedKey = scrypt(password: password, salt: saltData!, length: dkLen, N: N, R: R, P: P) else { throw AbstractKeystoreError.keyDerivationError } let last16bytes = derivedKey[(derivedKey.count - 16)...(derivedKey.count - 1)] let encryptionKey = derivedKey[0...15] - guard let IV = Data.randomBytes(length: 16) else { - throw AbstractKeystoreError.noEntropyError - } + let IV = Data.randomBytes(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 = try? 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 = try? AES(key: encryptionKey.bytes, blockMode: CTR(iv: IV!.bytes), padding: .pkcs7) default: aesCipher = nil } if aesCipher == nil { throw AbstractKeystoreError.aesError } - guard let encryptedKey = try aesCipher?.encrypt(data!.bytes) else { + guard let encryptedKey = try aesCipher?.encrypt(data.bytes) else { throw AbstractKeystoreError.aesError } let encryptedKeyData = Data(encryptedKey) @@ -242,8 +252,8 @@ public class BIP32Keystore: AbstractKeystore { 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 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) var keystorePars = KeystoreParamsBIP32(crypto: crypto, id: UUID().uuidString.lowercased(), version: Self.KeystoreParamsBIP32Version) @@ -369,7 +379,7 @@ public class BIP32Keystore: AbstractKeystore { return data } - public func serializeRootNodeToString(password: String ) throws -> String { + public func serializeRootNodeToString(password: String = "web3swift") throws -> String { guard let decryptedRootNode = try? self.getPrefixNodeData(password) else { throw AbstractKeystoreError.encryptionError("Failed to decrypt a keystore") } diff --git a/Sources/Core/KeystoreManager/BIP39.swift b/Sources/Core/KeystoreManager/BIP39.swift index 80271616a..2b025b4c7 100755 --- a/Sources/Core/KeystoreManager/BIP39.swift +++ b/Sources/Core/KeystoreManager/BIP39.swift @@ -1,3 +1,4 @@ +// web3swift // // Created by Alex Vlasov. // Copyright © 2018 Alex Vlasov. All rights reserved. @@ -71,48 +72,77 @@ public enum BIP39Language { public class BIP39 { static public func generateMnemonicsFromEntropy(entropy: Data, language: BIP39Language = BIP39Language.english) -> String? { - guard entropy.count >= 16, entropy.count & 4 == 0 else {return nil} - let checksum = entropy.sha256() - let checksumBits = entropy.count*8/32 - var fullEntropy = Data() - fullEntropy.append(entropy) - fullEntropy.append(checksum[0 ..< (checksumBits+7)/8 ]) - var wordList = [String]() - for i in 0 ..< fullEntropy.count*8/11 { - guard let bits = fullEntropy.bitsInRange(i*11, 11) else {return nil} - let index = Int(bits) - guard language.words.count > index else {return nil} - let word = language.words[index] - wordList.append(word) - } + let wordList = generateMnemonicsFrom(entropy: entropy) let separator = language.separator return wordList.joined(separator: separator) } + static public func generateMnemonicsFrom(entropy: Data, language: BIP39Language = BIP39Language.english) -> [String] { + let entropy_bit_size = entropy.count * 8 + let checksum_length = entropy_bit_size / 32 + + var entropy_bits = bitarray(from: entropy) + print("array: \(entropy_bits)") + guard let checksumTest = generateChecksum(entropyBytes: entropy, checksumLength: checksum_length) else { + return [] + } + entropy_bits += checksumTest + return entropy_bits + .split(every: 11) + .compactMap { binary in + Int(binary, radix: 2) + } + .map { index in + language.words[index] + } + } + + static func bitarray(from data: Data) -> String { + data.map { + let binary = String($0, radix: 2) + let padding = String(repeating: "0", count: 8 - binary.count) + return padding + binary + }.joined() + } + static func generateChecksum(entropyBytes inputData: Data, checksumLength: Int) -> String? { + guard let checksumData = inputData.sha256().bitsInRange(0, checksumLength) else { + return nil + } + let checksum = String(checksumData, radix: 2).leftPadding(toLength: checksumLength, withPad: "0") + return checksum + } + + /** + Initializes a new mnemonics set with the provided bitsOfEntropy. + **/ + /// Initializes a new mnemonics set with the provided bitsOfEntropy. /// - Parameters: /// - bitsOfEntropy: 128 - 12 words, 192 - 18 words , 256 - 24 words in output. /// - language: words language, default english - /// - Returns: random 12-24 words, that represent new Mnemonic phrase. - static public func generateMnemonics(bitsOfEntropy: Int, language: BIP39Language = BIP39Language.english) throws -> String? { + static public func generateMnemonics(bitsOfEntropy: Int, language: BIP39Language = BIP39Language.english) -> String? { guard bitsOfEntropy >= 128 && bitsOfEntropy <= 256 && bitsOfEntropy.isMultiple(of: 32) else {return nil} - guard let entropy = Data.randomBytes(length: bitsOfEntropy/8) else {throw AbstractKeystoreError.noEntropyError} - return BIP39.generateMnemonicsFromEntropy(entropy: entropy, language: - language) + let entropy = Data.randomBytes(length: bitsOfEntropy/8)! + return generateMnemonicsFromEntropy(entropy: entropy, language: language) + } + static public func generateMnemonics(entropy: Int, language: BIP39Language = BIP39Language.english) -> [String]? { + guard entropy >= 128 && entropy <= 256 && entropy.isMultiple(of: 32) else {return nil} + let entropy = Data.randomBytes(length: entropy/8)! + return generateMnemonicsFrom(entropy: entropy, language: language) } static public func mnemonicsToEntropy(_ mnemonics: String, language: BIP39Language = BIP39Language.english) -> Data? { - let wordList = mnemonics.components(separatedBy: " ") - guard wordList.count >= 12 && wordList.count.isMultiple(of: 3) && wordList.count <= 24 else {return nil} + mnemonicsToEntropy(mnemonics.components(separatedBy: language.separator)) + } + static public func mnemonicsToEntropy(_ mnemonics: [String], language: BIP39Language = BIP39Language.english) -> Data? { + guard mnemonics.count >= 12 && mnemonics.count.isMultiple(of: 3) && mnemonics.count <= 24 else {return nil} var bitString = "" - for word in wordList { - let idx = language.words.firstIndex(of: word) - if (idx == nil) { + for word in mnemonics { + guard let idx = language.words.firstIndex(of: word) else { return nil } - let idxAsInt = language.words.startIndex.distance(to: idx!) - let stringForm = String(UInt16(idxAsInt), radix: 2).leftPadding(toLength: 11, withPad: "0") + let stringForm = String(UInt16(idx), radix: 2).leftPadding(toLength: 11, withPad: "0") bitString.append(stringForm) } let stringCount = bitString.count @@ -132,22 +162,21 @@ public class BIP39 { } static public func seedFromMmemonics(_ mnemonics: String, password: String = "", language: BIP39Language = BIP39Language.english) -> Data? { - let valid = BIP39.mnemonicsToEntropy(mnemonics, language: language) != nil - if (!valid) { + seedFromMmemonics(mnemonics.components(separatedBy: language.separator)) + } + static public func seedFromMmemonics(_ mnemonics: [String], password: String = "", language: BIP39Language = BIP39Language.english) -> Data? { + if mnemonicsToEntropy(mnemonics, language: language) == nil { return nil } - guard let mnemData = mnemonics.decomposedStringWithCompatibilityMapping.data(using: .utf8) else {return nil} + guard let mnemData = mnemonics.joined(separator: language.separator).decomposedStringWithCompatibilityMapping.data(using: .utf8) else {return nil} let salt = "mnemonic" + password guard let saltData = salt.decomposedStringWithCompatibilityMapping.data(using: .utf8) else {return nil} guard let seedArray = try? PKCS5.PBKDF2(password: mnemData.bytes, salt: saltData.bytes, iterations: 2048, keyLength: 64, variant: HMAC.Variant.sha2(.sha512)).calculate() else {return nil} - let seed = Data(seedArray) - return seed + return Data(seedArray) } static public func seedFromEntropy(_ entropy: Data, password: String = "", language: BIP39Language = BIP39Language.english) -> Data? { - guard let mnemonics = BIP39.generateMnemonicsFromEntropy(entropy: entropy, language: language) else { - return nil - } - return BIP39.seedFromMmemonics(mnemonics, password: password, language: language) + let mnemonics = generateMnemonicsFrom(entropy: entropy, language: language) + return seedFromMmemonics(mnemonics, password: password, language: language) } } diff --git a/Sources/Core/KeystoreManager/EthereumKeystoreV3.swift b/Sources/Core/KeystoreManager/EthereumKeystoreV3.swift index 5c0373593..a9510c520 100755 --- a/Sources/Core/KeystoreManager/EthereumKeystoreV3.swift +++ b/Sources/Core/KeystoreManager/EthereumKeystoreV3.swift @@ -1,3 +1,4 @@ +// web3swift // // Created by Alex Vlasov. // Copyright © 2018 Alex Vlasov. All rights reserved. @@ -69,7 +70,7 @@ public class EthereumKeystoreV3: AbstractKeystore { } } - public init?(password: String, aesMode: String = "aes-128-cbc") throws { + public init?(password: String = "web3swift", aesMode: String = "aes-128-cbc") throws { guard var newPrivateKey = SECP256K1.generatePrivateKey() else { return nil } @@ -79,7 +80,7 @@ public class EthereumKeystoreV3: AbstractKeystore { try encryptDataToStorage(password, keyData: newPrivateKey, aesMode: aesMode) } - public init?(privateKey: Data, password: String, aesMode: String = "aes-128-cbc") throws { + public init?(privateKey: Data, password: String = "web3swift", aesMode: String = "aes-128-cbc") throws { guard privateKey.count == 32 else { return nil } @@ -93,18 +94,14 @@ public class EthereumKeystoreV3: AbstractKeystore { if (keyData == nil) { throw AbstractKeystoreError.encryptionError("Encryption without key data") } - let saltLen = 32 - guard let saltData = Data.randomBytes(length: saltLen) else { - throw AbstractKeystoreError.noEntropyError - } + let saltLen = 32; + let saltData = Data.randomBytes(length: saltLen)! guard let derivedKey = scrypt(password: password, salt: saltData, length: dkLen, N: N, R: R, P: P) else { throw AbstractKeystoreError.keyDerivationError } let last16bytes = Data(derivedKey[(derivedKey.count - 16)...(derivedKey.count - 1)]) let encryptionKey = Data(derivedKey[0...15]) - guard let IV = Data.randomBytes(length: 16) else { - throw AbstractKeystoreError.noEntropyError - } + let IV = Data.randomBytes(length: 16)! var aesCipher: AES? switch aesMode { case "aes-128-cbc": diff --git a/Sources/Core/KeystoreManager/KeystoreParams.swift b/Sources/Core/KeystoreManager/KeystoreParams.swift index a0329aa88..07dfb9d2a 100644 --- a/Sources/Core/KeystoreManager/KeystoreParams.swift +++ b/Sources/Core/KeystoreManager/KeystoreParams.swift @@ -12,10 +12,22 @@ public struct KdfParamsV3: Decodable, Encodable { var r: Int? var c: Int? var prf: String? + public init(salt: String, dklen: Int, n: Int? = nil, p: Int? = nil, r: Int? = nil, c: Int? = nil, prf: String? = nil) { + self.salt = salt + self.dklen = dklen + self.n = n + self.p = p + self.r = r + self.c = c + self.prf = prf + } } public struct CipherParamsV3: Decodable, Encodable { var iv: String + public init(iv: String) { + self.iv = iv + } } public struct CryptoParamsV3: Decodable, Encodable { @@ -26,6 +38,15 @@ public struct CryptoParamsV3: Decodable, Encodable { var kdfparams: KdfParamsV3 var mac: String var version: String? + public init(ciphertext: String, cipher: String, cipherparams: CipherParamsV3, kdf: String, kdfparams: KdfParamsV3, mac: String, version: String? = nil) { + self.ciphertext = ciphertext + self.cipher = cipher + self.cipherparams = cipherparams + self.kdf = kdf + self.kdfparams = kdfparams + self.mac = mac + self.version = version + } } public protocol AbstractKeystoreParams: Codable { @@ -37,8 +58,12 @@ public protocol AbstractKeystoreParams: Codable { } public struct PathAddressPair: Codable { - let path: String - let address: String + public let path: String + public let address: String + public init(path: String, address: String) { + self.path = path + self.address = address + } } public struct KeystoreParamsBIP32: AbstractKeystoreParams { @@ -61,7 +86,7 @@ public struct KeystoreParamsBIP32: AbstractKeystoreParams { } } - var pathAddressPairs: [PathAddressPair] + public var pathAddressPairs: [PathAddressPair] var rootPath: String? public init(crypto cr: CryptoParamsV3, id i: String, version ver: Int = 32, rootPath: String? = nil) { @@ -81,7 +106,7 @@ public struct KeystoreParamsV3: AbstractKeystoreParams { public var version: Int public var isHDWallet: Bool - var address: String? + public var address: String? public init(address ad: String?, crypto cr: CryptoParamsV3, id i: String, version ver: Int) { address = ad diff --git a/Sources/Core/Utility/Data+Extension.swift b/Sources/Core/Utility/Data+Extension.swift index 4bc94645d..482c0fa90 100755 --- a/Sources/Core/Utility/Data+Extension.swift +++ b/Sources/Core/Utility/Data+Extension.swift @@ -4,6 +4,7 @@ // import Foundation +import Metal extension Data { init(fromArray values: [T]) { @@ -41,21 +42,23 @@ extension Data { } public static func randomBytes(length: Int) -> Data? { - for _ in 0...1024 { - var data = Data(repeating: 0, count: length) - let result = data.withUnsafeMutableBytes { (body: UnsafeMutableRawBufferPointer) -> Int32? in - if let bodyAddress = body.baseAddress, body.count > 0 { - let pointer = bodyAddress.assumingMemoryBound(to: UInt8.self) - return SecRandomCopyBytes(kSecRandomDefault, length, pointer) - } else { - return nil - } - } - if let notNilResult = result, notNilResult == errSecSuccess { - return data - } + let entropy_bit_size = length//128 + //# valid_entropy_bit_sizes = [128, 160, 192, 224, 256], count: [12, 15, 18, 21, 24] + var entropy_bytes = [UInt8](repeating: 0, count: entropy_bit_size)// / 8) + let status = SecRandomCopyBytes(kSecRandomDefault, entropy_bytes.count, &entropy_bytes) + + if status != errSecSuccess { // Always test the status. + } else { + entropy_bytes = [UInt8](repeating: 0, count: entropy_bit_size)// / 8) + arc4random_buf(&entropy_bytes, entropy_bytes.count) + } + + let source1 = MTLCreateSystemDefaultDevice()?.makeBuffer(length: length)?.hash.description.data(using: .utf8) + + let entropyData = entropy_bytes.shuffled().map{ bit in + return bit ^ (source1?.randomElement() ?? 0) } - return nil + return Data(entropyData) } public func bitsInRange(_ startingBit: Int, _ length: Int) -> UInt64? { // return max of 8 bytes for simplicity, non-public diff --git a/Sources/Core/Utility/String+Extension.swift b/Sources/Core/Utility/String+Extension.swift index 45a7ecbf3..cd0511dce 100755 --- a/Sources/Core/Utility/String+Extension.swift +++ b/Sources/Core/Utility/String+Extension.swift @@ -32,6 +32,26 @@ extension String { return output } + /// Splits a string into groups of `every` n characters, grouping from left-to-right by default. If `backwards` is true, right-to-left. + public func split(every: Int, backwards: Bool = false) -> [String] { + var result = [String]() + + for i in stride(from: 0, to: self.count, by: every) { + switch backwards { + case true: + let endIndex = self.index(self.endIndex, offsetBy: -i) + let startIndex = self.index(endIndex, offsetBy: -every, limitedBy: self.startIndex) ?? self.startIndex + result.insert(String(self[startIndex..) -> String { let start = index(self.startIndex, offsetBy: bounds.lowerBound) let end = index(self.startIndex, offsetBy: bounds.upperBound) @@ -50,7 +70,7 @@ extension String { return String(self[start.. String { + public func leftPadding(toLength: Int, withPad character: Character) -> String { let stringLength = self.count if stringLength < toLength { return String(repeatElement(character, count: toLength - stringLength)) + self diff --git a/Sources/web3swift/Operations/WriteOperation.swift b/Sources/web3swift/Operations/WriteOperation.swift index 89f7f3a72..401298918 100755 --- a/Sources/web3swift/Operations/WriteOperation.swift +++ b/Sources/web3swift/Operations/WriteOperation.swift @@ -12,7 +12,7 @@ public class WriteOperation: ReadOperation { // FIXME: Rewrite this to CodableTransaction public func writeToChain(password: String) async throws -> TransactionSendingResult { - await transaction.resolve(provider: web3.provider) + try await transaction.resolve(provider: web3.provider) if let attachedKeystoreManager = self.web3.provider.attachedKeystoreManager { do { try Web3Signer.signTX(transaction: &transaction, @@ -28,6 +28,34 @@ public class WriteOperation: ReadOperation { // MARK: Sending Data flow return try await web3.eth.send(transaction) } + + public func depploy(password: String) async throws -> TransactionSendingResult { + //TODO: optimize/cleanup + try await transaction.resolve(provider: web3.provider) + + guard let attachedKeystoreManager = self.web3.provider.attachedKeystoreManager else { + throw Web3Error.inputError(desc: "Failed to locally sign a transaction") + } + + do { + //TODO: optimize/cleanup +// transaction.unsign() + let account = transaction.from ?? transaction.sender ?? EthereumAddress.contractDeploymentAddress() + var privateKey = try attachedKeystoreManager.UNSAFE_getPrivateKeyData(password: password, account: account) + defer { Data.zero(&privateKey) } + try transaction.sign(privateKey: privateKey, useExtraEntropy: false) + } catch { + throw Web3Error.inputError(desc: "Failed to locally sign a transaction") + } + + //TODO: optimize/cleanup + guard let transactionData = transaction.encode(for: .transaction) else { throw Web3Error.dataError } +// let vectorHash = transaction.hash!.toHexString().addHexPrefix() +// print(vectorHash) + + //TODO: optimize/cleanup + return try await web3.eth.send(raw: transactionData) + } // FIXME: Rewrite this to CodableTransaction func nonce(for policy: CodableTransaction.NoncePolicy, from: EthereumAddress) async throws -> BigUInt { diff --git a/Sources/web3swift/Utils/ENS/ENS.swift b/Sources/web3swift/Utils/ENS/ENS.swift index d598d57e6..c4b2a3408 100755 --- a/Sources/web3swift/Utils/ENS/ENS.swift +++ b/Sources/web3swift/Utils/ENS/ENS.swift @@ -84,7 +84,7 @@ public class ENS { guard let resolver = try? await self.registry.getResolver(forDomain: node) else { throw Web3Error.processingError(desc: "Failed to get resolver for domain") } - guard let isAddrSupports = try? await resolver.supportsInterface(interfaceID: Resolver.InterfaceName.addr.hash()) else { + guard let isAddrSupports = try? await resolver.supportsInterface(interfaceID: Resolver.InterfaceName.addr.rawValue) else { throw Web3Error.processingError(desc: "Resolver don't support interface with this ID") } guard isAddrSupports else { @@ -101,7 +101,7 @@ public class ENS { guard let resolver = try? await self.registry.getResolver(forDomain: node) else { throw Web3Error.processingError(desc: "Failed to get resolver for domain") } - guard let isAddrSupports = try? await resolver.supportsInterface(interfaceID: Resolver.InterfaceName.addr.hash()) else { + guard let isAddrSupports = try? await resolver.supportsInterface(interfaceID: Resolver.InterfaceName.addr.rawValue) else { throw Web3Error.processingError(desc: "Resolver don't support interface with this ID") } guard isAddrSupports else { @@ -119,12 +119,12 @@ public class ENS { guard let resolver = try? await self.registry.getResolver(forDomain: node) else { throw Web3Error.processingError(desc: "Failed to get resolver for domain") } - guard let isNameSupports = try? await resolver.supportsInterface(interfaceID: Resolver.InterfaceName.name.hash()) else { - throw Web3Error.processingError(desc: "Resolver don't support interface with this ID") - } - guard isNameSupports else { - throw Web3Error.processingError(desc: "Name isn't supported") - } +// guard let isNameSupports = try? await resolver.supportsInterface(interfaceID: Resolver.InterfaceName.name.rawValue) else { +// throw Web3Error.processingError(desc: "Resolver don't support interface with this ID") +// } +// guard isNameSupports else { +// throw Web3Error.processingError(desc: "Name isn't supported") +// } guard let name = try? await resolver.getCanonicalName(forNode: node) else { throw Web3Error.processingError(desc: "Can't get name") } @@ -136,7 +136,7 @@ public class ENS { guard let resolver = try? await self.registry.getResolver(forDomain: node) else { throw Web3Error.processingError(desc: "Failed to get resolver for domain") } - guard let isNameSupports = try? await resolver.supportsInterface(interfaceID: Resolver.InterfaceName.name.hash()) else { + guard let isNameSupports = try? await resolver.supportsInterface(interfaceID: Resolver.InterfaceName.name.rawValue) else { throw Web3Error.processingError(desc: "Resolver don't support interface with this ID") } guard isNameSupports else { @@ -155,7 +155,7 @@ public class ENS { guard let resolver = try? await self.registry.getResolver(forDomain: node) else { throw Web3Error.processingError(desc: "Failed to get resolver for domain") } - guard let isContentSupports = try? await resolver.supportsInterface(interfaceID: Resolver.InterfaceName.content.hash()) else { + guard let isContentSupports = try? await resolver.supportsInterface(interfaceID: Resolver.InterfaceName.content.rawValue) else { throw Web3Error.processingError(desc: "Resolver don't support interface with this ID") } guard isContentSupports else { @@ -172,7 +172,7 @@ public class ENS { guard let resolver = try? await self.registry.getResolver(forDomain: node) else { throw Web3Error.processingError(desc: "Failed to get resolver for domain") } - guard let isContentSupports = try? await resolver.supportsInterface(interfaceID: Resolver.InterfaceName.content.hash()) else { + guard let isContentSupports = try? await resolver.supportsInterface(interfaceID: Resolver.InterfaceName.content.rawValue) else { throw Web3Error.processingError(desc: "Resolver don't support interface with this ID") } guard isContentSupports else { @@ -190,7 +190,7 @@ public class ENS { guard let resolver = try? await self.registry.getResolver(forDomain: node) else { throw Web3Error.processingError(desc: "Failed to get resolver for domain") } - guard let isABISupports = try? await resolver.supportsInterface(interfaceID: Resolver.InterfaceName.ABI.hash()) else { + guard let isABISupports = try? await resolver.supportsInterface(interfaceID: Resolver.InterfaceName.ABI.rawValue) else { throw Web3Error.processingError(desc: "Resolver don't support interface with this ID") } guard isABISupports else { @@ -207,7 +207,7 @@ public class ENS { guard let resolver = try? await self.registry.getResolver(forDomain: node) else { throw Web3Error.processingError(desc: "Failed to get resolver for domain") } - guard let isABISupports = try? await resolver.supportsInterface(interfaceID: Resolver.InterfaceName.ABI.hash()) else { + guard let isABISupports = try? await resolver.supportsInterface(interfaceID: Resolver.InterfaceName.ABI.rawValue) else { throw Web3Error.processingError(desc: "Resolver don't support interface with this ID") } guard isABISupports else { @@ -225,7 +225,7 @@ public class ENS { guard let resolver = try? await self.registry.getResolver(forDomain: node) else { throw Web3Error.processingError(desc: "Failed to get resolver for domain") } - guard let isPKSupports = try? await resolver.supportsInterface(interfaceID: Resolver.InterfaceName.pubkey.hash()) else { + guard let isPKSupports = try? await resolver.supportsInterface(interfaceID: Resolver.InterfaceName.pubkey.rawValue) else { throw Web3Error.processingError(desc: "Resolver don't support interface with this ID") } guard isPKSupports else { @@ -242,7 +242,7 @@ public class ENS { guard let resolver = try? await self.registry.getResolver(forDomain: node) else { throw Web3Error.processingError(desc: "Failed to get resolver for domain") } - guard let isPKSupports = try? await resolver.supportsInterface(interfaceID: Resolver.InterfaceName.pubkey.hash()) else { + guard let isPKSupports = try? await resolver.supportsInterface(interfaceID: Resolver.InterfaceName.pubkey.rawValue) else { throw Web3Error.processingError(desc: "Resolver don't support interface with this ID") } guard isPKSupports else { @@ -260,7 +260,7 @@ public class ENS { guard let resolver = try? await self.registry.getResolver(forDomain: node) else { throw Web3Error.processingError(desc: "Failed to get resolver for domain") } - guard let isTextSupports = try? await resolver.supportsInterface(interfaceID: Resolver.InterfaceName.text.hash()) else { + guard let isTextSupports = try? await resolver.supportsInterface(interfaceID: Resolver.InterfaceName.text.rawValue) else { throw Web3Error.processingError(desc: "Resolver don't support interface with this ID") } guard isTextSupports else { @@ -277,7 +277,7 @@ public class ENS { guard let resolver = try? await self.registry.getResolver(forDomain: node) else { throw Web3Error.processingError(desc: "Failed to get resolver for domain") } - guard let isTextSupports = try? await resolver.supportsInterface(interfaceID: Resolver.InterfaceName.text.hash()) else { + guard let isTextSupports = try? await resolver.supportsInterface(interfaceID: Resolver.InterfaceName.text.rawValue) else { throw Web3Error.processingError(desc: "Resolver don't support interface with this ID") } guard isTextSupports else { From f6f86b39c4f8605256da5b787a873a66a2f5dab4 Mon Sep 17 00:00:00 2001 From: "pharms.eth" <100330083+pharms-eth@users.noreply.github.com> Date: Tue, 15 Nov 2022 18:25:40 -0800 Subject: [PATCH 06/18] cleanup post merge --- .../Core/KeystoreManager/BIP32Keystore.swift | 2 +- .../Core/Transaction/CodableTransaction.swift | 110 ------------------ .../web3swift/Operations/WriteOperation.swift | 24 ++-- .../web3swiftTests/remoteTests/ENSTests.swift | 10 +- 4 files changed, 18 insertions(+), 128 deletions(-) diff --git a/Sources/Core/KeystoreManager/BIP32Keystore.swift b/Sources/Core/KeystoreManager/BIP32Keystore.swift index 040bb3cdf..756d40ced 100755 --- a/Sources/Core/KeystoreManager/BIP32Keystore.swift +++ b/Sources/Core/KeystoreManager/BIP32Keystore.swift @@ -63,7 +63,7 @@ public class BIP32Keystore: AbstractKeystore { private static let KeystoreParamsBIP32Version = 4 - private (set) var addressStorage: PathAddressStorage + public private(set) var addressStorage: PathAddressStorage public convenience init?(_ jsonString: String) { let lowercaseJSON = jsonString.lowercased() diff --git a/Sources/Core/Transaction/CodableTransaction.swift b/Sources/Core/Transaction/CodableTransaction.swift index 896625a11..19f5773fe 100644 --- a/Sources/Core/Transaction/CodableTransaction.swift +++ b/Sources/Core/Transaction/CodableTransaction.swift @@ -188,27 +188,6 @@ public struct CodableTransaction { return self.envelope.encode(for: type) } - public mutating func resolve(provider: Web3Provider) async throws { - // FIXME: Delete force try - self.gasLimit = try await self.gasLimitPolicy.resolve(provider: provider, transaction: self) - - if from != nil || sender != nil { - self.nonce = try await self.resolveNonce(provider: provider) - } - if case .eip1559 = type { - self.maxFeePerGas = try await self.maxFeePerGasPolicy.resolve(provider: provider) - self.maxPriorityFeePerGas = try await self.maxPriorityFeePerGasPolicy.resolve(provider: provider) - } else { - self.gasPrice = try await self.gasPricePolicy.resolve(provider: provider) - } - } - - public var noncePolicy: NoncePolicy - public var maxFeePerGasPolicy: FeePerGasPolicy - public var maxPriorityFeePerGasPolicy: PriorityFeePerGasPolicy - public var gasPricePolicy: GasPricePolicy - public var gasLimitPolicy: GasLimitPolicy - public static var emptyTransaction = CodableTransaction(to: EthereumAddress.contractDeploymentAddress()) } @@ -285,95 +264,6 @@ extension CodableTransaction: Codable { } -public protocol Policyable { - func resolve(provider: Web3Provider, transaction: CodableTransaction?) async throws -> BigUInt -} - -extension CodableTransaction { - public enum GasLimitPolicy { - case automatic - case manual(BigUInt) - case limited(BigUInt) - case withMargin(Double) - - func resolve(provider: Web3Provider, transaction: CodableTransaction?) async throws -> BigUInt { - guard let transaction = transaction else { throw Web3Error.valueError } - let request: APIRequest = .estimateGas(transaction, transaction.callOnBlock ?? .latest) - let response: APIResponse = try await APIRequest.sendRequest(with: provider, for: request) - switch self { - case .automatic, .withMargin: - return response.result - case .manual(let value): - return value - case .limited(let limit): - if limit <= response.result { - return response.result - } else { - return limit - } - } - } - } - - public enum GasPricePolicy { - case automatic - case manual(BigUInt) - case withMargin(Double) - - func resolve(provider: Web3Provider, transaction: CodableTransaction? = nil) async throws -> BigUInt { - let oracle = Oracle(provider) - switch self { - case .automatic, .withMargin: - return await oracle.gasPriceLegacyPercentiles().max() ?? 0 - case .manual(let value): - return value - } - } - } - - public enum PriorityFeePerGasPolicy: Policyable { - case automatic - case manual(BigUInt) - - public func resolve(provider: Web3Provider, transaction: CodableTransaction? = nil) async throws -> BigUInt { - let oracle = Oracle(provider) - switch self { - case .automatic: - return await oracle.tipFeePercentiles().max() ?? 0 - case .manual(let value): - return value - } - } - } - - public enum FeePerGasPolicy: Policyable { - case automatic - case manual(BigUInt) - - public func resolve(provider: Web3Provider, transaction: CodableTransaction? = nil) async throws -> BigUInt { - let oracle = Oracle(provider) - switch self { - case .automatic: - return await oracle.baseFeePercentiles().max() ?? 0 - case .manual(let value): - return value - } - } - } - - func resolveNonce(provider: Web3Provider) async throws -> BigUInt { - switch noncePolicy { - case .pending, .latest, .earliest: - guard let address = from ?? sender else { throw Web3Error.valueError } - let request: APIRequest = .getTransactionCount(address.address, callOnBlock ?? .latest) - let response: APIResponse = try await APIRequest.sendRequest(with: provider, for: request) - return response.result - case .exact(let value): - return value - } - } -} - extension CodableTransaction: CustomStringConvertible { /// required by CustomString convertable /// returns a string description for the transaction and its data diff --git a/Sources/web3swift/Operations/WriteOperation.swift b/Sources/web3swift/Operations/WriteOperation.swift index b055611dd..6aebe8683 100755 --- a/Sources/web3swift/Operations/WriteOperation.swift +++ b/Sources/web3swift/Operations/WriteOperation.swift @@ -63,16 +63,16 @@ public class WriteOperation: ReadOperation { } // FIXME: Rewrite this to CodableTransaction - func nonce(for policy: CodableTransaction.NoncePolicy, from: EthereumAddress) async throws -> BigUInt { - switch policy { - case .latest: - return try await self.web3.eth.getTransactionCount(for: from, onBlock: .latest) - case .pending: - return try await self.web3.eth.getTransactionCount(for: from, onBlock: .pending) - case .earliest: - return try await self.web3.eth.getTransactionCount(for: from, onBlock: .earliest) - case .exact(let nonce): - return nonce - } - } +// func nonce(for policy: CodableTransaction.NoncePolicy, from: EthereumAddress) async throws -> BigUInt { +// switch policy { +// case .latest: +// return try await self.web3.eth.getTransactionCount(for: from, onBlock: .latest) +// case .pending: +// return try await self.web3.eth.getTransactionCount(for: from, onBlock: .pending) +// case .earliest: +// return try await self.web3.eth.getTransactionCount(for: from, onBlock: .earliest) +// case .exact(let nonce): +// return nonce +// } +// } } diff --git a/Tests/web3swiftTests/remoteTests/ENSTests.swift b/Tests/web3swiftTests/remoteTests/ENSTests.swift index 598948504..3097cb4b0 100755 --- a/Tests/web3swiftTests/remoteTests/ENSTests.swift +++ b/Tests/web3swiftTests/remoteTests/ENSTests.swift @@ -44,10 +44,10 @@ class ENSTests: XCTestCase { let ens = ENS(web3: web3) let domain = "somename.eth" let resolver = try await ens?.registry.getResolver(forDomain: domain) - let isAddrSupports = try await resolver?.supportsInterface(interfaceID: ENS.Resolver.InterfaceName.addr.hash()) - let isNameSupports = try await resolver?.supportsInterface(interfaceID: ENS.Resolver.InterfaceName.name.hash()) - let isABIsupports = try await resolver?.supportsInterface(interfaceID: ENS.Resolver.InterfaceName.ABI.hash()) - let isPubkeySupports = try await resolver?.supportsInterface(interfaceID: ENS.Resolver.InterfaceName.pubkey.hash()) + let isAddrSupports = try await resolver?.supportsInterface(interfaceID: ENS.Resolver.InterfaceName.addr.rawValue) + let isNameSupports = try await resolver?.supportsInterface(interfaceID: ENS.Resolver.InterfaceName.name.rawValue) + let isABIsupports = try await resolver?.supportsInterface(interfaceID: ENS.Resolver.InterfaceName.ABI.rawValue) + let isPubkeySupports = try await resolver?.supportsInterface(interfaceID: ENS.Resolver.InterfaceName.pubkey.rawValue) XCTAssertEqual(isAddrSupports, true) XCTAssertEqual(isNameSupports, true) XCTAssertEqual(isABIsupports, true) @@ -59,7 +59,7 @@ class ENSTests: XCTestCase { let ens = ENS(web3: web3) let domain = "somename.eth" let resolver = try await ens?.registry.getResolver(forDomain: domain) - if let isABIsupported = try await resolver?.supportsInterface(interfaceID: ENS.Resolver.InterfaceName.ABI.hash()), + if let isABIsupported = try await resolver?.supportsInterface(interfaceID: ENS.Resolver.InterfaceName.ABI.rawValue), isABIsupported { let res = try await resolver?.getContractABI(forNode: domain, contentType: .zlibCompressedJSON) XCTAssert(res?.0 == 0) From e968d2f68d0b095386742d72b9792a311b0dc5a8 Mon Sep 17 00:00:00 2001 From: "pharms.eth" <100330083+pharms-eth@users.noreply.github.com> Date: Wed, 16 Nov 2022 07:08:47 -0800 Subject: [PATCH 07/18] fix bug --- Tests/web3swiftTests/localTests/LocalTestCase.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Tests/web3swiftTests/localTests/LocalTestCase.swift b/Tests/web3swiftTests/localTests/LocalTestCase.swift index 20640fd3b..e56048d82 100644 --- a/Tests/web3swiftTests/localTests/LocalTestCase.swift +++ b/Tests/web3swiftTests/localTests/LocalTestCase.swift @@ -15,8 +15,10 @@ class LocalTestCase: XCTestCase { override func setUp() async throws { let web3 = try! await Web3.new(LocalTestCase.url) - let block = try! await web3.eth.blockNumber() - if block >= 25 { return } + guard let block = try? await web3.eth.blockNumber() else { + return + } + guard block < 25 else { return } print("\n ****** Preloading Ganache (\(25 - block) blocks) *****\n") From 1fff2c678de2787c3657615ad6999e9f5ea9d8af Mon Sep 17 00:00:00 2001 From: "pharms.eth" <100330083+pharms-eth@users.noreply.github.com> Date: Wed, 16 Nov 2022 10:49:35 -0800 Subject: [PATCH 08/18] improve test code actually handle force unwrap fix crashes --- .../localTests/KeystoresTests.swift | 230 +++++++++++++----- 1 file changed, 169 insertions(+), 61 deletions(-) diff --git a/Tests/web3swiftTests/localTests/KeystoresTests.swift b/Tests/web3swiftTests/localTests/KeystoresTests.swift index 4911c6c39..82032dcd6 100755 --- a/Tests/web3swiftTests/localTests/KeystoresTests.swift +++ b/Tests/web3swiftTests/localTests/KeystoresTests.swift @@ -73,126 +73,225 @@ class KeystoresTests: LocalTestCase { func testHMAC() throws { let seed = Data.fromHex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b")! let data = Data.fromHex("4869205468657265")! - let hmac = try! HMAC.init(key: seed.bytes, variant: HMAC.Variant.sha2(.sha512)).authenticate(data.bytes) + guard let hmac = try? HMAC.init(key: seed.bytes, variant: HMAC.Variant.sha2(.sha512)).authenticate(data.bytes) else { + XCTFail() + return + } XCTAssert(Data(hmac).toHexString() == "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854") } func testV3keystoreExportPrivateKey() throws { - let keystore = try! EthereumKeystoreV3(password: "") + guard let keystore = try? EthereumKeystoreV3(password: "") else { + XCTFail() + return + } XCTAssertNotNil(keystore) - let account = keystore!.addresses![0] + let account = keystore.addresses![0] print(account) - let data = try! keystore!.serialize() - print(try! JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions(rawValue: 0))) - let key = try! keystore!.UNSAFE_getPrivateKeyData(password: "", account: account) + guard let data = try? keystore.serialize() else { + XCTFail() + return + } + print(try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions(rawValue: 0))) + guard let key = try? keystore.UNSAFE_getPrivateKeyData(password: "", account: account) else { + XCTFail() + return + } XCTAssertNotNil(key) } func testV3keystoreSerialization() throws { - let keystore = try! EthereumKeystoreV3(password: "") + guard let keystore = try? EthereumKeystoreV3(password: "") else { + XCTFail() + return + } XCTAssertNotNil(keystore) - let account = keystore!.addresses![0] - let data = try! keystore!.serialize() - let key = try! keystore!.UNSAFE_getPrivateKeyData(password: "", account: account) + let account = keystore.addresses![0] + guard let data = try? keystore.serialize() else { + XCTFail() + return + } + guard let key = try? keystore.UNSAFE_getPrivateKeyData(password: "", account: account) else { + XCTFail() + return + } XCTAssertNotNil(key) - let restored = EthereumKeystoreV3(data!) + let restored = EthereumKeystoreV3(data) XCTAssertNotNil(restored) - XCTAssertEqual(keystore!.addresses!.first!, restored!.addresses!.first!) - let restoredKey = try! restored!.UNSAFE_getPrivateKeyData(password: "", account: account) + XCTAssertEqual(keystore.addresses!.first!, restored!.addresses!.first!) + guard let restoredKey = try? restored!.UNSAFE_getPrivateKeyData(password: "", account: account) else { + XCTFail() + return + } XCTAssertNotNil(restoredKey) XCTAssertEqual(key, restoredKey) } func testNewBIP32keystore() throws { - let mnemonic = try! BIP39.generateMnemonics(bitsOfEntropy: 256)! - let keystore = try! BIP32Keystore(mnemonics: mnemonic, password: "", mnemonicsPassword: "") + guard let mnemonic = BIP39.generateMnemonics(bitsOfEntropy: 256) else { + XCTFail() + return + } + let keystore = try? BIP32Keystore(mnemonics: mnemonic, password: "", mnemonicsPassword: "") XCTAssert(keystore != nil) } func testSameAddressesFromTheSameMnemonics() throws { - let mnemonic = try! BIP39.generateMnemonics(bitsOfEntropy: 256)! - let keystore1 = try! BIP32Keystore(mnemonics: mnemonic, password: "", mnemonicsPassword: "") - let keystore2 = try! BIP32Keystore(mnemonics: mnemonic, password: "", mnemonicsPassword: "") - XCTAssert(keystore1?.addresses?.first == keystore2?.addresses?.first) + guard let mnemonic = BIP39.generateMnemonics(bitsOfEntropy: 256) else { + XCTFail() + return + } + guard let keystore1 = try? BIP32Keystore(mnemonics: mnemonic, password: "", mnemonicsPassword: "") else { + XCTFail() + return + } + guard let keystore2 = try? BIP32Keystore(mnemonics: mnemonic, password: "", mnemonicsPassword: "") else { + XCTFail() + return + } + XCTAssert(keystore1.addresses?.first == keystore2.addresses?.first) } func testBIP32keystoreExportPrivateKey() throws { let mnemonic = "normal dune pole key case cradle unfold require tornado mercy hospital buyer" - let keystore = try! BIP32Keystore(mnemonics: mnemonic, password: "", mnemonicsPassword: "") + guard let keystore = try? BIP32Keystore(mnemonics: mnemonic, password: "", mnemonicsPassword: "") else { + XCTFail() + return + } XCTAssertNotNil(keystore) - let account = keystore!.addresses![0] - let key = try! keystore!.UNSAFE_getPrivateKeyData(password: "", account: account) + let account = keystore.addresses![0] + guard let key = try? keystore.UNSAFE_getPrivateKeyData(password: "", account: account) else { + XCTFail() + return + } XCTAssertNotNil(key) } func testBIP32keystoreMatching() throws { - let keystore = try! BIP32Keystore(mnemonics: mnemonic, password: "", mnemonicsPassword: "banana") + guard let keystore = try? BIP32Keystore(mnemonics: mnemonic, password: "", mnemonicsPassword: "banana") else { + XCTFail() + return + } XCTAssertNotNil(keystore) - let account = keystore!.addresses![0] - let key = try! keystore!.UNSAFE_getPrivateKeyData(password: "", account: account) + let account = keystore.addresses![0] + guard let key = try? keystore.UNSAFE_getPrivateKeyData(password: "", account: account) else { + XCTFail() + return + } let pubKey = Utilities.privateToPublic(key, compressed: true) XCTAssert(pubKey?.toHexString() == "027160bd3a4d938cac609ff3a11fe9233de7b76c22a80d2b575e202cbf26631659") } func testBIP32keystoreMatchingRootNode() throws { - let keystore = try! BIP32Keystore(mnemonics: mnemonic, password: "", mnemonicsPassword: "banana") + guard let keystore = try? BIP32Keystore(mnemonics: mnemonic, password: "", mnemonicsPassword: "banana") else { + XCTFail() + return + } XCTAssertNotNil(keystore) - let rootNode = try! keystore!.serializeRootNodeToString(password: "") + guard let rootNode = try? keystore.serializeRootNodeToString(password: "") else { + XCTFail() + return + } XCTAssert(rootNode == "xprvA2KM71v838kPwE8Lfr12m9DL939TZmPStMnhoFcZkr1nBwDXSG7c3pjYbMM9SaqcofK154zNSCp7W7b4boEVstZu1J3pniLQJJq7uvodfCV") } func testBIP32keystoreCustomPathMatching() throws { - let keystore = try! BIP32Keystore(mnemonics: mnemonic, password: "", mnemonicsPassword: "banana", prefixPath: "m/44'/60'/0'/0") + guard let keystore = try? BIP32Keystore(mnemonics: mnemonic, password: "", mnemonicsPassword: "banana", prefixPath: "m/44'/60'/0'/0") else { + XCTFail() + return + } XCTAssertNotNil(keystore) - let account = keystore!.addresses![0] - let key = try! keystore!.UNSAFE_getPrivateKeyData(password: "", account: account) + let account = keystore.addresses![0] + guard let key = try? keystore.UNSAFE_getPrivateKeyData(password: "", account: account) else { + XCTFail() + return + } let pubKey = Utilities.privateToPublic(key, compressed: true) XCTAssert(pubKey?.toHexString() == "027160bd3a4d938cac609ff3a11fe9233de7b76c22a80d2b575e202cbf26631659") } func testByBIP32keystoreCreateChildAccount() throws { let mnemonic = "normal dune pole key case cradle unfold require tornado mercy hospital buyer" - let keystore = try! BIP32Keystore(mnemonics: mnemonic, password: "", mnemonicsPassword: "") + guard let keystore = try? BIP32Keystore(mnemonics: mnemonic, password: "", mnemonicsPassword: "") else { + XCTFail() + return + } XCTAssertNotNil(keystore) - XCTAssertEqual(keystore!.addresses?.count, 1) - try! keystore?.createNewChildAccount(password: "") - XCTAssertEqual(keystore?.addresses?.count, 2) - let account = keystore!.addresses![0] - let key = try! keystore!.UNSAFE_getPrivateKeyData(password: "", account: account) + XCTAssertEqual(keystore.addresses?.count, 1) + try? keystore.createNewChildAccount(password: "") + XCTAssertEqual(keystore.addresses?.count, 2) + let account = keystore.addresses![0] + guard let key = try? keystore.UNSAFE_getPrivateKeyData(password: "", account: account) else { + XCTFail() + return + } XCTAssertNotNil(key) } func testByBIP32keystoreCreateCustomChildAccount() throws { let mnemonic = "normal dune pole key case cradle unfold require tornado mercy hospital buyer" - let keystore = try! BIP32Keystore(mnemonics: mnemonic, password: "", mnemonicsPassword: "") + guard let keystore = try? BIP32Keystore(mnemonics: mnemonic, password: "", mnemonicsPassword: "") else { + XCTFail() + return + } XCTAssertNotNil(keystore) - XCTAssertEqual(keystore!.addresses?.count, 1) - try! keystore?.createNewCustomChildAccount(password: "", path: "/42/1") - XCTAssertEqual(keystore?.addresses?.count, 2) - let account = keystore!.addresses![1] - let key = try! keystore!.UNSAFE_getPrivateKeyData(password: "", account: account) + XCTAssertEqual(keystore.addresses?.count, 1) + try? keystore.createNewCustomChildAccount(password: "", path: "/42/1") + XCTAssertEqual(keystore.addresses?.count, 2) + guard keystore.addresses?.count ?? 0 > 1 else { + XCTFail() + return + } + guard let account = keystore.addresses?[1] else { + XCTFail() + return + } + guard let key = try? keystore.UNSAFE_getPrivateKeyData(password: "", account: account) else { + XCTFail() + return + } XCTAssertNotNil(key) - print(keystore!.addressStorage.paths) + print(keystore.addressStorage.paths) } func testByBIP32keystoreSaveAndDeriva() throws { let mnemonic = "normal dune pole key case cradle unfold require tornado mercy hospital buyer" - let keystore = try! BIP32Keystore(mnemonics: mnemonic, password: "", mnemonicsPassword: "", prefixPath: "m/44'/60'/0'") + guard let keystore = try? BIP32Keystore(mnemonics: mnemonic, password: "", mnemonicsPassword: "", prefixPath: "m/44'/60'/0'") else { + XCTFail() + return + } XCTAssertNotNil(keystore) - XCTAssertEqual(keystore!.addresses?.count, 1) - try! keystore?.createNewCustomChildAccount(password: "", path: "/0/1") - XCTAssertEqual(keystore?.addresses?.count, 2) - let data = try! keystore?.serialize() - let recreatedStore = BIP32Keystore.init(data!) - XCTAssert(keystore?.addresses?.count == recreatedStore?.addresses?.count) - XCTAssert(keystore?.rootPrefix == recreatedStore?.rootPrefix) - print(keystore!.addresses![0].address) - print(keystore!.addresses![1].address) - print(recreatedStore!.addresses![0].address) + XCTAssertEqual(keystore.addresses?.count, 1) + try? keystore.createNewCustomChildAccount(password: "", path: "/0/1") + XCTAssertEqual(keystore.addresses?.count, 2) + guard let data = try? keystore.serialize() else { + XCTFail() + return + } + let recreatedStore = BIP32Keystore.init(data) + XCTAssert(keystore.addresses?.count == recreatedStore?.addresses?.count) + XCTAssert(keystore.rootPrefix == recreatedStore?.rootPrefix) + guard let firstAddress = keystore.addresses?.first else { + XCTFail() + return + } + guard let recreatedFirstAddress = recreatedStore?.addresses?.first else { + XCTFail() + return + } + print(firstAddress.address) + print(recreatedFirstAddress.address) + XCTAssert(firstAddress == recreatedFirstAddress) + + guard keystore.addresses?.count ?? 0 > 1 && recreatedStore?.addresses?.count ?? 0 > 1 else { + XCTFail() + return + } + + print(keystore.addresses![1].address) print(recreatedStore!.addresses![1].address) - XCTAssert(keystore?.addresses![0] == recreatedStore?.addresses![0]) - XCTAssert(keystore?.addresses![1] == recreatedStore?.addresses![1]) + XCTAssert(keystore.addresses![1] == recreatedStore?.addresses![1]) } // FIXME: Failed on async with 10_000 iterations @@ -207,7 +306,10 @@ class KeystoresTests: LocalTestCase { func testRIPEMD() throws { let data = "message digest".data(using: .ascii) - let hash = try! RIPEMD160.hash(message: data!) + guard let hash = try? RIPEMD160.hash(message: data!) else { + XCTFail() + return + } XCTAssert(hash.toHexString() == "5d0689ef49d2fae572b881b123a85ffa21595f36") } @@ -269,15 +371,21 @@ class KeystoresTests: LocalTestCase { func testKeystoreDerivationTime() throws { let privateKey = Data.randomBytes(length: 32)! measure { - let ks = try! EthereumKeystoreV3(privateKey: privateKey, password: "TEST")! + guard let ks = try? EthereumKeystoreV3(privateKey: privateKey, password: "TEST") else { + XCTFail() + return + } let account = ks.addresses!.first! - _ = try! ks.UNSAFE_getPrivateKeyData(password: "TEST", account: account) + _ = try? ks.UNSAFE_getPrivateKeyData(password: "TEST", account: account) } } func testSingleScryptDerivation() throws { - let privateKey = Data.randomBytes(length: 32)! - _ = try! EthereumKeystoreV3(privateKey: privateKey, password: "TEST")! + guard let privateKey = Data.randomBytes(length: 32) else { + XCTFail() + return + } + _ = try? EthereumKeystoreV3(privateKey: privateKey, password: "TEST") } } From a9b4a46971b2a8a860f3d5d6b3fa1ea7544434e9 Mon Sep 17 00:00:00 2001 From: "pharms.eth" <100330083+pharms-eth@users.noreply.github.com> Date: Sun, 5 Mar 2023 20:28:33 -0800 Subject: [PATCH 09/18] Update EthereumKeystoreV3.swift --- .../KeystoreManager/EthereumKeystoreV3.swift | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Sources/Web3Core/KeystoreManager/EthereumKeystoreV3.swift b/Sources/Web3Core/KeystoreManager/EthereumKeystoreV3.swift index d7b6993a4..d2602637c 100755 --- a/Sources/Web3Core/KeystoreManager/EthereumKeystoreV3.swift +++ b/Sources/Web3Core/KeystoreManager/EthereumKeystoreV3.swift @@ -1,4 +1,3 @@ -// web3swift // // Created by Alex Vlasov. // Copyright © 2018 Alex Vlasov. All rights reserved. @@ -71,7 +70,7 @@ public class EthereumKeystoreV3: AbstractKeystore { } } - public init?(password: String = "web3swift", aesMode: String = "aes-128-cbc") throws { + public init?(password: String, aesMode: String = "aes-128-cbc") throws { guard var newPrivateKey = SECP256K1.generatePrivateKey() else { return nil } @@ -81,7 +80,7 @@ public class EthereumKeystoreV3: AbstractKeystore { try encryptDataToStorage(password, keyData: newPrivateKey, aesMode: aesMode) } - public init?(privateKey: Data, password: String = "web3swift", aesMode: String = "aes-128-cbc") throws { + public init?(privateKey: Data, password: String, aesMode: String = "aes-128-cbc") throws { guard privateKey.count == 32 else { return nil } @@ -95,14 +94,18 @@ public class EthereumKeystoreV3: AbstractKeystore { if keyData == nil { throw AbstractKeystoreError.encryptionError("Encryption without key data") } - let saltLen = 32; - let saltData = Data.randomBytes(length: saltLen)! + let saltLen = 32 + guard let saltData = Data.randomBytes(length: saltLen) else { + throw AbstractKeystoreError.noEntropyError + } guard let derivedKey = scrypt(password: password, salt: saltData, length: dkLen, N: N, R: R, P: P) else { throw AbstractKeystoreError.keyDerivationError } let last16bytes = Data(derivedKey[(derivedKey.count - 16)...(derivedKey.count - 1)]) let encryptionKey = Data(derivedKey[0...15]) - let IV = Data.randomBytes(length: 16)! + guard let IV = Data.randomBytes(length: 16) else { + throw AbstractKeystoreError.noEntropyError + } var aesCipher: AES? switch aesMode { case "aes-128-cbc": From 224490275ab2940e854b9c97210b860824749300 Mon Sep 17 00:00:00 2001 From: "pharms.eth" <100330083+pharms-eth@users.noreply.github.com> Date: Sun, 5 Mar 2023 20:31:50 -0800 Subject: [PATCH 10/18] Update Data+Extension.swift --- Sources/Web3Core/Utility/Data+Extension.swift | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/Sources/Web3Core/Utility/Data+Extension.swift b/Sources/Web3Core/Utility/Data+Extension.swift index bed56dc0c..448728f1a 100755 --- a/Sources/Web3Core/Utility/Data+Extension.swift +++ b/Sources/Web3Core/Utility/Data+Extension.swift @@ -4,7 +4,6 @@ // import Foundation -import Metal extension Data { init(fromArray values: [T]) { @@ -42,23 +41,21 @@ extension Data { } public static func randomBytes(length: Int) -> Data? { - let entropy_bit_size = length//128 - //# valid_entropy_bit_sizes = [128, 160, 192, 224, 256], count: [12, 15, 18, 21, 24] - var entropy_bytes = [UInt8](repeating: 0, count: entropy_bit_size)// / 8) - let status = SecRandomCopyBytes(kSecRandomDefault, entropy_bytes.count, &entropy_bytes) - - if status != errSecSuccess { // Always test the status. - } else { - entropy_bytes = [UInt8](repeating: 0, count: entropy_bit_size)// / 8) - arc4random_buf(&entropy_bytes, entropy_bytes.count) - } - - let source1 = MTLCreateSystemDefaultDevice()?.makeBuffer(length: length)?.hash.description.data(using: .utf8) - - let entropyData = entropy_bytes.shuffled().map{ bit in - return bit ^ (source1?.randomElement() ?? 0) + for _ in 0...1024 { + var data = Data(repeating: 0, count: length) + let result = data.withUnsafeMutableBytes { (body: UnsafeMutableRawBufferPointer) -> Int32? in + if let bodyAddress = body.baseAddress, body.count > 0 { + let pointer = bodyAddress.assumingMemoryBound(to: UInt8.self) + return SecRandomCopyBytes(kSecRandomDefault, length, pointer) + } else { + return nil + } + } + if let notNilResult = result, notNilResult == errSecSuccess { + return data + } } - return Data(entropyData) + return nil } public func bitsInRange(_ startingBit: Int, _ length: Int) -> UInt64? { // return max of 8 bytes for simplicity, non-public From 4108eddb558b931eafbefc1e6f15d5e50955356d Mon Sep 17 00:00:00 2001 From: "pharms.eth" <100330083+pharms-eth@users.noreply.github.com> Date: Sun, 5 Mar 2023 20:36:18 -0800 Subject: [PATCH 11/18] Update Encodable+Extensions.swift --- Sources/Web3Core/Utility/Encodable+Extensions.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/Sources/Web3Core/Utility/Encodable+Extensions.swift b/Sources/Web3Core/Utility/Encodable+Extensions.swift index e43a39b7f..1981636e6 100644 --- a/Sources/Web3Core/Utility/Encodable+Extensions.swift +++ b/Sources/Web3Core/Utility/Encodable+Extensions.swift @@ -63,5 +63,3 @@ public extension EncodableToHex where Self: BinaryInteger { extension BigUInt: EncodableToHex { } extension UInt: EncodableToHex { } - -extension Int: EncodableToHex { } From 0847410c2ade515f7771660c42252a837ce316a7 Mon Sep 17 00:00:00 2001 From: "pharms.eth" <100330083+pharms-eth@users.noreply.github.com> Date: Sun, 5 Mar 2023 20:36:22 -0800 Subject: [PATCH 12/18] Update String+Extension.swift --- .../Web3Core/Utility/String+Extension.swift | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/Sources/Web3Core/Utility/String+Extension.swift b/Sources/Web3Core/Utility/String+Extension.swift index 0ea3aaaed..dbe0a10ca 100755 --- a/Sources/Web3Core/Utility/String+Extension.swift +++ b/Sources/Web3Core/Utility/String+Extension.swift @@ -32,26 +32,6 @@ extension String { return output } - /// Splits a string into groups of `every` n characters, grouping from left-to-right by default. If `backwards` is true, right-to-left. - public func split(every: Int, backwards: Bool = false) -> [String] { - var result = [String]() - - for i in stride(from: 0, to: self.count, by: every) { - switch backwards { - case true: - let endIndex = self.index(self.endIndex, offsetBy: -i) - let startIndex = self.index(endIndex, offsetBy: -every, limitedBy: self.startIndex) ?? self.startIndex - result.insert(String(self[startIndex..) -> String { let start = index(self.startIndex, offsetBy: bounds.lowerBound) let end = index(self.startIndex, offsetBy: bounds.upperBound) From dd93a0c040bf3b21f7363030e6d37e691aa1b1f0 Mon Sep 17 00:00:00 2001 From: "pharms.eth" <100330083+pharms-eth@users.noreply.github.com> Date: Sun, 5 Mar 2023 20:36:25 -0800 Subject: [PATCH 13/18] Update WriteOperation.swift --- .../web3swift/Operations/WriteOperation.swift | 43 ------------------- 1 file changed, 43 deletions(-) diff --git a/Sources/web3swift/Operations/WriteOperation.swift b/Sources/web3swift/Operations/WriteOperation.swift index 5c54b6cff..10fd972cb 100755 --- a/Sources/web3swift/Operations/WriteOperation.swift +++ b/Sources/web3swift/Operations/WriteOperation.swift @@ -42,47 +42,4 @@ public class WriteOperation: ReadOperation { guard let transactionData = transaction.encode(for: .transaction) else { throw Web3Error.dataError } return try await web3.eth.send(raw: transactionData) } - - public func depploy(password: String, policies: Policies = .auto) async throws -> TransactionSendingResult { - //TODO: optimize/cleanup -// try await transaction.resolve(provider: web3.provider) - try await policyResolver.resolveAll(for: &transaction, with: policies) - - guard let attachedKeystoreManager = self.web3.provider.attachedKeystoreManager else { - throw Web3Error.inputError(desc: "Failed to locally sign a transaction") - } - - do { - //TODO: optimize/cleanup -// transaction.unsign() - let account = transaction.from ?? transaction.sender ?? EthereumAddress.contractDeploymentAddress() - var privateKey = try attachedKeystoreManager.UNSAFE_getPrivateKeyData(password: password, account: account) - defer { Data.zero(&privateKey) } - try transaction.sign(privateKey: privateKey, useExtraEntropy: false) - } catch { - throw Web3Error.inputError(desc: "Failed to locally sign a transaction") - } - - //TODO: optimize/cleanup - guard let transactionData = transaction.encode(for: .transaction) else { throw Web3Error.dataError } -// let vectorHash = transaction.hash!.toHexString().addHexPrefix() -// print(vectorHash) - - //TODO: optimize/cleanup - return try await web3.eth.send(raw: transactionData) - } - - // FIXME: Rewrite this to CodableTransaction -// func nonce(for policy: CodableTransaction.NoncePolicy, from: EthereumAddress) async throws -> BigUInt { -// switch policy { -// case .latest: -// return try await self.web3.eth.getTransactionCount(for: from, onBlock: .latest) -// case .pending: -// return try await self.web3.eth.getTransactionCount(for: from, onBlock: .pending) -// case .earliest: -// return try await self.web3.eth.getTransactionCount(for: from, onBlock: .earliest) -// case .exact(let nonce): -// return nonce -// } -// } } From c3abf9323a3af712b76e949faa930e1cbc691b6b Mon Sep 17 00:00:00 2001 From: Jenea Vranceanu <36865532+JeneaVranceanu@users.noreply.github.com> Date: Thu, 9 Mar 2023 09:51:30 +0200 Subject: [PATCH 14/18] chore: fixed spacing; KeystoreParamsV3 init - removed local names of arguments; --- .../KeystoreManager/KeystoreParams.swift | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Sources/Web3Core/KeystoreManager/KeystoreParams.swift b/Sources/Web3Core/KeystoreManager/KeystoreParams.swift index 07dfb9d2a..a4729d112 100644 --- a/Sources/Web3Core/KeystoreManager/KeystoreParams.swift +++ b/Sources/Web3Core/KeystoreManager/KeystoreParams.swift @@ -12,6 +12,7 @@ public struct KdfParamsV3: Decodable, Encodable { var r: Int? var c: Int? var prf: String? + public init(salt: String, dklen: Int, n: Int? = nil, p: Int? = nil, r: Int? = nil, c: Int? = nil, prf: String? = nil) { self.salt = salt self.dklen = dklen @@ -25,6 +26,7 @@ public struct KdfParamsV3: Decodable, Encodable { public struct CipherParamsV3: Decodable, Encodable { var iv: String + public init(iv: String) { self.iv = iv } @@ -38,6 +40,7 @@ public struct CryptoParamsV3: Decodable, Encodable { var kdfparams: KdfParamsV3 var mac: String var version: String? + public init(ciphertext: String, cipher: String, cipherparams: CipherParamsV3, kdf: String, kdfparams: KdfParamsV3, mac: String, version: String? = nil) { self.ciphertext = ciphertext self.cipher = cipher @@ -54,12 +57,12 @@ public protocol AbstractKeystoreParams: Codable { var id: String? { get } var version: Int { get } var isHDWallet: Bool { get } - } public struct PathAddressPair: Codable { public let path: String public let address: String + public init(path: String, address: String) { self.path = path self.address = address @@ -97,23 +100,20 @@ public struct KeystoreParamsBIP32: AbstractKeystoreParams { self.rootPath = rootPath self.isHDWallet = true } - } public struct KeystoreParamsV3: AbstractKeystoreParams { + public var address: String? public var crypto: CryptoParamsV3 public var id: String? public var version: Int public var isHDWallet: Bool - public var address: String? - - public init(address ad: String?, crypto cr: CryptoParamsV3, id i: String, version ver: Int) { - address = ad - self.crypto = cr - self.id = i - self.version = ver + public init(address: String?, crypto: CryptoParamsV3, id: String, version: Int) { + self.address = address + self.crypto = crypto + self.id = id + self.version = version self.isHDWallet = false } - } From 95214d9c517c1845c71ac6f8a544f41fa88f4d19 Mon Sep 17 00:00:00 2001 From: Jenea Vranceanu <36865532+JeneaVranceanu@users.noreply.github.com> Date: Thu, 9 Mar 2023 10:14:39 +0200 Subject: [PATCH 15/18] chore: removed pathToAddress; replaced En/Decodable with Codable --- .../KeystoreManager/KeystoreParams.swift | 20 +++---------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/Sources/Web3Core/KeystoreManager/KeystoreParams.swift b/Sources/Web3Core/KeystoreManager/KeystoreParams.swift index a4729d112..4d6667720 100644 --- a/Sources/Web3Core/KeystoreManager/KeystoreParams.swift +++ b/Sources/Web3Core/KeystoreManager/KeystoreParams.swift @@ -4,7 +4,7 @@ import Foundation -public struct KdfParamsV3: Decodable, Encodable { +public struct KdfParamsV3: Codable { var salt: String var dklen: Int var n: Int? @@ -24,7 +24,7 @@ public struct KdfParamsV3: Decodable, Encodable { } } -public struct CipherParamsV3: Decodable, Encodable { +public struct CipherParamsV3: Codable { var iv: String public init(iv: String) { @@ -32,7 +32,7 @@ public struct CipherParamsV3: Decodable, Encodable { } } -public struct CryptoParamsV3: Decodable, Encodable { +public struct CryptoParamsV3: Codable { var ciphertext: String var cipher: String var cipherparams: CipherParamsV3 @@ -75,20 +75,6 @@ public struct KeystoreParamsBIP32: AbstractKeystoreParams { public var version: Int public var isHDWallet: Bool - @available(*, deprecated, message: "Please use pathAddressPairs instead") - var pathToAddress: [String: String] { - get { - return self.pathAddressPairs.reduce(into: [String: String]()) { - $0[$1.path] = $1.address - } - } - set { - for pair in newValue { - self.pathAddressPairs.append(PathAddressPair(path: pair.0, address: pair.1)) - } - } - } - public var pathAddressPairs: [PathAddressPair] var rootPath: String? From 4ff3f356eb69c193716755ee9fc55e847d84866f Mon Sep 17 00:00:00 2001 From: Jenea Vranceanu <36865532+JeneaVranceanu@users.noreply.github.com> Date: Thu, 9 Mar 2023 10:25:52 +0200 Subject: [PATCH 16/18] chore: fixed trailing whitespaces --- Sources/Web3Core/KeystoreManager/KeystoreParams.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/Web3Core/KeystoreManager/KeystoreParams.swift b/Sources/Web3Core/KeystoreManager/KeystoreParams.swift index 4d6667720..c6de1ae24 100644 --- a/Sources/Web3Core/KeystoreManager/KeystoreParams.swift +++ b/Sources/Web3Core/KeystoreManager/KeystoreParams.swift @@ -74,10 +74,10 @@ public struct KeystoreParamsBIP32: AbstractKeystoreParams { public var id: String? public var version: Int public var isHDWallet: Bool - + public var pathAddressPairs: [PathAddressPair] var rootPath: String? - + public init(crypto cr: CryptoParamsV3, id i: String, version ver: Int = 32, rootPath: String? = nil) { self.crypto = cr self.id = i @@ -94,7 +94,7 @@ public struct KeystoreParamsV3: AbstractKeystoreParams { public var id: String? public var version: Int public var isHDWallet: Bool - + public init(address: String?, crypto: CryptoParamsV3, id: String, version: Int) { self.address = address self.crypto = crypto From c8827b6f41d395a12a69d806589610b2c0a6df89 Mon Sep 17 00:00:00 2001 From: Jenea Vranceanu <36865532+JeneaVranceanu@users.noreply.github.com> Date: Thu, 9 Mar 2023 10:43:27 +0200 Subject: [PATCH 17/18] chore: ran swiftlint --fix --- .../Web3Core/KeystoreManager/KeystoreParams.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Sources/Web3Core/KeystoreManager/KeystoreParams.swift b/Sources/Web3Core/KeystoreManager/KeystoreParams.swift index c6de1ae24..2b1747a03 100644 --- a/Sources/Web3Core/KeystoreManager/KeystoreParams.swift +++ b/Sources/Web3Core/KeystoreManager/KeystoreParams.swift @@ -12,7 +12,7 @@ public struct KdfParamsV3: Codable { var r: Int? var c: Int? var prf: String? - + public init(salt: String, dklen: Int, n: Int? = nil, p: Int? = nil, r: Int? = nil, c: Int? = nil, prf: String? = nil) { self.salt = salt self.dklen = dklen @@ -26,7 +26,7 @@ public struct KdfParamsV3: Codable { public struct CipherParamsV3: Codable { var iv: String - + public init(iv: String) { self.iv = iv } @@ -40,7 +40,7 @@ public struct CryptoParamsV3: Codable { var kdfparams: KdfParamsV3 var mac: String var version: String? - + public init(ciphertext: String, cipher: String, cipherparams: CipherParamsV3, kdf: String, kdfparams: KdfParamsV3, mac: String, version: String? = nil) { self.ciphertext = ciphertext self.cipher = cipher @@ -62,7 +62,7 @@ public protocol AbstractKeystoreParams: Codable { public struct PathAddressPair: Codable { public let path: String public let address: String - + public init(path: String, address: String) { self.path = path self.address = address @@ -74,10 +74,10 @@ public struct KeystoreParamsBIP32: AbstractKeystoreParams { public var id: String? public var version: Int public var isHDWallet: Bool - + public var pathAddressPairs: [PathAddressPair] var rootPath: String? - + public init(crypto cr: CryptoParamsV3, id i: String, version ver: Int = 32, rootPath: String? = nil) { self.crypto = cr self.id = i @@ -94,7 +94,7 @@ public struct KeystoreParamsV3: AbstractKeystoreParams { public var id: String? public var version: Int public var isHDWallet: Bool - + public init(address: String?, crypto: CryptoParamsV3, id: String, version: Int) { self.address = address self.crypto = crypto From 62b74376a2fcf8e30a8326a69f1368a13dfbc18a Mon Sep 17 00:00:00 2001 From: Jenea Vranceanu Date: Sun, 2 Apr 2023 14:03:54 +0300 Subject: [PATCH 18/18] fix: marked pathAddressPairs as public internal(set) var --- Sources/Web3Core/KeystoreManager/KeystoreParams.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Web3Core/KeystoreManager/KeystoreParams.swift b/Sources/Web3Core/KeystoreManager/KeystoreParams.swift index 2b1747a03..9eaa047c7 100644 --- a/Sources/Web3Core/KeystoreManager/KeystoreParams.swift +++ b/Sources/Web3Core/KeystoreManager/KeystoreParams.swift @@ -75,7 +75,7 @@ public struct KeystoreParamsBIP32: AbstractKeystoreParams { public var version: Int public var isHDWallet: Bool - public var pathAddressPairs: [PathAddressPair] + public internal(set) var pathAddressPairs: [PathAddressPair] var rootPath: String? public init(crypto cr: CryptoParamsV3, id i: String, version ver: Int = 32, rootPath: String? = nil) {