Skip to content

Commit

Permalink
Merge pull request #1316 from pacu/custom-checkpoint-provisioning
Browse files Browse the repository at this point in the history
Create a Dependency that can load checkpoints from bundle
  • Loading branch information
LukasKorba authored Nov 15, 2023
2 parents 65cab30 + 6625ffd commit 5644f89
Show file tree
Hide file tree
Showing 12 changed files with 338 additions and 60 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// BundleCheckpointSource.swift
//
//
// Created by Francisco Gindre on 2023-10-30.
//

import Foundation

struct BundleCheckpointSource: CheckpointSource {
var network: NetworkType

var saplingActivation: Checkpoint

init(network: NetworkType) {
self.network = network
self.saplingActivation = switch network {
case .mainnet:
Checkpoint.mainnetMin
case .testnet:
Checkpoint.testnetMin
}
}

func latestKnownCheckpoint() -> Checkpoint {
Checkpoint.birthday(
with: .max,
checkpointDirectory: BundleCheckpointURLProvider.default.url(self.network)
) ?? saplingActivation
}

func birthday(for height: BlockHeight) -> Checkpoint {
Checkpoint.birthday(
with: height,
checkpointDirectory: BundleCheckpointURLProvider.default.url(self.network)
) ?? saplingActivation
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//
// BundleCheckpointURLProvider.swift
//
//
// Created by Francisco Gindre on 2023-10-30.
//

import Foundation

struct BundleCheckpointURLProvider {
var url: (NetworkType) -> URL
}

extension BundleCheckpointURLProvider {
/// Attempts to resolve the platform. `#if os(macOS)` implies that the build is for a macOS
/// target, otherwise we assume the build is for an iOS target.
static let `default` = BundleCheckpointURLProvider { networkType in
#if os(macOS)
Self.macOS.url(networkType)
#else
Self.iOS.url(networkType)
#endif
}

static let iOS = BundleCheckpointURLProvider(url: { networkType in
switch networkType {
case .mainnet:
return Checkpoint.mainnetCheckpointDirectory
case .testnet:
return Checkpoint.testnetCheckpointDirectory
}
})

/// This variant attempts to retrieve the saplingActivation checkpoint for the given network
/// type using `Bundle.module.url(forResource:withExtension:subdirectory:localization)`.
/// If not found it will return `WalletBirthday.mainnetCheckpointDirectory` or
/// `WalletBirthday.testnetCheckpointDirectory`. This responds to tests failing on a macOS
/// target because the checkpoint resources would not be found.
static let macOS = BundleCheckpointURLProvider(url: { networkType in
switch networkType {
case .mainnet:
return Bundle.module.url(
forResource: "419200",
withExtension: "json",
subdirectory: "checkpoints/mainnet/",
localization: nil
)?
.deletingLastPathComponent() ?? Checkpoint.mainnetCheckpointDirectory
case .testnet:
return Bundle.module.url(
forResource: "280000",
withExtension: "json",
subdirectory: "checkpoints/testnet/",
localization: nil
)?
.deletingLastPathComponent() ?? Checkpoint.testnetCheckpointDirectory
}
})
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// WalletBirthday+Constants.swift
// Checkpoint+helpers.swift
// ZcashLightClientKit
//
// Created by Francisco Gindre on 7/28/21.
Expand Down Expand Up @@ -64,54 +64,3 @@ extension Checkpoint {
}
}
}

struct BundleCheckpointURLProvider {
var url: (NetworkType) -> URL
}

extension BundleCheckpointURLProvider {
/// Attempts to resolve the platform by checking `#if os(macOS)` build corresponds to a MacOS target
/// `#else` branch of that condition will assume iOS is the target platform
static let `default` = BundleCheckpointURLProvider { networkType in
#if os(macOS)
Self.macOS.url(networkType)
#else
Self.iOS.url(networkType)
#endif
}

static let iOS = BundleCheckpointURLProvider(url: { networkType in
switch networkType {
case .mainnet:
return Checkpoint.mainnetCheckpointDirectory
case .testnet:
return Checkpoint.testnetCheckpointDirectory
}
})

/// This variant attempts to retrieve the saplingActivation checkpoint for the given network type
/// using `Bundle.module.url(forResource:withExtension:subdirectory:localization)`
/// if not found it will return `WalletBirthday.mainnetCheckpointDirectory` or
/// `WalletBirthday.testnetCheckpointDirectory`. This responds to tests
/// failing on MacOS target because the checkpoint resources would fail.
static let macOS = BundleCheckpointURLProvider(url: { networkType in
switch networkType {
case .mainnet:
return Bundle.module.url(
forResource: "419200",
withExtension: "json",
subdirectory: "checkpoints/mainnet/",
localization: nil
)?
.deletingLastPathComponent() ?? Checkpoint.mainnetCheckpointDirectory
case .testnet:
return Bundle.module.url(
forResource: "280000",
withExtension: "json",
subdirectory: "checkpoints/testnet/",
localization: nil
)?
.deletingLastPathComponent() ?? Checkpoint.testnetCheckpointDirectory
}
})
}
34 changes: 34 additions & 0 deletions Sources/ZcashLightClientKit/Checkpoint/CheckpointSource.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// CheckpointSource.swift
//
//
// Created by Francisco Gindre on 2023-10-30.
//

import Foundation

/// A protocol that abstracts the requirements around obtaining wallet checkpoints
/// (also known as TreeStates).
protocol CheckpointSource {
/// `NetworkType` of this Checkpoint source
var network: NetworkType { get }

/// The `Checkpoint` that represents the block in which Sapling was activated
var saplingActivation: Checkpoint { get }

/// Obtain the latest `Checkpoint` in terms of block height known by
/// this `CheckpointSource`. It is possible that the returned checkpoint
/// is not the latest checkpoint that exists in the blockchain.
/// - Returns a `Checkpoint` with the highest height known by this source
func latestKnownCheckpoint() -> Checkpoint

/// Obtain a `Checkpoint` in terms of a "wallet birthday". Wallet birthday
/// is estimated to be the latest height of the Zcash blockchain at the moment when the wallet was
/// created.
/// - Parameter height: Estimated or effective height known for the wallet birthday
/// - Returns: a `Checkpoint` that will allow the wallet to manage funds from the given `height`
/// onwards.
/// - Note: When the user knows the exact height of the first received funds for a wallet,
/// the effective birthday of that wallet is `transaction.height - 1`.
func birthday(for height: BlockHeight) -> Checkpoint
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// CheckpointSourceFactory.swift
//
//
// Created by Francisco Gindre on 2023-10-30.
//

import Foundation

struct CheckpointSourceFactory {
static func fromBundle(for network: NetworkType) -> CheckpointSource {
BundleCheckpointSource(network: network)
}
}
6 changes: 4 additions & 2 deletions Sources/ZcashLightClientKit/Initializer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ public class Initializer {
self.storage = container.resolve(CompactBlockRepository.self)
self.blockDownloaderService = container.resolve(BlockDownloaderService.self)
self.network = network
self.walletBirthday = Checkpoint.birthday(with: 0, network: network).height
self.walletBirthday = container.resolve(CheckpointSource.self).saplingActivation.height
self.urlsParsingError = urlsParsingError
self.logger = container.resolve(Logger.self)
}
Expand Down Expand Up @@ -416,7 +416,9 @@ public class Initializer {
return .seedRequired
}

let checkpoint = Checkpoint.birthday(with: walletBirthday, network: network)
let checkpointSource = container.resolve(CheckpointSource.self)

let checkpoint = checkpointSource.birthday(for: walletBirthday)

self.walletBirthday = checkpoint.height

Expand Down
4 changes: 4 additions & 0 deletions Sources/ZcashLightClientKit/Synchronizer/Dependencies.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ enum Dependencies {
loggingPolicy: Initializer.LoggingPolicy = .default(.debug),
enableBackendTracing: Bool = false
) {
container.register(type: CheckpointSource.self, isSingleton: true) { _ in
return CheckpointSourceFactory.fromBundle(for: networkType)
}

container.register(type: Logger.self, isSingleton: true) { _ in
let logger: Logger
switch loggingPolicy {
Expand Down
10 changes: 6 additions & 4 deletions Tests/DarksideTests/ReOrgTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,9 @@ class ReOrgTests: ZcashTestCase {
let mockLatestHeight = BlockHeight(663200)
let targetLatestHeight = BlockHeight(663202)
let reOrgHeight = BlockHeight(663195)
let walletBirthday = Checkpoint.birthday(with: 663150, network: network).height

let checkpointSource = CheckpointSourceFactory.fromBundle(for: network.networkType)
let walletBirthday = checkpointSource.birthday(for: 663150).height

try await basicReOrgTest(
baseDataset: .beforeReOrg,
reorgDataset: .afterSmallReorg,
Expand All @@ -113,8 +114,9 @@ class ReOrgTests: ZcashTestCase {
let mockLatestHeight = BlockHeight(663200)
let targetLatestHeight = BlockHeight(663250)
let reOrgHeight = BlockHeight(663180)
let walletBirthday = Checkpoint.birthday(with: BlockHeight(663150), network: network).height

let checkpointSource = CheckpointSourceFactory.fromBundle(for: network.networkType)
let walletBirthday = checkpointSource.birthday(for: 663150).height

try await basicReOrgTest(
baseDataset: .beforeReOrg,
reorgDataset: .afterLargeReorg,
Expand Down
3 changes: 2 additions & 1 deletion Tests/DarksideTests/TransactionEnhancementTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ class TransactionEnhancementTests: ZcashTestCase {

waitExpectation = XCTestExpectation(description: "\(self.description) waitExpectation")

let birthday = Checkpoint.birthday(with: walletBirthday, network: network)
let checkpointSource = CheckpointSourceFactory.fromBundle(for: network.networkType)
let birthday = checkpointSource.birthday(for: walletBirthday)

let pathProvider = DefaultResourceProvider(network: network)
processorConfig = CompactBlockProcessor.Configuration(
Expand Down
Loading

0 comments on commit 5644f89

Please sign in to comment.