mirror of
https://github.com/oxen-io/session-ios.git
synced 2023-12-13 21:30:14 +01:00
Implement basic onion request UI
This commit is contained in:
parent
8fb5e7102f
commit
00f5cebdef
8 changed files with 231 additions and 14 deletions
|
@ -592,6 +592,8 @@
|
|||
B86BD08423399ACF000F5AE3 /* Modal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08323399ACF000F5AE3 /* Modal.swift */; };
|
||||
B86BD08623399CEF000F5AE3 /* SeedModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08523399CEF000F5AE3 /* SeedModal.swift */; };
|
||||
B8783E9E23EB948D00404FB8 /* UILabel+Interaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8783E9D23EB948D00404FB8 /* UILabel+Interaction.swift */; };
|
||||
B879D449247E1BE300DB3608 /* PathVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B879D448247E1BE300DB3608 /* PathVC.swift */; };
|
||||
B879D44B247E1D9200DB3608 /* PathStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B879D44A247E1D9200DB3608 /* PathStatusView.swift */; };
|
||||
B885D5F4233491AB00EE0D8E /* DeviceLinkingModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B885D5F3233491AB00EE0D8E /* DeviceLinkingModal.swift */; };
|
||||
B886B4A72398B23E00211ABE /* QRCodeVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B886B4A62398B23E00211ABE /* QRCodeVC.swift */; };
|
||||
B886B4A92398BA1500211ABE /* QRCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = B886B4A82398BA1500211ABE /* QRCode.swift */; };
|
||||
|
@ -1462,6 +1464,8 @@
|
|||
B86BD08323399ACF000F5AE3 /* Modal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modal.swift; sourceTree = "<group>"; };
|
||||
B86BD08523399CEF000F5AE3 /* SeedModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedModal.swift; sourceTree = "<group>"; };
|
||||
B8783E9D23EB948D00404FB8 /* UILabel+Interaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UILabel+Interaction.swift"; sourceTree = "<group>"; };
|
||||
B879D448247E1BE300DB3608 /* PathVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PathVC.swift; sourceTree = "<group>"; };
|
||||
B879D44A247E1D9200DB3608 /* PathStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PathStatusView.swift; sourceTree = "<group>"; };
|
||||
B885D5F3233491AB00EE0D8E /* DeviceLinkingModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceLinkingModal.swift; sourceTree = "<group>"; };
|
||||
B885D5F52334A32100EE0D8E /* UIView+Constraints.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Constraints.swift"; sourceTree = "<group>"; };
|
||||
B886B4A62398B23E00211ABE /* QRCodeVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeVC.swift; sourceTree = "<group>"; };
|
||||
|
@ -2871,6 +2875,7 @@
|
|||
B8B26C8E234D629C004ED98C /* MentionCandidateSelectionView.swift */,
|
||||
B8B26C90234D8CBD004ED98C /* MentionCandidateSelectionViewDelegate.swift */,
|
||||
B83F2B85240C7B8F000A54AB /* NewConversationButtonSet.swift */,
|
||||
B879D44A247E1D9200DB3608 /* PathStatusView.swift */,
|
||||
C353F8F8244809150011121A /* PNOptionView.swift */,
|
||||
B8BB82B02390C37000BA5194 /* SearchBar.swift */,
|
||||
B85357BE23A1AE0800AAF6CD /* SeedReminderView.swift */,
|
||||
|
@ -2917,6 +2922,7 @@
|
|||
B8CCF63623961D6D0091D419 /* NewPrivateChatVC.swift */,
|
||||
B894D0742339EDCF00B4D94D /* NukeDataModal.swift */,
|
||||
C3DFFAC723E970080058DAF8 /* OpenGroupSuggestionSheet.swift */,
|
||||
B879D448247E1BE300DB3608 /* PathVC.swift */,
|
||||
C353F8F6244808E90011121A /* PNModeSheet.swift */,
|
||||
C3548F0524456447009433A8 /* PNModeVC.swift */,
|
||||
B886B4A62398B23E00211ABE /* QRCodeVC.swift */,
|
||||
|
@ -4010,6 +4016,7 @@
|
|||
341F2C0F1F2B8AE700D07D6B /* DebugUIMisc.m in Sources */,
|
||||
B893063F2383961A005EAA8E /* ScanQRCodeWrapperVC.swift in Sources */,
|
||||
B83F2B86240C7B8F000A54AB /* NewConversationButtonSet.swift in Sources */,
|
||||
B879D449247E1BE300DB3608 /* PathVC.swift in Sources */,
|
||||
340FC8AF204DAC8D007AEB0F /* OWSLinkDeviceViewController.m in Sources */,
|
||||
34E3EF0D1EFC235B007F6822 /* DebugUIDiskUsage.m in Sources */,
|
||||
454A84042059C787008B8C75 /* MediaTileViewController.swift in Sources */,
|
||||
|
@ -4146,6 +4153,7 @@
|
|||
34D8C0281ED3673300188D7C /* DebugUITableViewController.m in Sources */,
|
||||
45F32C222057297A00A300D5 /* MediaDetailViewController.m in Sources */,
|
||||
B8B26C8F234D629C004ED98C /* MentionCandidateSelectionView.swift in Sources */,
|
||||
B879D44B247E1D9200DB3608 /* PathStatusView.swift in Sources */,
|
||||
34B3F8851E8DF1700035BE1A /* NewGroupViewController.m in Sources */,
|
||||
B8CCF63F23975CFB0091D419 /* JoinPublicChatVC.swift in Sources */,
|
||||
34ABC0E421DD20C500ED9469 /* ConversationMessageMapping.swift in Sources */,
|
||||
|
|
45
Signal/src/Loki/Components/PathStatusView.swift
Normal file
45
Signal/src/Loki/Components/PathStatusView.swift
Normal file
|
@ -0,0 +1,45 @@
|
|||
|
||||
final class PathStatusView : UIView {
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
setUpViewHierarchy()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
setUpViewHierarchy()
|
||||
}
|
||||
|
||||
private func setUpViewHierarchy() {
|
||||
backgroundColor = Colors.accent
|
||||
let size = Values.pathStatusViewSize
|
||||
layer.cornerRadius = size / 2
|
||||
setGlow(to: size, with: Colors.accent, animated: false)
|
||||
layer.masksToBounds = false
|
||||
}
|
||||
|
||||
func setGlow(to size: CGFloat, with color: UIColor, animated isAnimated: Bool) {
|
||||
let newPath = UIBezierPath(ovalIn: CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: size, height: size))).cgPath
|
||||
if isAnimated {
|
||||
let pathAnimation = CABasicAnimation(keyPath: "shadowPath")
|
||||
pathAnimation.fromValue = layer.shadowPath
|
||||
pathAnimation.toValue = newPath
|
||||
pathAnimation.duration = 0.25
|
||||
layer.add(pathAnimation, forKey: pathAnimation.keyPath)
|
||||
}
|
||||
layer.shadowPath = newPath
|
||||
let newColor = color.cgColor
|
||||
if isAnimated {
|
||||
let colorAnimation = CABasicAnimation(keyPath: "shadowColor")
|
||||
colorAnimation.fromValue = layer.shadowColor
|
||||
colorAnimation.toValue = newColor
|
||||
colorAnimation.duration = 0.25
|
||||
layer.add(colorAnimation, forKey: colorAnimation.keyPath)
|
||||
}
|
||||
layer.shadowColor = newColor
|
||||
layer.shadowOffset = CGSize(width: 0, height: 0.8)
|
||||
layer.shadowOpacity = isLightMode ? 0.4 : 1
|
||||
layer.shadowRadius = isLightMode ? 6 : 8
|
||||
}
|
||||
}
|
|
@ -299,6 +299,18 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrol
|
|||
profilePictureView.pin(.trailing, to: .trailing, of: profilePictureViewContainer)
|
||||
profilePictureView.pin(.bottom, to: .bottom, of: profilePictureViewContainer)
|
||||
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: profilePictureViewContainer)
|
||||
let pathStatusViewContainer = UIView()
|
||||
let pathStatusViewContainerSize = Values.verySmallProfilePictureSize // Match the profile picture view
|
||||
pathStatusViewContainer.set(.width, to: pathStatusViewContainerSize)
|
||||
pathStatusViewContainer.set(.height, to: pathStatusViewContainerSize)
|
||||
let pathStatusView = PathStatusView()
|
||||
pathStatusView.set(.width, to: Values.pathStatusViewSize)
|
||||
pathStatusView.set(.height, to: Values.pathStatusViewSize)
|
||||
pathStatusViewContainer.addSubview(pathStatusView)
|
||||
pathStatusView.center(.horizontal, in: pathStatusViewContainer)
|
||||
pathStatusView.center(.vertical, in: pathStatusViewContainer)
|
||||
pathStatusViewContainer.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(showPath)))
|
||||
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: pathStatusViewContainer)
|
||||
}
|
||||
|
||||
// MARK: Interaction
|
||||
|
@ -398,6 +410,12 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrol
|
|||
present(navigationController, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
@objc private func showPath() {
|
||||
let pathVC = PathVC()
|
||||
let navigationController = OWSNavigationController(rootViewController: pathVC)
|
||||
present(navigationController, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
@objc func joinOpenGroup() {
|
||||
let joinPublicChatVC = JoinPublicChatVC()
|
||||
let navigationController = OWSNavigationController(rootViewController: joinPublicChatVC)
|
||||
|
|
140
Signal/src/Loki/View Controllers/PathVC.swift
Normal file
140
Signal/src/Loki/View Controllers/PathVC.swift
Normal file
|
@ -0,0 +1,140 @@
|
|||
|
||||
final class PathVC : BaseVC {
|
||||
|
||||
// MARK: Lifecycle
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
// Set gradient background
|
||||
view.backgroundColor = .clear
|
||||
let gradient = Gradients.defaultLokiBackground
|
||||
view.setGradient(gradient)
|
||||
// Set up navigation bar
|
||||
let navigationBar = navigationController!.navigationBar
|
||||
navigationBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default)
|
||||
navigationBar.shadowImage = UIImage()
|
||||
navigationBar.isTranslucent = false
|
||||
navigationBar.barTintColor = Colors.navigationBarBackground
|
||||
// Set up close button
|
||||
let closeButton = UIBarButtonItem(image: #imageLiteral(resourceName: "X"), style: .plain, target: self, action: #selector(close))
|
||||
closeButton.tintColor = Colors.text
|
||||
navigationItem.leftBarButtonItem = closeButton
|
||||
// Customize title
|
||||
let titleLabel = UILabel()
|
||||
titleLabel.text = NSLocalizedString("Path", comment: "")
|
||||
titleLabel.textColor = Colors.text
|
||||
titleLabel.font = .boldSystemFont(ofSize: Values.veryLargeFontSize)
|
||||
navigationItem.titleView = titleLabel
|
||||
// Set up explanation label
|
||||
let explanationLabel = UILabel()
|
||||
explanationLabel.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity)
|
||||
explanationLabel.font = .systemFont(ofSize: Values.smallFontSize)
|
||||
explanationLabel.text = NSLocalizedString("Session hides your IP by onion routing your messages through Session's decentralized Service Node network. The Service Nodes currently being used for this are shown below.", comment: "")
|
||||
explanationLabel.numberOfLines = 0
|
||||
explanationLabel.textAlignment = .center
|
||||
explanationLabel.lineBreakMode = .byWordWrapping
|
||||
view.addSubview(explanationLabel)
|
||||
explanationLabel.pin(.leading, to: .leading, of: view, withInset: Values.largeSpacing)
|
||||
explanationLabel.pin(.top, to: .top, of: view, withInset: Values.mediumSpacing)
|
||||
explanationLabel.pin(.trailing, to: .trailing, of: view, withInset: -Values.largeSpacing)
|
||||
// Set up path stack view
|
||||
guard var mainPath = OnionRequestAPI.paths.first else {
|
||||
return close() // TODO: Show path establishing UI
|
||||
}
|
||||
let rows: [UIStackView]
|
||||
switch mainPath.count {
|
||||
case 1: return // TODO: Do we want to handle this case?
|
||||
case 2:
|
||||
let topPathRow = getPathRow(forSnode: mainPath[1], at: .top)
|
||||
let bottomPathRow = getPathRow(forSnode: mainPath[0], at: .bottom)
|
||||
rows = [ topPathRow, bottomPathRow ]
|
||||
default:
|
||||
let topPathRow = getPathRow(forSnode: mainPath.removeLast(), at: .top)
|
||||
let bottomPathRow = getPathRow(forSnode: mainPath.removeFirst(), at: .bottom)
|
||||
let middlePathRows = mainPath.map {
|
||||
getPathRow(forSnode: $0, at: .middle)
|
||||
}
|
||||
rows = [ topPathRow ] + middlePathRows + [ bottomPathRow ]
|
||||
}
|
||||
let pathStackView = UIStackView(arrangedSubviews: rows)
|
||||
pathStackView.axis = .vertical
|
||||
view.addSubview(pathStackView)
|
||||
pathStackView.pin(.top, to: .bottom, of: explanationLabel, withInset: Values.largeSpacing)
|
||||
pathStackView.center(.horizontal, in: view)
|
||||
pathStackView.leadingAnchor.constraint(greaterThanOrEqualTo: view.leadingAnchor, constant: Values.largeSpacing).isActive = true
|
||||
view.trailingAnchor.constraint(greaterThanOrEqualTo: pathStackView.trailingAnchor, constant: Values.largeSpacing).isActive = true
|
||||
}
|
||||
|
||||
private func getPathRow(forSnode snode: LokiAPITarget, at location: LineView.Location) -> UIStackView {
|
||||
let lineView = LineView(location: location)
|
||||
lineView.set(.width, to: Values.pathRowDotSize)
|
||||
let snodeLabel = UILabel()
|
||||
snodeLabel.textColor = Colors.text
|
||||
snodeLabel.font = .systemFont(ofSize: Values.mediumFontSize)
|
||||
var snodeDescription = snode.description
|
||||
if snodeDescription.hasPrefix("https://") {
|
||||
snodeDescription.removeFirst(8)
|
||||
}
|
||||
if let colonIndex = snodeDescription.lastIndex(of: ":") {
|
||||
snodeDescription = String(snodeDescription[snodeDescription.startIndex..<colonIndex])
|
||||
}
|
||||
snodeLabel.text = snodeDescription
|
||||
snodeLabel.lineBreakMode = .byTruncatingTail
|
||||
let stackView = UIStackView(arrangedSubviews: [ lineView, snodeLabel ])
|
||||
stackView.axis = .horizontal
|
||||
stackView.spacing = Values.largeSpacing
|
||||
return stackView
|
||||
}
|
||||
|
||||
// MARK: Interaction
|
||||
@objc private func close() {
|
||||
dismiss(animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Line View
|
||||
private final class LineView : UIView {
|
||||
private let location: Location
|
||||
|
||||
enum Location {
|
||||
case top, middle, bottom
|
||||
}
|
||||
|
||||
init(location: Location) {
|
||||
self.location = location
|
||||
super.init(frame: CGRect.zero)
|
||||
setUpViewHierarchy()
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
preconditionFailure("Use init(location:) instead.")
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
preconditionFailure("Use init(location:) instead.")
|
||||
}
|
||||
|
||||
private func setUpViewHierarchy() {
|
||||
set(.height, to: Values.pathRowHeight)
|
||||
let lineView = UIView()
|
||||
lineView.set(.width, to: Values.pathRowLineThickness)
|
||||
lineView.backgroundColor = Colors.text
|
||||
addSubview(lineView)
|
||||
lineView.center(.horizontal, in: self)
|
||||
switch location {
|
||||
case .top: lineView.topAnchor.constraint(equalTo: centerYAnchor).isActive = true
|
||||
case .middle, .bottom: lineView.pin(.top, to: .top, of: self)
|
||||
}
|
||||
switch location {
|
||||
case .top, .middle: lineView.pin(.bottom, to: .bottom, of: self)
|
||||
case .bottom: lineView.bottomAnchor.constraint(equalTo: centerYAnchor).isActive = true
|
||||
}
|
||||
let dotView = UIView()
|
||||
let dotSize = Values.pathRowDotSize
|
||||
dotView.set(.width, to: dotSize)
|
||||
dotView.set(.height, to: dotSize)
|
||||
dotView.layer.cornerRadius = dotSize / 2
|
||||
dotView.backgroundColor = Colors.text
|
||||
addSubview(dotView)
|
||||
dotView.center(in: self)
|
||||
}
|
||||
}
|
|
@ -2829,3 +2829,5 @@
|
|||
"Please check your internet connection and try again" = "Please check your internet connection and try again";
|
||||
"Authorizing Device Link" = "Authorizing Device Link";
|
||||
"Please wait while the device link is created. This can take up to a minute." = "Please wait while the device link is created. This can take up to a minute.";
|
||||
"Path" = "Path";
|
||||
"Session hides your IP by onion routing your messages through Session's decentralized Service Node network. The Service Nodes currently being used for this are shown below." = "Session hides your IP by onion routing your messages through Session's decentralized Service Node network. The Service Nodes currently being used for this are shown below.";
|
||||
|
|
|
@ -47,6 +47,10 @@ public final class Values : NSObject {
|
|||
@objc public static let messageBubbleCornerRadius: CGFloat = 10
|
||||
@objc public static let progressBarThickness: CGFloat = 2
|
||||
@objc public static let pnOptionCornerRadius = CGFloat(8)
|
||||
@objc public static let pathStatusViewSize = CGFloat(8)
|
||||
@objc public static let pathRowLineThickness = CGFloat(1)
|
||||
@objc public static let pathRowDotSize = CGFloat(8)
|
||||
@objc public static let pathRowHeight = CGFloat(72)
|
||||
|
||||
// MARK: - Distances
|
||||
@objc public static let verySmallSpacing = CGFloat(4)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
/// Either a service node or another client if P2P is enabled.
|
||||
internal final class LokiAPITarget : NSObject, NSCoding {
|
||||
public final class LokiAPITarget : NSObject, NSCoding {
|
||||
internal let address: String
|
||||
internal let port: UInt16
|
||||
internal let publicKeySet: KeySet?
|
||||
|
@ -27,7 +27,7 @@ internal final class LokiAPITarget : NSObject, NSCoding {
|
|||
}
|
||||
|
||||
// MARK: Coding
|
||||
internal init?(coder: NSCoder) {
|
||||
public init?(coder: NSCoder) {
|
||||
address = coder.decodeObject(forKey: "address") as! String
|
||||
port = coder.decodeObject(forKey: "port") as! UInt16
|
||||
if let idKey = coder.decodeObject(forKey: "idKey") as? String, let encryptionKey = coder.decodeObject(forKey: "encryptionKey") as? String {
|
||||
|
@ -38,7 +38,7 @@ internal final class LokiAPITarget : NSObject, NSCoding {
|
|||
super.init()
|
||||
}
|
||||
|
||||
internal func encode(with coder: NSCoder) {
|
||||
public func encode(with coder: NSCoder) {
|
||||
coder.encode(address, forKey: "address")
|
||||
coder.encode(port, forKey: "port")
|
||||
if let keySet = publicKeySet {
|
||||
|
@ -48,16 +48,16 @@ internal final class LokiAPITarget : NSObject, NSCoding {
|
|||
}
|
||||
|
||||
// MARK: Equality
|
||||
override internal func isEqual(_ other: Any?) -> Bool {
|
||||
override public func isEqual(_ other: Any?) -> Bool {
|
||||
guard let other = other as? LokiAPITarget else { return false }
|
||||
return address == other.address && port == other.port
|
||||
}
|
||||
|
||||
// MARK: Hashing
|
||||
override internal var hash: Int { // Override NSObject.hash and not Hashable.hashValue or Hashable.hash(into:)
|
||||
override public var hash: Int { // Override NSObject.hash and not Hashable.hashValue or Hashable.hash(into:)
|
||||
return address.hashValue ^ port.hashValue
|
||||
}
|
||||
|
||||
// MARK: Description
|
||||
override internal var description: String { return "\(address):\(port)" }
|
||||
override public var description: String { return "\(address):\(port)" }
|
||||
}
|
||||
|
|
|
@ -2,11 +2,11 @@ import CryptoSwift
|
|||
import PromiseKit
|
||||
|
||||
/// See the "Onion Requests" section of [The Session Whitepaper](https://arxiv.org/pdf/2002.04609.pdf) for more information.
|
||||
internal enum OnionRequestAPI {
|
||||
public enum OnionRequestAPI {
|
||||
/// - Note: Must only be modified from `LokiAPI.workQueue`.
|
||||
private static var guardSnodes: Set<LokiAPITarget> = []
|
||||
/// - Note: Must only be modified from `LokiAPI.workQueue`.
|
||||
private static var paths: Set<Path> = []
|
||||
public static var paths: [Path] = []
|
||||
|
||||
private static var snodePool: Set<LokiAPITarget> {
|
||||
let unreliableSnodes = Set(LokiAPI.failureCount.keys)
|
||||
|
@ -42,7 +42,7 @@ internal enum OnionRequestAPI {
|
|||
}
|
||||
|
||||
// MARK: Path
|
||||
internal typealias Path = [LokiAPITarget]
|
||||
public typealias Path = [LokiAPITarget]
|
||||
|
||||
// MARK: Onion Building Result
|
||||
private typealias OnionBuildingResult = (guardSnode: LokiAPITarget, finalEncryptionResult: EncryptionResult, targetSnodeSymmetricKey: Data)
|
||||
|
@ -54,7 +54,7 @@ internal enum OnionRequestAPI {
|
|||
let queue = DispatchQueue.global() // No need to block the work queue for this
|
||||
queue.async {
|
||||
let url = "\(snode.address):\(snode.port)/get_stats/v1"
|
||||
let timeout: TimeInterval = 6 // Use a shorter timeout for testing
|
||||
let timeout: TimeInterval = 3 // Use a shorter timeout for testing
|
||||
HTTP.execute(.get, url, timeout: timeout).done(on: queue) { rawResponse in
|
||||
guard let json = rawResponse as? JSON, let version = json["version"] as? String else { return seal.reject(Error.missingSnodeVersion) }
|
||||
if version >= "2.0.0" {
|
||||
|
@ -100,15 +100,15 @@ internal enum OnionRequestAPI {
|
|||
|
||||
/// Builds and returns `pathCount` paths. The returned promise errors out with `Error.insufficientSnodes`
|
||||
/// if not enough (reliable) snodes are available.
|
||||
private static func buildPaths() -> Promise<Set<Path>> {
|
||||
private static func buildPaths() -> Promise<[Path]> {
|
||||
print("[Loki] [Onion Request API] Building onion request paths.")
|
||||
return LokiAPI.getRandomSnode().then(on: LokiAPI.workQueue) { _ -> Promise<Set<Path>> in // Just used to populate the snode pool
|
||||
return LokiAPI.getRandomSnode().then(on: LokiAPI.workQueue) { _ -> Promise<[Path]> in // Just used to populate the snode pool
|
||||
return getGuardSnodes().map(on: LokiAPI.workQueue) { guardSnodes in
|
||||
var unusedSnodes = snodePool.subtracting(guardSnodes)
|
||||
let pathSnodeCount = guardSnodeCount * pathSize - guardSnodeCount
|
||||
guard unusedSnodes.count >= pathSnodeCount else { throw Error.insufficientSnodes }
|
||||
// Don't test path snodes as this would reveal the user's IP to them
|
||||
return Set(guardSnodes.map { guardSnode in
|
||||
return guardSnodes.map { guardSnode in
|
||||
let result = [ guardSnode ] + (0..<(pathSize - 1)).map { _ in
|
||||
// randomElement() uses the system's default random generator, which is cryptographically secure
|
||||
let pathSnode = unusedSnodes.randomElement()! // Safe because of the pathSnodeCount check above
|
||||
|
@ -117,7 +117,7 @@ internal enum OnionRequestAPI {
|
|||
}
|
||||
print("[Loki] [Onion Request API] Built new onion request path: \(result.prettifiedDescription).")
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue