From 6625ffd71150cfa2d2f3bea8fe0fafbd6a3f21ea Mon Sep 17 00:00:00 2001 From: Francisco Gindre Date: Mon, 30 Oct 2023 15:49:15 -0300 Subject: [PATCH] Create a Dependency that can load checkpoints from bundle Closes #1315 This PR introduces small changes on each commit. Things done: rename Checkpoint+Constants to Checkpoint+helpers Move `Checkpoint` from Model folder to Checkpoint folder Remove unused function `ofLatestCheckpoint` from BlockHeight Create a protocol called `CheckpointSource` that contains the relevant functionality to get checkpoints from Bundle Create a set of tests that check that functionality is maintained when a `CheckpointSource` is used instead of Checkpoint helpers Implement `BundleCheckpointSource` and add Tests Code clean up: move `BundleCheckpointURLProvider` to its own file Code clean up: `Checkpoint+helpers` match file header Replace use of `Checkpoint.birthday(with:network)` with CheckpointSource Revert "Remove unused function `ofLatestCheckpoint` from BlockHeight" addresses PR comment from @daira This reverts commit d0e154ded71bb61ac2dd6ac669741500a8d12dfc, since it modifies a public API and it was not the goal of this PR. Update Sources/ZcashLightClientKit/Checkpoint/BundleCheckpointSource.swift Use a decent Date Format Co-authored-by: Daira Emma Hopwood Improve documentation on BundleCheckpointURLProvider Co-authored-by: Daira Emma Hopwood Improve documentation on BundleCheckpointURLProvider Co-authored-by: Daira Emma Hopwood use YYYY-mm-dd on file header author: @daira Co-authored-by: Daira Emma Hopwood Add test that verifies that the exact height is returned if available --- .../Checkpoint/BundleCheckpointSource.swift | 38 ++++ .../BundleCheckpointURLProvider.swift | 59 ++++++ .../Checkpoint+helpers.swift} | 53 +----- .../{Model => Checkpoint}/Checkpoint.swift | 0 .../Checkpoint/CheckpointSource.swift | 34 ++++ .../Checkpoint/CheckpointSourceFactory.swift | 14 ++ Sources/ZcashLightClientKit/Initializer.swift | 6 +- .../Synchronizer/Dependencies.swift | 4 + Tests/DarksideTests/ReOrgTests.swift | 10 +- .../TransactionEnhancementTests.swift | 3 +- .../OfflineTests/CheckpointSourceTests.swift | 174 ++++++++++++++++++ .../OfflineTests/ZcashRustBackendTests.swift | 3 +- 12 files changed, 338 insertions(+), 60 deletions(-) create mode 100644 Sources/ZcashLightClientKit/Checkpoint/BundleCheckpointSource.swift create mode 100644 Sources/ZcashLightClientKit/Checkpoint/BundleCheckpointURLProvider.swift rename Sources/ZcashLightClientKit/{Constants/Checkpoint+Constants.swift => Checkpoint/Checkpoint+helpers.swift} (52%) rename Sources/ZcashLightClientKit/{Model => Checkpoint}/Checkpoint.swift (100%) create mode 100644 Sources/ZcashLightClientKit/Checkpoint/CheckpointSource.swift create mode 100644 Sources/ZcashLightClientKit/Checkpoint/CheckpointSourceFactory.swift create mode 100644 Tests/OfflineTests/CheckpointSourceTests.swift diff --git a/Sources/ZcashLightClientKit/Checkpoint/BundleCheckpointSource.swift b/Sources/ZcashLightClientKit/Checkpoint/BundleCheckpointSource.swift new file mode 100644 index 00000000..92eaa501 --- /dev/null +++ b/Sources/ZcashLightClientKit/Checkpoint/BundleCheckpointSource.swift @@ -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 + } +} diff --git a/Sources/ZcashLightClientKit/Checkpoint/BundleCheckpointURLProvider.swift b/Sources/ZcashLightClientKit/Checkpoint/BundleCheckpointURLProvider.swift new file mode 100644 index 00000000..ebf90599 --- /dev/null +++ b/Sources/ZcashLightClientKit/Checkpoint/BundleCheckpointURLProvider.swift @@ -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 + } + }) +} diff --git a/Sources/ZcashLightClientKit/Constants/Checkpoint+Constants.swift b/Sources/ZcashLightClientKit/Checkpoint/Checkpoint+helpers.swift similarity index 52% rename from Sources/ZcashLightClientKit/Constants/Checkpoint+Constants.swift rename to Sources/ZcashLightClientKit/Checkpoint/Checkpoint+helpers.swift index 88fd7e6d..e4d6ca84 100644 --- a/Sources/ZcashLightClientKit/Constants/Checkpoint+Constants.swift +++ b/Sources/ZcashLightClientKit/Checkpoint/Checkpoint+helpers.swift @@ -1,5 +1,5 @@ // -// WalletBirthday+Constants.swift +// Checkpoint+helpers.swift // ZcashLightClientKit // // Created by Francisco Gindre on 7/28/21. @@ -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 - } - }) -} diff --git a/Sources/ZcashLightClientKit/Model/Checkpoint.swift b/Sources/ZcashLightClientKit/Checkpoint/Checkpoint.swift similarity index 100% rename from Sources/ZcashLightClientKit/Model/Checkpoint.swift rename to Sources/ZcashLightClientKit/Checkpoint/Checkpoint.swift diff --git a/Sources/ZcashLightClientKit/Checkpoint/CheckpointSource.swift b/Sources/ZcashLightClientKit/Checkpoint/CheckpointSource.swift new file mode 100644 index 00000000..3eca9806 --- /dev/null +++ b/Sources/ZcashLightClientKit/Checkpoint/CheckpointSource.swift @@ -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 +} diff --git a/Sources/ZcashLightClientKit/Checkpoint/CheckpointSourceFactory.swift b/Sources/ZcashLightClientKit/Checkpoint/CheckpointSourceFactory.swift new file mode 100644 index 00000000..53608891 --- /dev/null +++ b/Sources/ZcashLightClientKit/Checkpoint/CheckpointSourceFactory.swift @@ -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) + } +} diff --git a/Sources/ZcashLightClientKit/Initializer.swift b/Sources/ZcashLightClientKit/Initializer.swift index e50ed95e..fcdd1572 100644 --- a/Sources/ZcashLightClientKit/Initializer.swift +++ b/Sources/ZcashLightClientKit/Initializer.swift @@ -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) } @@ -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 diff --git a/Sources/ZcashLightClientKit/Synchronizer/Dependencies.swift b/Sources/ZcashLightClientKit/Synchronizer/Dependencies.swift index c7bd01b3..407f724d 100644 --- a/Sources/ZcashLightClientKit/Synchronizer/Dependencies.swift +++ b/Sources/ZcashLightClientKit/Synchronizer/Dependencies.swift @@ -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 { diff --git a/Tests/DarksideTests/ReOrgTests.swift b/Tests/DarksideTests/ReOrgTests.swift index d82513cf..4451c16c 100644 --- a/Tests/DarksideTests/ReOrgTests.swift +++ b/Tests/DarksideTests/ReOrgTests.swift @@ -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, @@ -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, diff --git a/Tests/DarksideTests/TransactionEnhancementTests.swift b/Tests/DarksideTests/TransactionEnhancementTests.swift index 2a68adb2..52ff65ed 100644 --- a/Tests/DarksideTests/TransactionEnhancementTests.swift +++ b/Tests/DarksideTests/TransactionEnhancementTests.swift @@ -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( diff --git a/Tests/OfflineTests/CheckpointSourceTests.swift b/Tests/OfflineTests/CheckpointSourceTests.swift new file mode 100644 index 00000000..e43fbcba --- /dev/null +++ b/Tests/OfflineTests/CheckpointSourceTests.swift @@ -0,0 +1,174 @@ +// +// CheckpointSourceTests.swift +// +// +// Created by Francisco Gindre on 2023-10-30. +// +import XCTest +@testable import TestUtils +@testable import ZcashLightClientKit + +class CheckpointSourceTests: XCTestCase { + func test_BirthdayGetsMostRecentCheckpointPrecedingTheGivenHeight_Testnet() throws { + + let source = CheckpointSourceFactory.fromBundle(for: .testnet) + + let birthday = source.birthday(for: 1530003) + + let expected = Checkpoint( + height: 1530000, + hash: "0011f78082f26747e02f0ab3525dc34d8df8f69dde273f462fcbf08fe2aa14d6", + time: 1629030383, + saplingTree: """ + 0103ac57cbe96a0f86b78527aa69b21db02318e7e7a6995cbe497a107707825655001001623311941fc8cfac849331dca1ba89a60552eb9dbadd0019f8dfcb5f6ac6c906\ + 01b9a73d583be12b8e9c8a7616fe78a65469a2b91bdf02d411951fa261c9e1e64001e64e2365c8064f711643681da68b4fd626b28e5624abb9fb19d13208818b4d600133\ + 0c2415a69eddb56d7a0846f03f4c98936607d5c0e7f580748224bd2117e51200000149f61a12a3f8407f4f7bd3e4f619937fa1a09e984a5f7334fcd7734c4ba3e3690000\ + 0001bab80e68a5c63460d1e5c94ef540940792fa4703fa488b09fdfded97f8ec8a3d00013d2fd009bf8a22d68f720eac19c411c99014ed9c5f85d5942e15d1fc039e2868\ + 0001f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39 + """, + orchardTree: nil + ) + + XCTAssertEqual(birthday, expected) + } + + func test_BirthdayGetsMostRecentCheckpointPrecedingTheGivenHeight_Mainnet() throws { + let source = CheckpointSourceFactory.fromBundle(for: .mainnet) + + let birthday = source.birthday(for: 1340004) + + let expected = Checkpoint( + height: 1340000, + hash: "00000000031bc547da975ebd77d9113b178053f88fb6a1d8511b4f8962c21c4b", + time: 1627846248, + saplingTree: """ + 01ef55e131bf5e7d6737a6e353fe0ff246ba8938a264335457452db2c4023241590113f4e2a1f043d0a303c769d9aac5eeb8b6854d1a64d71b6b86cda2e0eeee07621301\ + 206a8d77952d4143cc5ba4d7943261e7145f0f138a81fe37c10e50a487487966012fb54cf3a70cccf01479fefc42e539c92a8215aead4179278cf1e8a302cb4868014574\ + 313eb9fd9ee592346fdf27752f698c1f629b044437853972e266e95b56020001be3f0fa5b20bbfa445293d588073dc27a856c92e9903831c6de4455f03d57a0401bb534b\ + 0af17c990f836204115aa17d4c2504fa0a675353ec7ae8a7d67510cc46012e2edeb7e5acb0d440dd5b500bec4a6efd6f53ba02c10e3883e23e53d7f91369000183c334e4\ + 55aeeeb82cceddbe832919324d7011418749fc9dea759cfa6c2cc21501f4a3504117d35efa15f57d5fdd19515b7fb1dd14c3b98b8a91685f0f788db330000000018846ec\ + 9170ad4e40a093cfb53162e5211d55377d8d22f826cde7783d30c1dd5f01b35fe4a943a47404f68db220c77b0573e13c3378a65c6f2396f93be7609d8f2a000125911f45\ + 24469c00ccb1ba69e64f0ee7380c8d17bbfc76ecd238421b86eb6e09000118f64df255c9c43db708255e7bf6bffd481e5c2f38fe9ed8f3d189f7f9cf2644 + """, + orchardTree: nil + ) + + XCTAssertEqual(birthday, expected) + } + + func test_BirthdayCheckpointGetsExactHeightIfAvailable_Testnet() throws { + + let source = CheckpointSourceFactory.fromBundle(for: .testnet) + + let birthday = source.birthday(for: 1520000) + + + let expected = Checkpoint( + height: 1520000, + hash: "0014a50344a6a43b02421286f6db15dad50cea54f3f0858f044ad0f1b845c395", + time: 1628358967, + saplingTree: "017d0620dbe96cb488e44dccfde260cf599c23c4ca689589d2e1ad743ec6770a6d00100000000000000000000001bab80e68a5c63460d1e5c94ef540940792fa4703fa488b09fdfded97f8ec8a3d00013d2fd009bf8a22d68f720eac19c411c99014ed9c5f85d5942e15d1fc039e28680001f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39", + orchardTree: nil + ) + + XCTAssertEqual(birthday, expected) + } + + func test_BirthdayCheckpointGetsExactHeightIfAvailable_Mainnet() throws { + let source = CheckpointSourceFactory.fromBundle(for: .mainnet) + + let birthday = source.birthday(for: 1340000) + + let expected = Checkpoint( + height: 1340000, + hash: "00000000031bc547da975ebd77d9113b178053f88fb6a1d8511b4f8962c21c4b", + time: 1627846248, + saplingTree: """ + 01ef55e131bf5e7d6737a6e353fe0ff246ba8938a264335457452db2c4023241590113f4e2a1f043d0a303c769d9aac5eeb8b6854d1a64d71b6b86cda2e0eeee07621301\ + 206a8d77952d4143cc5ba4d7943261e7145f0f138a81fe37c10e50a487487966012fb54cf3a70cccf01479fefc42e539c92a8215aead4179278cf1e8a302cb4868014574\ + 313eb9fd9ee592346fdf27752f698c1f629b044437853972e266e95b56020001be3f0fa5b20bbfa445293d588073dc27a856c92e9903831c6de4455f03d57a0401bb534b\ + 0af17c990f836204115aa17d4c2504fa0a675353ec7ae8a7d67510cc46012e2edeb7e5acb0d440dd5b500bec4a6efd6f53ba02c10e3883e23e53d7f91369000183c334e4\ + 55aeeeb82cceddbe832919324d7011418749fc9dea759cfa6c2cc21501f4a3504117d35efa15f57d5fdd19515b7fb1dd14c3b98b8a91685f0f788db330000000018846ec\ + 9170ad4e40a093cfb53162e5211d55377d8d22f826cde7783d30c1dd5f01b35fe4a943a47404f68db220c77b0573e13c3378a65c6f2396f93be7609d8f2a000125911f45\ + 24469c00ccb1ba69e64f0ee7380c8d17bbfc76ecd238421b86eb6e09000118f64df255c9c43db708255e7bf6bffd481e5c2f38fe9ed8f3d189f7f9cf2644 + """, + orchardTree: nil + ) + + XCTAssertEqual(birthday, expected) + } + + func test_startBirthdayIsGivenIfTooLow_Testnet() throws { + let source = CheckpointSourceFactory.fromBundle(for: .testnet) + + let birthday = source.birthday(for: 4) + + let expected = Checkpoint( + height: 280000, + hash: "000420e7fcc3a49d729479fb0b560dd7b8617b178a08e9e389620a9d1dd6361a", + time: 1535262293, + saplingTree: "000000", + orchardTree: nil + ) + + XCTAssertEqual(birthday, expected) + } + + func test_startBirthdayIsGivenIfTooLow_Mainnet() throws { + let source = CheckpointSourceFactory.fromBundle(for: .mainnet) + + let birthday = source.birthday(for: 4) + + let expected = Checkpoint( + height: 419200, + hash: "00000000025a57200d898ac7f21e26bf29028bbe96ec46e05b2c17cc9db9e4f3", + time: 1540779337, + saplingTree: "000000", + orchardTree: nil + ) + + XCTAssertEqual(birthday, expected) + } + + func test_orchardTreeIsNotNilOnActivation_Mainnet() throws { + let activationHeight = 1_687_104 + + let source = CheckpointSourceFactory.fromBundle(for: .mainnet) + + let birthday = source.birthday(for: activationHeight) + + XCTAssertEqual(birthday.height, activationHeight) + XCTAssertEqual(birthday.orchardTree, "000000") + } + + func test_orchardTreeIsNilBeforeActivation_Mainnet() throws { + let activationHeight = 1_687_104 + + let source = CheckpointSourceFactory.fromBundle(for: .mainnet) + + let birthday = source.birthday(for: activationHeight - 1) + + XCTAssertNil(birthday.orchardTree) + } + + func test_orchardTreeIsNotNilOnActivation_Testnet() throws { + let activationHeight = 1_842_420 + + let source = CheckpointSourceFactory.fromBundle(for: .testnet) + + let birthday = source.birthday(for: activationHeight) + + XCTAssertEqual(birthday.height, activationHeight) + XCTAssertEqual(birthday.orchardTree, "000000") + } + + func test_orchardTreeIsNilBeforeActivation_Testnet() throws { + let activationHeight = 1_687_104 + + let source = CheckpointSourceFactory.fromBundle(for: .testnet) + + let birthday = source.birthday(for: activationHeight - 1) + + XCTAssertNil(birthday.orchardTree) + } +} diff --git a/Tests/OfflineTests/ZcashRustBackendTests.swift b/Tests/OfflineTests/ZcashRustBackendTests.swift index f4bbd768..4b77e426 100644 --- a/Tests/OfflineTests/ZcashRustBackendTests.swift +++ b/Tests/OfflineTests/ZcashRustBackendTests.swift @@ -107,7 +107,8 @@ class ZcashRustBackendTests: XCTestCase { let initResult = try await rustBackend.initDataDb(seed: seed) XCTAssertEqual(initResult, .success) - let treeState = Checkpoint.birthday(with: 1234567, network: ZcashMainnet()).treeState() + let checkpointSource = CheckpointSourceFactory.fromBundle(for: .mainnet) + let treeState = checkpointSource.birthday(for: 1234567).treeState() let usk = try await rustBackend.createAccount(seed: seed, treeState: treeState, recoverUntil: nil) XCTAssertEqual(usk.account, 0)