From b581261737a16ef06d6eba6bd768cb0a3024ca35 Mon Sep 17 00:00:00 2001 From: Swen van Zanten Date: Thu, 7 Mar 2019 23:48:54 +0100 Subject: [PATCH] Add stealth address sending support --- VergeiOS.xcodeproj/project.pbxproj | 12 +++++-- VergeiOS/Extensions/Array+Extensions.swift | 23 ++++++++++++++ VergeiOS/Http/TorClient.swift | 4 +++ VergeiOS/Models/TxOutput.swift | 2 ++ VergeiOS/Wallet/TxTransponder.swift | 2 ++ VergeiOS/Wallet/WalletClient.swift | 37 ++++++++++++++-------- 6 files changed, 64 insertions(+), 16 deletions(-) create mode 100644 VergeiOS/Extensions/Array+Extensions.swift diff --git a/VergeiOS.xcodeproj/project.pbxproj b/VergeiOS.xcodeproj/project.pbxproj index be6702bf..37729140 100644 --- a/VergeiOS.xcodeproj/project.pbxproj +++ b/VergeiOS.xcodeproj/project.pbxproj @@ -89,6 +89,7 @@ A33B759E21A5CDD400030CCF /* BitcoinKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A33B759D21A5CDD400030CCF /* BitcoinKit.framework */; }; A33B75C021A61A3500030CCF /* CurrencyInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = A33B75BF21A61A3500030CCF /* CurrencyInput.swift */; }; A33B75C421A8917700030CCF /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A33B75C321A8917700030CCF /* Settings.storyboard */; }; + A33DB6952231D2010070DBCE /* Array+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A33DB6942231D2010070DBCE /* Array+Extensions.swift */; }; A33F6A7020EEE485001492C2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A33F6A6F20EEE485001492C2 /* AppDelegate.swift */; }; A33F6A7220EEE485001492C2 /* WelcomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A33F6A7120EEE485001492C2 /* WelcomeViewController.swift */; }; A33F6A7520EEE485001492C2 /* WelcomeGuide.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A33F6A7320EEE485001492C2 /* WelcomeGuide.storyboard */; }; @@ -417,6 +418,7 @@ A33B759D21A5CDD400030CCF /* BitcoinKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = BitcoinKit.framework; path = Carthage/Build/iOS/BitcoinKit.framework; sourceTree = ""; }; A33B75BF21A61A3500030CCF /* CurrencyInput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrencyInput.swift; sourceTree = ""; }; A33B75C321A8917700030CCF /* Settings.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Settings.storyboard; sourceTree = ""; }; + A33DB6942231D2010070DBCE /* Array+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Extensions.swift"; sourceTree = ""; }; A33F6A6C20EEE485001492C2 /* VergeiOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = VergeiOS.app; sourceTree = BUILT_PRODUCTS_DIR; }; A33F6A6F20EEE485001492C2 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; A33F6A7120EEE485001492C2 /* WelcomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeViewController.swift; sourceTree = ""; }; @@ -680,6 +682,7 @@ isa = PBXGroup; children = ( B2BD7F498F12AE2C1C892E66 /* AppDelegate+NotificationCenter.swift */, + A33DB6942231D2010070DBCE /* Array+Extensions.swift */, B2BD74E5640073CDD7246E24 /* Collection+Extensions.swift */, B2BD7E6BDB05995D9DB9E0D0 /* Data+Extensions.swift */, A3408E5721768AF4009F0C9D /* Date+Extensions.swift */, @@ -689,8 +692,8 @@ A36F8A882146EE0F00304777 /* String+Subs.swift */, B2BD7945C26A344BBFEAB261 /* UIAlertController+Statics.swift */, A302D34C21260F1C00EA8091 /* UIScrollView+Pagination.swift */, - A398B20E212B2886004ACB73 /* UIWindow+VisibleViewController.swift */, CABF1481221061FD00F92CBC /* UIView+Actions.swift */, + A398B20E212B2886004ACB73 /* UIWindow+VisibleViewController.swift */, ); path = Extensions; sourceTree = ""; @@ -1674,6 +1677,7 @@ A305DDB22107A1F100911B64 /* PinKeyboard.swift in Sources */, A316B0AF215454FB00FF5200 /* AddressValidator.swift in Sources */, A305DDBA2107BB8B00911B64 /* KeyboardDelegate.swift in Sources */, + A33DB6952231D2010070DBCE /* Array+Extensions.swift in Sources */, A338E6B72110D5D4000D44EE /* TransactionsWalletSlideView.swift in Sources */, A302D34321220A1E00EA8091 /* Notification+Names.swift in Sources */, A338E6A2210DD893000D44EE /* FinishSetupViewController.swift in Sources */, @@ -1946,6 +1950,7 @@ isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; + CF_BUNDLE_SHORT_VERSION_STRING = 0.17.1; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = S7743EWR3C; INFOPLIST_FILE = VergeiOSTests/Info.plist; @@ -1966,6 +1971,7 @@ isa = XCBuildConfiguration; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; + CF_BUNDLE_SHORT_VERSION_STRING = 0.17.1; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = S7743EWR3C; INFOPLIST_FILE = VergeiOSTests/Info.plist; @@ -2024,7 +2030,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - CF_BUNDLE_SHORT_VERSION_STRING = 0.17; + CF_BUNDLE_SHORT_VERSION_STRING = 0.17.1; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; @@ -2087,7 +2093,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - CF_BUNDLE_SHORT_VERSION_STRING = 0.17; + CF_BUNDLE_SHORT_VERSION_STRING = 0.17.1; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; diff --git a/VergeiOS/Extensions/Array+Extensions.swift b/VergeiOS/Extensions/Array+Extensions.swift new file mode 100644 index 00000000..91f2eef9 --- /dev/null +++ b/VergeiOS/Extensions/Array+Extensions.swift @@ -0,0 +1,23 @@ +// +// Array+Extensions.swift +// VergeiOS +// +// Created by Swen van Zanten on 07/03/2019. +// Copyright © 2019 Verge Currency. All rights reserved. +// + +import Foundation + +extension Array { + func sortByIndices(indices: [Int]) -> Array { + var array: Array = [] + + for index in indices { + if self.indices.contains(index) { + array.append(self[index]) + } + } + + return array + } +} diff --git a/VergeiOS/Http/TorClient.swift b/VergeiOS/Http/TorClient.swift index 8eaf54a0..43ca39ca 100644 --- a/VergeiOS/Http/TorClient.swift +++ b/VergeiOS/Http/TorClient.swift @@ -87,6 +87,10 @@ class TorClient { func restart() { resign() + if !isOperational { + return + } + while controller.isConnected { print("Disconnecting Tor...") } diff --git a/VergeiOS/Models/TxOutput.swift b/VergeiOS/Models/TxOutput.swift index 469b0a43..25f01478 100644 --- a/VergeiOS/Models/TxOutput.swift +++ b/VergeiOS/Models/TxOutput.swift @@ -11,6 +11,8 @@ public struct TxOutput: Decodable { public let message: String? public let encryptedMessage: String? public let toAddress: String + public let ephemeralPrivKey: String? + public let stealth: Bool? } diff --git a/VergeiOS/Wallet/TxTransponder.swift b/VergeiOS/Wallet/TxTransponder.swift index af6c6f13..543a419c 100644 --- a/VergeiOS/Wallet/TxTransponder.swift +++ b/VergeiOS/Wallet/TxTransponder.swift @@ -30,6 +30,8 @@ class TxTransponder { public func send(txp: TxProposalResponse, completion: @escaping completionType) { self.completion = completion + self.step = .publish + self.previousTxp = nil // Publish the tx proposal and start the sequence. walletClient.publishTxProposal(txp: txp, completion: completionHandler) diff --git a/VergeiOS/Wallet/WalletClient.swift b/VergeiOS/Wallet/WalletClient.swift index 02ee5483..4387b1c0 100644 --- a/VergeiOS/Wallet/WalletClient.swift +++ b/VergeiOS/Wallet/WalletClient.swift @@ -16,6 +16,7 @@ public class WalletClient { case invalidMessageData(message: String) case invalidWidHex(id: String) case invalidAddressReceived(address: AddressInfo?) + case noOutputFound() } public static let shared = WalletClient( @@ -609,8 +610,12 @@ public class WalletClient { } private func getUnsignedTx(txp: TxProposalResponse) throws -> UnsignedTransaction { + guard let output = txp.outputs.first else { + throw WalletClientError.noOutputFound() + } + let changeAddress: Address = try AddressFactory.create(txp.changeAddress.address) - let toAddress: Address = try getToAddress(address: txp.outputs.first!.toAddress) + let toAddress: Address = try AddressFactory.create(output.toAddress) let unspentOutputs = txp.inputs let unspentTransactions: [UnspentTransaction] = try unspentOutputs.map { output in @@ -637,11 +642,25 @@ public class WalletClient { var outputs = [toOutput] if change > 0 { - outputs = [toOutput, changeOutput] - if txp.outputOrder == [1, 0] { - outputs = [changeOutput, toOutput] - } + outputs.append(changeOutput) + } + + if output.stealth == true { + let ephemeral = PrivateKey( + data: Data(hex: output.ephemeralPrivKey!), + network: .mainnetXVG, + isPublicKeyCompressed: true + ) + + let opReturnMeta = try Script() + .append(.OP_RETURN) + .appendData(ephemeral.publicKey().data) + + let opReturnOutput = TransactionOutput(value: 0, lockingScript: opReturnMeta.data) + outputs.insert(opReturnOutput, at: 0) } + + outputs = outputs.sortByIndices(indices: txp.outputOrder.map { Int($0) }) let tx = Transaction( version: 1, @@ -692,12 +711,4 @@ public class WalletClient { return hexes } - - private func getToAddress(address: String) throws -> Address { - do { - return try AddressFactory.create(address) - } catch { - return try StealthAddress(address) - } - } }