Commit 74833e38 authored by Jü's avatar

Merge branch 'fix/issue-21' into 'development'

Fix: issue#22 OnboardingServiceTests and String-ExtenstionTest fail

See merge request rusticbison/xwallet!5
parents 9a01f076 45ee0ec1
......@@ -8,4 +8,5 @@ DerivedData/
*.xcuserstate
Breakpoints_v2.xcbkptlist
.vscode
*/xcuserdata/*
\ No newline at end of file
**/xcuserdata/*
.idea
\ No newline at end of file
v1.0
A six digit PIN is used to control access to your X Wallet.
A 25 word seed mnemonic is generated when you initialize your X Wallet. This seed is compatible with the Monero reference client software.
View your balance and transaction history.
Send Monero with an optional Payment ID, for transfers to exchanges and other account-based services. Send via QR code scan or by copying and pasting an address.
Receive Monero via QR code or by copying and pasting your wallet address to a messaging app.
v1.1
Connect to the remote node of your choice.
Quickly delete all application data.
Change your PIN
View your seed mnemonic
v1.2
Added fiat conversion reference (CHF, EUR, GBP, USD)
Upgraded default remote node hardware: 16GB/6vCPU
Upgraded libraries to Monero v0.12
Built for iOS 11.3
v1.3
Send now notifies you if you try to send more than your balance.
v1.4
Modified the previous notification.
v1.5
Send now displays the estimated fee and computes the total amount you can send.
v1.6
Added support for all available fiat currencies.
Added support for QR codes containing both payment ID and amount.
\ No newline at end of file
Privacy Statement:
We don’t request or collect any data from you. We work hard to create software which prevents anyone, including us, from being able to collect any data about you.
......@@ -8,29 +8,7 @@
The X Wallet is a native iOS interface for the experimental digital currency Monero. The X Wallet's goal is to deliver the benefits of the Monero protocol without compromise.
v1.0
* A 6 digit PIN is used to control access to your X Wallet.
* A 25 word seed mnemonic is generated when you initialize your X Wallet. This seed is compatible with the [Monero reference client software](https://github.com/monero-project/monero).
* View your balance and transaction history.
* Send Monero with an optional Payment ID, for transfers to exchanges and other account-based services. Send via QR code scan or by copying and pasting an address.
* Receive Monero via QR code or by copying and pasting your wallet address to a messaging app.
v1.1
* Connect to the remote node of your choice.
* Quickly delete all application data.
* Change your PIN
* View your seed mnemonic
v1.2
* Added fiat conversion reference (CHF, EUR, GBP, USD)
* Upgraded default remote node hardware: 16GB/6vCPU
* Upgraded libraries to Monero v0.12
* Built for iOS 11.3
The X Wallet should be treated as experimental software. Read and follow these instructions carefully, and **use this software at your own risk**. Before using this software, you should also carefully read and agree to the [privacy policy](https://gitlab.com/rusticbison/xwallet.tech/blob/master/PRIVACY.md).
The X Wallet should be treated as experimental software. Read and follow these instructions carefully, and **use this software at your own risk**.
## Standard installation and initialization procedure
......@@ -139,7 +117,7 @@ The Options View is accessed from the top right corner of the wallet view.
## Troubleshooting
#### I try to create a transfer, but the transfer summary shows a network fee of "0.00000". That sounds too good to be true. When I try to confirm the transfer, it fails. What is going on?
This is a known issue which we're working on. Your X Wallet's "Send" feature should still be locked, as it is actually not yet synced with the Monero network and therefore the network fee cannot be calculated. You might want to wait an hour or more before trying to "Send" Monero. After your wallet is synchronized with the network, you will see the estimated network fee has been calculated, and your transfer can be sent.
This issue was resolved in v1.2. Please upgrade your X Wallet to the latest version.
#### I just installed the X Wallet, and it’s been syncing for a long time. Now it seems stuck at 99%. What should I do?
......@@ -156,8 +134,12 @@ Don't panic. First make sure:
- You X Wallet has permission to access the network. If you do not have a wifi connection, make sure your X Wallet has permission to use the cellular network.
- Your VPN is functioning properly, if applicable.
- The transfer was actually sent. You may ask the sender for the transaction ID, your address, and the transaction key so that [they may prove the transaction was sent](https://getmonero.org/resources/user-guides/prove-payment.html).
- Make sure the remote node you are connected to is functioning properly.
Depending on network conditions, it may take several minutes for the transaction to appear.
⚠️ On startup, the X Wallet connects to a random remote node from https://moneroworld.com/. It is possible, even likely, the node you connect to will have very poor performance. *Poor performing nodes will prevent you from seeing any new transfers you may have received and from broadcasting new transfers. You can 1) close and restart the X Wallet to force a new connection to a hopefully better quality node, or 2) connect to your own remote node where you are in control of the hardware.* You can even buy pre-built remote nodes now, from vendors like https://icryptonode.com/ (we have no affiliation).
Depending on network conditions and the hardware performance of the node you are connected to, it may take several minutes for newly received transactions to appear.
Please note: if you have sent Monero to your X Wallet from a custodial software application and you do not see the funds appear in your X Wallet, please check your transfer details against a block explorer to ensure that the transfer actually occurred. Custodial wallets are often designed to steal cryptocurrency.
......@@ -218,7 +200,7 @@ Your keys are generated on your iOS device, and they never leave your device.
#### How do you generate the seed mnemonic?
The X Wallet uses the Monero reference client's methods (Monero RPC) to generate the seed mnemonic.
The X Wallet uses the Monero reference client's methods (Monero RPC) to generate the seed mnemonic in a sandboxed environment on your iOS device.
#### Is the X Wallet associated with the Monero project?
......@@ -226,7 +208,7 @@ No. The X Wallet is developed and offered by XMR Systems LLC. Neither the X Wall
#### Is 32 bit hardware supported?
The X Wallet is built for 64 bit architecture running iOS 11 and later.
The X Wallet is built for 64 bit architecture.
#### If I restore from my seed mnemonic, I will have full control over my Monero. Will my transaction history also be restored?
......@@ -238,19 +220,21 @@ If you use your X Wallet frequently, it should synchronize relatively fast. The
#### Are there any privacy issues I should be concerned with?
Your X Wallet connects to a default Monero remote node, which maintains a copy of the Monero blockchain. Remote nodes can't learn about your Monero activities, but they can see your IP address, as well as the timing and frequency with which you connect. Malicious third parties will track and analyze this data. Use a VPN to protect your privacy, and connect to a remote node which you control.
Your X Wallet connects to a random Monero remote node, which maintains a copy of the Monero blockchain. Remote nodes can't learn about your Monero activities, but they can see your IP address, as well as the timing and frequency with which you connect. Malicious third parties will track and analyze this data. Use a VPN to protect your privacy, and connect to a remote node which you control.
#### How do you fund development?
For the foreseeable future we need to operate off of donations. Funds are used to pay for programmer time, infrastructure costs, and related overhead. If you are not able to download the app to make a donation and you would still like to support the project, you can donate to development using this address:
Donations. Funds are used to pay ridiculously low wages for programming effort. You can donate to development using this address:
`48u79gBhhdo6Pts6daXfvn7fQ2QL9BhaqNfqTgzbgGu5fJVaX7zjTVjNXaHtj71w3y81cc9vcuH7rNiz37BC9hQuUKEcoiU`
`44r3NncNrigGfoTuyiWv461PXxEcYaRrVLKzYRTTCPKhQMbnVNjufmkQp2zCVZrJA7RLpA3BrWQUCgLtBaPwbm7bUt54ePj`
## Contact
[email protected]
Justin Smith
[@rusticbison](https://twitter.com/@rusticbison)
[email protected]
## Credits
#### Core Team in Zürich🇨🇭
#### Team in Zürich🇨🇭
* Programmer: [Jürgen](http://www.22of8.ch)
* Designer: [Rizvi](http://www.designeese.ch/)
......@@ -258,11 +242,6 @@ [email protected]
* Sysops: [Eduardo](https://ebalsa.org/)
* x86_64 and arm64 builds: [Dan](https://www.linkedin.com/in/dkoio/)
#### Special thanks regarding v1.1.0.1
* [stroborobo](https://github.com/stroborobo) for code review
* /u/TurbalOilk for first pointing out the nsuserdefaults vulnerability
## Reference
* [X Wallet Marketing Webpage](https://xwallet.tech/)
* [X Wallet Marketing Webpage Source](https://gitlab.com/rusticbison/website-xwallet)
* [Privacy policy](https://gitlab.com/rusticbison/website-xwallet/blob/master/PRIVACY.md)
* [X Wallet webpage](https://xwallet.tech/)
* [X Wallet webpage source](https://gitlab.com/rusticbison/website-xwallet)
//
// ApplicationContext.swift
// XWallet
//
// Created by loj on 29.07.18.
//
import Foundation
public enum ApplicationContextTag: String {
case requestId = "requestId"
case qrcImage = "qrcImage"
}
{
"images" : [
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : "<=145"
},
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : ">145"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"assets" : [
{
"idiom" : "watch",
"filename" : "Circular.imageset",
"role" : "circular"
},
{
"idiom" : "watch",
"filename" : "Extra Large.imageset",
"role" : "extra-large"
},
{
"idiom" : "watch",
"filename" : "Modular.imageset",
"role" : "modular"
},
{
"idiom" : "watch",
"filename" : "Utilitarian.imageset",
"role" : "utilitarian"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"images" : [
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : "<=145"
},
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : ">145"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"images" : [
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : "<=145"
},
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : ">145"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"images" : [
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : "<=145"
},
{
"idiom" : "watch",
"scale" : "2x",
"screen-width" : ">145"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
//
// ExtensionDelegate.swift
// XWallet Watchkit App Extension
//
// Created by loj on 15.07.18.
//
import WatchKit
class ExtensionDelegate: NSObject, WKExtensionDelegate {
private var notificationService: NotificationServiceProtocol!
private var communicationService: CommunicationServiceProtocol!
func applicationDidFinishLaunching() {
// Perform any final initialization of your application.
self.setup()
}
func applicationDidBecomeActive() {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillResignActive() {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, etc.
}
private func setup() {
self.notificationService = NotificationService()
self.communicationService = CommunicationServiceProvider.current()
self.notificationService.register(callbackForRequestId: { (requestId: String) in
self.handleNotification(requestId: requestId)
})
self.communicationService.register(receiveRequestIdHandler: { (requestId: String) in
self.handleCommunication(requestId: requestId)
})
self.communicationService.register(receiveQrcHandler: { (qrcImage: UIImage) in
self.handleCommunication(qrcImage: qrcImage)
})
}
private func handleNotification(requestId: String) {
self.communicationService.authenticate(requestId: requestId)
}
private func handleCommunication(requestId: String) {
if WKExtension.shared().applicationState == .background {
print("*** watch: is in background")
self.notificationService.schedule(requestId: requestId)
} else {
print("*** watch: is in foreground")
let context = [ApplicationContextTag.requestId.rawValue:requestId]
DispatchQueue.main.async {
WKInterfaceController.reloadRootPageControllers(
withNames: ["MainView"],
contexts: [context],
orientation: WKPageOrientation.vertical,
pageIndex: 0)
}
}
}
private func handleCommunication(qrcImage: UIImage) {
if WKExtension.shared().applicationState == .background {
print("*** watch: is in background")
// self.notificationService.schedule(requestId: requestId)
} else {
print("*** watch: is in foreground")
let context = [ApplicationContextTag.qrcImage.rawValue:qrcImage]
DispatchQueue.main.async {
WKInterfaceController.reloadRootPageControllers(
withNames: ["MainView"],
contexts: [context],
orientation: WKPageOrientation.vertical,
pageIndex: 0)
}
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>XWallet Watchkit App Extension</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>1.9</string>
<key>CFBundleVersion</key>
<string>1.9.0.1</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>WKAppBundleIdentifier</key>
<string>com.XMRSystemsLLC.XWallet.watchkitapp</string>
</dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.watchkit</string>
</dict>
<key>WKExtensionDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).ExtensionDelegate</string>
</dict>
</plist>
//
// InterfaceController.swift
// XWallet Watchkit App Extension
//
// Created by loj on 15.07.18.
//
import Foundation
import WatchKit
class InterfaceController: WKInterfaceController {
@IBOutlet weak var label: WKInterfaceLabel!
@IBOutlet weak var authenticateButton: WKInterfaceButton!
@IBOutlet weak var qrcImage: WKInterfaceImage!
@IBAction func authenticateButtonTouched() {
if let requestId = self.requestId {
self.communicationService.authenticate(requestId: requestId)
self.requestId = nil
}
self.updateControls(authenticate: false)
}
private var communicationService: CommunicationServiceProtocol!
private var requestId: String?
override func awake(withContext context: Any?) {
super.awake(withContext: context)
// Configure interface objects here.
print("*** watch: interfaceController.awake")
self.communicationService = CommunicationServiceProvider.current()
self.handle(context)
}
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
print("*** watch: interfaceController.willActivate")
}
override func didDeactivate() {
// This method is called when watch view controller is no longer visible
super.didDeactivate()
print("*** watch: interfaceController.didDeactivate")
}
private func updateControls(authenticate: Bool) {
if authenticate {
self.showAuthenticateButton()
} else {
self.showQRC()
}
}
private func handle(_ context: Any?) {
guard let dictionary = context as? [String:Any?] else {
return
}
if let requestId = dictionary[ApplicationContextTag.requestId.rawValue] as? String {
self.requestId = requestId
let askForAuthentication = self.requestId != nil
self.updateControls(authenticate: askForAuthentication)
}
if let qrcImage = dictionary[ApplicationContextTag.qrcImage.rawValue] as? UIImage {
self.qrcImage.setImage(qrcImage)
}
}
private func showQRC() {
self.qrcImage.setHidden(false)
self.label.setHidden(true)
self.authenticateButton.setHidden(true)
}
private func showAuthenticateButton() {
self.label.setHidden(false)
self.authenticateButton.setHidden(false)
self.qrcImage.setHidden(true)
}
}
//
// NotificationController.swift
// XWallet Watchkit App Extension
//
// Created by loj on 15.07.18.
//
import WatchKit
import Foundation
import UserNotifications
class NotificationController: WKUserNotificationInterfaceController {
override init() {
// Initialize variables here.
super.init()
// Configure interface objects here.
}
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
}
override func didDeactivate() {
// This method is called when watch view controller is no longer visible
super.didDeactivate()
}
override func didReceive(_ notification: UNNotification,
withCompletion completionHandler: @escaping (WKUserNotificationInterfaceType) -> Swift.Void) {
// This method is called when a notification needs to be presented.
// Implement it if you use a dynamic notification interface.
// Populate your dynamic notification interface as quickly as possible.
//
// After populating your dynamic notification interface call the completion block.
completionHandler(.custom)
}
}
{
"aps": {
"alert": {
"body": "Test message",
"title": "Optional title"
},
"category": "myCategory",
"thread-id":"5280"
},
"WatchKit Simulator Actions": [
{
"title": "First Button",
"identifier": "firstButtonAction"
}
],
"customKey": "Use this file to define a testing payload for your notifications. The aps dictionary specifies the category, alert text and title. The WatchKit Simulator Actions array can provide info for one or more action buttons in addition to the standard Dismiss button. Any other top level keys are custom payload. If you have multiple such JSON files in your project, you'll be able to select them when choosing to debug the notification interface of your Watch App."
}
//
// CommunicationService.swift
// XWallet Watchkit App Extension
//
// Created by loj on 29.07.18.
//
import Foundation
import WatchConnectivity
import WatchKit
public protocol CommunicationServiceProtocol {
func register(receiveQrcHandler: @escaping (_ qrcImage: UIImage) -> Void)
func register(receiveRequestIdHandler: @escaping (_ requestId: String) -> Void)
func authenticate(requestId: String)
}
public class CommunicationService: NSObject, CommunicationServiceProtocol {
private var receiveRequestIdHandler: ((String) -> Void)?
private var receiveQrcHandler: ((UIImage) -> Void)?
public override init() {
super.init()
if WCSession.isSupported() {
let session = WCSession.default
session.delegate = self
session.activate()
}
}
public func register(receiveQrcHandler: @escaping (UIImage) -> Void) {
self.receiveQrcHandler = receiveQrcHandler
}
public func register(receiveRequestIdHandler: @escaping (_ requestId: String) -> Void) {
self.receiveRequestIdHandler = receiveRequestIdHandler
}
public func authenticate(requestId: String) {
if WCSession.isSupported() {
let message = [ApplicationContextTag.requestId.rawValue:"\(requestId)"]
let session = WCSession.default
do {
print("*** watch: sending to phone: \(message)")
try session.updateApplicationContext(message)
} catch {
print("*** watch: sending to phone failed: \(error)")
}
}
}
}
extension CommunicationService: WCSessionDelegate {
public func session(_ session: WCSession,
activationDidCompleteWith activationState: WCSessionActivationState,
error: Error?)
{
if let error = error {
print("*** watch: WC Session activation failed with error: \(error.localizedDescription)")