Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#1466] Choose the best server by testing responses from multiple hosts #1472

5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ and this library adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

# Unreleased

## Added

### [#1466] Choose the best server by testing responses from multiple hosts
- Synchronizer's `evaluateBestOf(endpoints: [], ...) async -> [LightWalletEndpoint]` method takes a list of endpoints and evaluates top k best performant servers.

# 2.2.1 - 2024-08-21

## Fixed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@
343CB69829CDC05D0063BF41 /* SyncBlocksListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 343CB69629CDC05D0063BF41 /* SyncBlocksListViewController.swift */; };
34CC8FD029CDC212001E06E9 /* SynchronizerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34CC8FCF29CDC212001E06E9 /* SynchronizerCell.swift */; };
34CC8FD129CDC212001E06E9 /* SynchronizerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34CC8FCF29CDC212001E06E9 /* SynchronizerCell.swift */; };
9EEFA3BF2C380DB900467ABC /* TestServersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EEFA3BE2C380DB900467ABC /* TestServersViewController.swift */; };
9EEFA3C02C380DB900467ABC /* TestServersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EEFA3BE2C380DB900467ABC /* TestServersViewController.swift */; };
F94912632790D7C4004BB3DE /* ZcashLightClientSample-Mainnet-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = F94912622790D7C4004BB3DE /* ZcashLightClientSample-Mainnet-Info.plist */; };
F9D63D1F27CD114A00F4DC5F /* ZcashLightClientKit in Frameworks */ = {isa = PBXBuildFile; productRef = F9D63D1E27CD114A00F4DC5F /* ZcashLightClientKit */; };
F9D63D2227CD125300F4DC5F /* KRProgressHUD in Frameworks */ = {isa = PBXBuildFile; productRef = F9D63D2127CD125300F4DC5F /* KRProgressHUD */; };
Expand Down Expand Up @@ -103,6 +105,7 @@
34CC8FCF29CDC212001E06E9 /* SynchronizerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SynchronizerCell.swift; sourceTree = "<group>"; };
372CC57DB80CC242BA556A30 /* Pods_ZcashLightClientSampleUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ZcashLightClientSampleUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
541D36743362A797BF085D14 /* Pods_ZcashLightClientSample_Mainnet.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ZcashLightClientSample_Mainnet.framework; sourceTree = BUILT_PRODUCTS_DIR; };
9EEFA3BE2C380DB900467ABC /* TestServersViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestServersViewController.swift; sourceTree = "<group>"; };
EBA186B246C4205F9BF71EED /* Pods_ZcashLightClientSample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ZcashLightClientSample.framework; sourceTree = BUILT_PRODUCTS_DIR; };
F94912622790D7C4004BB3DE /* ZcashLightClientSample-Mainnet-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "ZcashLightClientSample-Mainnet-Info.plist"; sourceTree = SOURCE_ROOT; };
F9D63D1D27CD103E00F4DC5F /* ZcashLightClientKit */ = {isa = PBXFileReference; lastKnownFileType = folder; name = ZcashLightClientKit; path = ../..; sourceTree = "<group>"; };
Expand Down Expand Up @@ -219,6 +222,7 @@
0D907F142322CC5900D641FE /* ZcashLightClientSample */ = {
isa = PBXGroup;
children = (
9EEFA3BD2C380D7900467ABC /* Test Servers */,
0D76121426B1D5D7001CA417 /* Constants */,
0D1BE47D2581933C00F78BE3 /* Get UTXOs */,
0D6CE8BB252E3C1A0005D707 /* Sapling Parameters */,
Expand Down Expand Up @@ -300,6 +304,14 @@
path = "Paginated Transactions";
sourceTree = "<group>";
};
9EEFA3BD2C380D7900467ABC /* Test Servers */ = {
isa = PBXGroup;
children = (
9EEFA3BE2C380DB900467ABC /* TestServersViewController.swift */,
);
path = "Test Servers";
sourceTree = "<group>";
};
F93EF550279077B70063E43A /* Frameworks */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -528,6 +540,7 @@
0DF53E6723A438F100D7249C /* PaginatedTransactionsViewController.swift in Sources */,
34CC8FD029CDC212001E06E9 /* SynchronizerCell.swift in Sources */,
343CB69729CDC05D0063BF41 /* SyncBlocksListViewController.swift in Sources */,
9EEFA3BF2C380DB900467ABC /* TestServersViewController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -560,6 +573,7 @@
0DA1C4CD27D11D9500E5006E /* PaginatedTransactionsViewController.swift in Sources */,
34CC8FD129CDC212001E06E9 /* SynchronizerCell.swift in Sources */,
343CB69829CDC05D0063BF41 /* SyncBlocksListViewController.swift in Sources */,
9EEFA3C02C380DB900467ABC /* TestServersViewController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="22505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Ewq-Xy-xHb">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Ewq-Xy-xHb">
<device id="retina6_0" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22504"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22685"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
Expand Down Expand Up @@ -297,6 +297,26 @@
<segue destination="Ugl-B2-O3O" kind="show" id="hk7-2x-BJl"/>
</connections>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="Cuf-Pp-TrG" style="IBUITableViewCellStyleDefault" id="B0F-J8-vVs">
<rect key="frame" x="0.0" y="661.33335113525391" width="390" height="43.666667938232422"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="B0F-J8-vVs" id="7IF-Qw-50i">
<rect key="frame" x="0.0" y="0.0" width="390" height="43.666667938232422"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Test servers" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Cuf-Pp-TrG">
<rect key="frame" x="20" y="0.0" width="350" height="43.666667938232422"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
<connections>
<segue destination="YrC-gV-U7F" kind="show" id="L7C-z0-MKd"/>
</connections>
</tableViewCell>
</cells>
</tableViewSection>
</sections>
Expand Down Expand Up @@ -1391,6 +1411,44 @@
</objects>
<point key="canvasLocation" x="1880" y="1126"/>
</scene>
<!--Test Servers View Controller-->
<scene sceneID="b7C-1S-hZW">
<objects>
<viewController id="YrC-gV-U7F" customClass="TestServersViewController" customModule="ZcashLightClientSample" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Iha-sQ-HNt">
<rect key="frame" x="0.0" y="0.0" width="390" height="844"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="oOW-JY-WzN">
<rect key="frame" x="136" y="109" width="119" height="35"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<state key="normal" title="Button"/>
<buttonConfiguration key="configuration" style="plain" title="Test Servers"/>
<connections>
<action selector="testServers:" destination="YrC-gV-U7F" eventType="touchUpInside" id="El4-aw-aUF"/>
</connections>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="top" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="asdasd" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="TVg-m2-N4p">
<rect key="frame" x="24" y="162" width="344" height="469"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<viewLayoutGuide key="safeArea" id="fec-YN-6id"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
</view>
<navigationItem key="navigationItem" id="jfe-3q-9Oj"/>
<connections>
<outlet property="resultsLabel" destination="TVg-m2-N4p" id="t2g-vv-hbe"/>
<outlet property="testServersBtn" destination="oOW-JY-WzN" id="Bqh-QJ-s56"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="bDV-rt-CHM" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-1623.0769230769231" y="1124.6445497630332"/>
</scene>
</scenes>
<inferredMetricsTieBreakers>
<segue reference="oxP-eV-1Z2"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//
// TestServersViewController.swift
// ZcashLightClientSample
//
// Created by Lukáš Korba on 05.07.2024.
// Copyright © 2024 Electric Coin Company. All rights reserved.
//

import UIKit
import ZcashLightClientKit

class TestServersViewController: UIViewController {
let endpoints: [LightWalletEndpoint] = [
LightWalletEndpoint(address: "zec.rocks", port: 443),
LightWalletEndpoint(address: "na.zec.rocks", port: 443),
LightWalletEndpoint(address: "sa.zec.rocks", port: 443),
LightWalletEndpoint(address: "eu.zec.rocks", port: 443),
LightWalletEndpoint(address: "ap.zec.rocks", port: 443),
LightWalletEndpoint(address: "lwd1.zcash-infra.com", port: 9067),
LightWalletEndpoint(address: "lwd2.zcash-infra.com", port: 9067),
LightWalletEndpoint(address: "lwd3.zcash-infra.com", port: 9067),
LightWalletEndpoint(address: "lwd4.zcash-infra.com", port: 9067),
LightWalletEndpoint(address: "lwd5.zcash-infra.com", port: 9067),
LightWalletEndpoint(address: "lwd6.zcash-infra.com", port: 9067),
LightWalletEndpoint(address: "lwd7.zcash-infra.com", port: 9067),
LightWalletEndpoint(address: "lwd8.zcash-infra.com", port: 9067)
]
LukasKorba marked this conversation as resolved.
Show resolved Hide resolved

@IBOutlet weak var resultsLabel: UILabel!
@IBOutlet weak var testServersBtn: UIButton!

var startTime: TimeInterval = 0.0

override func viewDidLoad() {
super.viewDidLoad()
}

@IBAction func testServers(_ sender: Any) {
let synchronizer = AppDelegate.shared.sharedSynchronizer
testServersBtn.isEnabled = false

startTime = Date().timeIntervalSince1970

Task {
let results = await synchronizer.evaluateBestOf(endpoints: endpoints)

await showResults(results)
}
}

@MainActor func showResults(_ endpoints: [LightWalletEndpoint]) async {
testServersBtn.isEnabled = true

var resultStr = ""

var counter = 1
endpoints.forEach {
resultStr = "\(resultStr) \(counter). \($0.host):\($0.port)\n"
counter += 1
}

resultStr = "\(resultStr) \n"

resultStr = "\(resultStr) time to evaluate all: \(Date().timeIntervalSince1970 - startTime)s"

resultsLabel.text = resultStr
}
}
4 changes: 4 additions & 0 deletions Sources/ZcashLightClientKit/Block/CompactBlockProcessor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,10 @@ extension CompactBlockProcessor {
func latestHeight() async throws -> BlockHeight {
try await blockDownloaderService.latestBlockHeight()
}

func consensusBranchIdFor(_ height: Int32) -> Int32? {
try? rustBackend.consensusBranchIdFor(height: height)
}
}

// MARK: - Rewind
Expand Down
3 changes: 3 additions & 0 deletions Sources/ZcashLightClientKit/Constants/ZcashSDK.swift
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,9 @@ public enum ZcashSDK {
public static let outputParamFilename = "sapling-output.params"
// swiftlint:disable:next force_unwrapping
public static let outputParamFileURL = URL(string: cloudParameterURL)!.appendingPathComponent(outputParamFilename)

/// A constant that helps determine if a server’s chain tip is so far away from the estimated height that we consider the server out of sync
public static let syncedThresholdBlocks = UInt64(288)
}

public protocol NetworkConstants {
Expand Down
17 changes: 17 additions & 0 deletions Sources/ZcashLightClientKit/Synchronizer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,23 @@
///
/// - parameter seed: byte array of the seed
func isSeedRelevantToAnyDerivedAccount(seed: [UInt8]) async throws -> Bool

/// Takes the list of endpoints and runs it through a series of checks to evaluate its performance.
/// - Parameters:
/// - endpoints: Array of endpoints to evaluate.
/// - latencyThresholdMillis: The mean latency of `getInfo` and `getTheLatestHeight` calls must be below this threshold. The default is 300 ms.
/// - fetchThresholdSeconds: The time to download `nBlocksToFetch` blocks from the stream must be below this threshold. The default is 60 seconds.
/// - nBlocksToFetch: The number of blocks expected to be downloaded from the stream, with the time compared to `fetchThresholdSeconds`. The default is 100.
/// - kServers: The expected number of endpoints in the output. The default is 3.
/// - network: Mainnet or testnet. The default is mainnet.
func evaluateBestOf(

Check warning on line 379 in Sources/ZcashLightClientKit/Synchronizer.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Function Parameter Count Violation: Function should have 5 parameters or less: it currently has 6 (function_parameter_count)
endpoints: [LightWalletEndpoint],
latencyThresholdMillis: Double,
fetchThresholdSeconds: Double,
nBlocksToFetch: UInt64,
kServers: Int,
network: NetworkType
) async -> [LightWalletEndpoint]
}

public enum SyncStatus: Equatable {
Expand Down
Loading
Loading