From 2fcfa6fdbe17586281d9271109a31b463ff92486 Mon Sep 17 00:00:00 2001 From: Francisco Gindre Date: Tue, 12 Jul 2022 16:36:12 -0300 Subject: [PATCH] [#398] Make WalletBirthday an internal type (#414) This commit makes Renames `WalletBirthday` to `Checkpoint` and makes Checkpoint an internal type. Public ocurrences of this type is replaced by `BlockHeight` (Int) and then retrieval of the Checkpoint is deferred to the appropiate place in the code Add an extension method to `BlockHeight` to get latest checkpoint height present in the SDK's bundle PR Suggestions: Add test for integer overflow on JSON File renamed from WalletBirthday to Checkpoint --- .../Processor/CompactBlockProcessor.swift | 5 +- ...tants.swift => Checkpoint+Constants.swift} | 22 ++--- ...mainnet.swift => Checkpoint+mainnet.swift} | 4 +- ...testnet.swift => Checkpoint+testnet.swift} | 5 +- Sources/ZcashLightClientKit/Initializer.swift | 17 ++-- .../Model/Checkpoint.swift | 80 ++++++++++++++++++ .../Model/WalletTypes.swift | 81 +------------------ .../Synchronizer/SDKSynchronizer.swift | 6 +- Tests/DarksideTests/ReOrgTests.swift | 4 +- .../TransactionEnhancementTests.swift | 4 +- .../BlockScanOperationTests.swift | 2 +- Tests/OfflineTests/BirthdayTests.swift | 44 +++++++--- Tests/OfflineTests/WalletTests.swift | 6 -- Tests/TestUtils/TestCoordinator.swift | 6 +- 14 files changed, 152 insertions(+), 134 deletions(-) rename Sources/ZcashLightClientKit/Constants/{WalletBirthday+Constants.swift => Checkpoint+Constants.swift} (84%) rename Sources/ZcashLightClientKit/Constants/{WalletBirthday+mainnet.swift => Checkpoint+mainnet.swift} (85%) rename Sources/ZcashLightClientKit/Constants/{WalletBirthday+testnet.swift => Checkpoint+testnet.swift} (84%) create mode 100644 Sources/ZcashLightClientKit/Model/Checkpoint.swift diff --git a/Sources/ZcashLightClientKit/Block/Processor/CompactBlockProcessor.swift b/Sources/ZcashLightClientKit/Block/Processor/CompactBlockProcessor.swift index 8d51af17..78104571 100644 --- a/Sources/ZcashLightClientKit/Block/Processor/CompactBlockProcessor.swift +++ b/Sources/ZcashLightClientKit/Block/Processor/CompactBlockProcessor.swift @@ -403,7 +403,10 @@ public class CompactBlockProcessor { config: Configuration( cacheDb: initializer.cacheDbURL, dataDb: initializer.dataDbURL, - walletBirthday: initializer.walletBirthday.height, + walletBirthday: Checkpoint.birthday( + with: initializer.walletBirthday, + network: initializer.network + ).height, network: initializer.network ), repository: initializer.transactionRepository, diff --git a/Sources/ZcashLightClientKit/Constants/WalletBirthday+Constants.swift b/Sources/ZcashLightClientKit/Constants/Checkpoint+Constants.swift similarity index 84% rename from Sources/ZcashLightClientKit/Constants/WalletBirthday+Constants.swift rename to Sources/ZcashLightClientKit/Constants/Checkpoint+Constants.swift index f1510e5b..00b97a0a 100644 --- a/Sources/ZcashLightClientKit/Constants/WalletBirthday+Constants.swift +++ b/Sources/ZcashLightClientKit/Constants/Checkpoint+Constants.swift @@ -7,8 +7,8 @@ import Foundation -public extension WalletBirthday { - static func birthday(with height: BlockHeight, network: ZcashNetwork) -> WalletBirthday { +extension Checkpoint { + static func birthday(with height: BlockHeight, network: ZcashNetwork) -> Checkpoint { let checkpointDirectoryURL = BundleCheckpointURLProvider.default.url(network.networkType) switch network.networkType { @@ -20,8 +20,8 @@ public extension WalletBirthday { } } -extension WalletBirthday { - static func birthday(with height: BlockHeight, checkpointDirectory: URL) -> WalletBirthday? { +extension Checkpoint { + static func birthday(with height: BlockHeight, checkpointDirectory: URL) -> Checkpoint? { return bestCheckpointHeight(for: height, checkpointDirectory: checkpointDirectory) .flatMap { checkpoint(height: $0, directory: checkpointDirectory) } } @@ -44,7 +44,7 @@ extension WalletBirthday { .last } - private static func checkpoint(height: BlockHeight, directory checkpointDirectory: URL) -> WalletBirthday? { + private static func checkpoint(height: BlockHeight, directory checkpointDirectory: URL) -> Checkpoint? { let url = checkpointDirectory .appendingPathComponent(String(height)) .appendingPathExtension("json") @@ -52,9 +52,9 @@ extension WalletBirthday { return try? checkpoint(at: url) } - private static func checkpoint(at url: URL) throws -> WalletBirthday { + private static func checkpoint(at url: URL) throws -> Checkpoint { let data = try Data(contentsOf: url) - let checkpoint = try JSONDecoder().decode(WalletBirthday.self, from: data) + let checkpoint = try JSONDecoder().decode(Checkpoint.self, from: data) return checkpoint } } @@ -79,9 +79,9 @@ extension BundleCheckpointURLProvider { static var iOS = BundleCheckpointURLProvider(url: { networkType in switch networkType { case .mainnet: - return WalletBirthday.mainnetCheckpointDirectory + return Checkpoint.mainnetCheckpointDirectory case .testnet: - return WalletBirthday.testnetCheckpointDirectory + return Checkpoint.testnetCheckpointDirectory } }) @@ -99,7 +99,7 @@ extension BundleCheckpointURLProvider { subdirectory: "checkpoints/mainnet/", localization: nil )? - .deletingLastPathComponent() ?? WalletBirthday.mainnetCheckpointDirectory + .deletingLastPathComponent() ?? Checkpoint.mainnetCheckpointDirectory case .testnet: return Bundle.module.url( forResource: "280000", @@ -107,7 +107,7 @@ extension BundleCheckpointURLProvider { subdirectory: "checkpoints/testnet/", localization: nil )? - .deletingLastPathComponent() ?? WalletBirthday.testnetCheckpointDirectory + .deletingLastPathComponent() ?? Checkpoint.testnetCheckpointDirectory } }) } diff --git a/Sources/ZcashLightClientKit/Constants/WalletBirthday+mainnet.swift b/Sources/ZcashLightClientKit/Constants/Checkpoint+mainnet.swift similarity index 85% rename from Sources/ZcashLightClientKit/Constants/WalletBirthday+mainnet.swift rename to Sources/ZcashLightClientKit/Constants/Checkpoint+mainnet.swift index dfe8c055..4bbcfc5d 100644 --- a/Sources/ZcashLightClientKit/Constants/WalletBirthday+mainnet.swift +++ b/Sources/ZcashLightClientKit/Constants/Checkpoint+mainnet.swift @@ -6,8 +6,8 @@ // import Foundation -extension WalletBirthday { - static let mainnetMin = WalletBirthday( +extension Checkpoint { + static let mainnetMin = Checkpoint( height: 419_200, hash: "00000000025a57200d898ac7f21e26bf29028bbe96ec46e05b2c17cc9db9e4f3", time: 1540779337, diff --git a/Sources/ZcashLightClientKit/Constants/WalletBirthday+testnet.swift b/Sources/ZcashLightClientKit/Constants/Checkpoint+testnet.swift similarity index 84% rename from Sources/ZcashLightClientKit/Constants/WalletBirthday+testnet.swift rename to Sources/ZcashLightClientKit/Constants/Checkpoint+testnet.swift index 79253b3d..7a9b5d98 100644 --- a/Sources/ZcashLightClientKit/Constants/WalletBirthday+testnet.swift +++ b/Sources/ZcashLightClientKit/Constants/Checkpoint+testnet.swift @@ -7,8 +7,8 @@ import Foundation -extension WalletBirthday { - static let testnetMin = WalletBirthday( +extension Checkpoint { + static let testnetMin = Checkpoint( height: 280000, hash: "000420e7fcc3a49d729479fb0b560dd7b8617b178a08e9e389620a9d1dd6361a", time: 1535262293, @@ -16,5 +16,4 @@ extension WalletBirthday { ) static let testnetCheckpointDirectory = Bundle.module.bundleURL.appendingPathComponent("checkpoints/testnet/") - } diff --git a/Sources/ZcashLightClientKit/Initializer.swift b/Sources/ZcashLightClientKit/Initializer.swift index 7c7e84de..fb1d0f46 100644 --- a/Sources/ZcashLightClientKit/Initializer.swift +++ b/Sources/ZcashLightClientKit/Initializer.swift @@ -78,7 +78,7 @@ public class Initializer { private(set) var downloader: CompactBlockDownloader private(set) var network: ZcashNetwork private(set) public var viewingKeys: [UnifiedViewingKey] - private(set) public var walletBirthday: WalletBirthday + private(set) public var walletBirthday: BlockHeight /** Constructs the Initializer @@ -168,7 +168,7 @@ public class Initializer { self.storage = storage self.downloader = CompactBlockDownloader(service: service, storage: storage) self.viewingKeys = viewingKeys - self.walletBirthday = WalletBirthday.birthday(with: walletBirthday, network: network) + self.walletBirthday = walletBirthday self.network = network } @@ -202,12 +202,13 @@ public class Initializer { } do { + let checkpoint = Checkpoint.birthday(with: self.walletBirthday, network: network) try rustBackend.initBlocksTable( dbData: dataDbURL, - height: Int32(walletBirthday.height), - hash: walletBirthday.hash, - time: walletBirthday.time, - saplingTree: walletBirthday.saplingTree, + height: Int32(checkpoint.height), + hash: checkpoint.hash, + time: checkpoint.time, + saplingTree: checkpoint.saplingTree, networkType: network.networkType ) } catch RustWeldingError.dataDbNotEmpty { @@ -216,9 +217,9 @@ public class Initializer { throw InitializerError.dataDbInitFailed } - let lastDownloaded = (try? downloader.storage.latestHeight()) ?? walletBirthday.height + let lastDownloaded = (try? downloader.storage.latestHeight()) ?? walletBirthday // resume from last downloaded block - lowerBoundHeight = max(walletBirthday.height, lastDownloaded) + lowerBoundHeight = max(walletBirthday, lastDownloaded) do { guard try rustBackend.initAccountsTable( diff --git a/Sources/ZcashLightClientKit/Model/Checkpoint.swift b/Sources/ZcashLightClientKit/Model/Checkpoint.swift new file mode 100644 index 00000000..29cc5eb9 --- /dev/null +++ b/Sources/ZcashLightClientKit/Model/Checkpoint.swift @@ -0,0 +1,80 @@ +// +// Checkpoint.swift +// +// +// Created by Francisco Gindre on 7/12/22. +// + +import Foundation + +/// Represents the wallet's birthday which can be thought of as a checkpoint at the earliest moment in history where +/// transactions related to this wallet could exist. Ideally, this would correspond to the latest block height at the +/// time the wallet key was created. Worst case, the height of Sapling activation could be used (280000). +/// +/// Knowing a wallet's birthday can significantly reduce the amount of data that it needs to download because none of +/// the data before that height needs to be scanned for transactions. However, we do need the Sapling tree data in +/// order to construct valid transactions from that point forward. This birthday contains that tree data, allowing us +/// to avoid downloading all the compact blocks required in order to generate it. +/// +/// New wallets can ignore any blocks created before their birthday. +/// +/// - Parameters: +/// - height: the height at the time the wallet was born +/// - hash: the block hash corresponding to the given height +/// - time: the time the wallet was born, in seconds +/// - saplingTree: the sapling tree corresponding to the given height. This takes around 15 minutes of processing to generate from scratch because all blocks since activation need to be considered. So when it is calculated in advance it can save the user a lot of time. +/// - orchardTree: the orchard tree corresponding to the given height. This field is optional since it won't be available prior +/// to NU5 activation height for the given network. +struct Checkpoint: Equatable { + private(set) var height: BlockHeight + private(set) var hash: String + private(set) var time: UInt32 + private(set) var saplingTree: String + private(set) var orchardTree: String? +} + +extension Checkpoint: Decodable { + public enum CodingKeys: String, CodingKey { + case height + case hash + case time + case saplingTree + case orchardTree + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.height = try Self.getHeight(from: container) + self.hash = try container.decode(String.self, forKey: .hash) + self.time = try container.decode(UInt32.self, forKey: .time) + self.saplingTree = try container.decode(String.self, forKey: .saplingTree) + self.orchardTree = try container.decodeIfPresent(String.self, forKey: .orchardTree) + } + + static func getHeight(from container: KeyedDecodingContainer) throws -> Int { + guard + let heightString = try? container.decode(String.self, forKey: .height), + let height = Int(heightString) + else { + throw DecodingError.typeMismatch( + String.self, + DecodingError.Context( + codingPath: [CodingKeys.height], + debugDescription: "expected height to be encoded as a string", + underlyingError: nil + ) + ) + } + return height + } +} + +public extension BlockHeight { + /// Useful when creating a new wallet to reduce sync times. + /// - Parameters: + /// - zcashNetwork: Network to use for the block height. + /// - Returns: The block height of the newest checkpoint known by the SDK. + static func ofLatestCheckpoint(network: ZcashNetwork) -> BlockHeight { + Checkpoint.birthday(with: BlockHeight.max, network: network).height + } +} diff --git a/Sources/ZcashLightClientKit/Model/WalletTypes.swift b/Sources/ZcashLightClientKit/Model/WalletTypes.swift index c0e0b222..0e650f77 100644 --- a/Sources/ZcashLightClientKit/Model/WalletTypes.swift +++ b/Sources/ZcashLightClientKit/Model/WalletTypes.swift @@ -1,89 +1,10 @@ // // WalletTypes.swift -// Pods +// // // Created by Francisco Gindre on 4/6/21. // - -/// Represents the wallet's birthday which can be thought of as a checkpoint at the earliest moment in history where -/// transactions related to this wallet could exist. Ideally, this would correspond to the latest block height at the -/// time the wallet key was created. Worst case, the height of Sapling activation could be used (280000). -/// -/// Knowing a wallet's birthday can significantly reduce the amount of data that it needs to download because none of -/// the data before that height needs to be scanned for transactions. However, we do need the Sapling tree data in -/// order to construct valid transactions from that point forward. This birthday contains that tree data, allowing us -/// to avoid downloading all the compact blocks required in order to generate it. -/// -/// New wallets can ignore any blocks created before their birthday. -/// -/// - Parameters: -/// - height: the height at the time the wallet was born -/// - hash: the block hash corresponding to the given height -/// - time: the time the wallet was born, in seconds -/// - saplingTree: the sapling tree corresponding to the given height. This takes around 15 minutes of processing to generate from scratch because all blocks since activation need to be considered. So when it is calculated in advance it can save the user a lot of time. -/// - orchardTree: the orchard tree corresponding to the given height. This field is optional since it won't be available prior -/// to NU5 activation height for the given network. -public struct WalletBirthday: Equatable { - public private(set) var height: BlockHeight = -1 - public private(set) var hash: String = "" - public private(set) var time: UInt32 = 0 - public private(set) var saplingTree: String = "" - public private(set) var orchardTree: String? -} - - -extension WalletBirthday: Decodable { - // let height: BlockHeight - // let hash: String - // let time: UInt32 - // let saplingTree: String - // let orchardTree: String? - // func walletBirthday() -> WalletBirthday { - // WalletBirthday( - // height: height, - // hash: hash, - // time: time, - // tree: tree - // ) - // } - - public enum CodingKeys: String, CodingKey { - case height - case hash - case time - case saplingTree - case orchardTree - } - - public init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - self.height = try Self.getHeight(from: container) - self.hash = try container.decode(String.self, forKey: .hash) - self.time = try container.decode(UInt32.self, forKey: .time) - self.saplingTree = try container.decode(String.self, forKey: .saplingTree) - self.orchardTree = try container.decodeIfPresent(String.self, forKey: .orchardTree) - } - - static func getHeight(from container: KeyedDecodingContainer) throws -> Int { - guard - let heightString = try? container.decode(String.self, forKey: .height), - let height = Int(heightString) - else { - throw DecodingError.typeMismatch( - String.self, - DecodingError.Context( - codingPath: [CodingKeys.height], - debugDescription: "expected height to be encoded as a string", - underlyingError: nil - ) - ) - } - return height - } -} - - /** Groups a Sapling Extended Full Viewing Key an a tranparent address extended public key. */ diff --git a/Sources/ZcashLightClientKit/Synchronizer/SDKSynchronizer.swift b/Sources/ZcashLightClientKit/Synchronizer/SDKSynchronizer.swift index fa22999f..e59164ba 100644 --- a/Sources/ZcashLightClientKit/Synchronizer/SDKSynchronizer.swift +++ b/Sources/ZcashLightClientKit/Synchronizer/SDKSynchronizer.swift @@ -137,7 +137,7 @@ public class SDKSynchronizer: Synchronizer { self.transactionRepository = transactionRepository self.utxoRepository = utxoRepository self.blockProcessor = blockProcessor - self.latestScannedHeight = (try? transactionRepository.lastScannedHeight()) ?? initializer.walletBirthday.height + self.latestScannedHeight = (try? transactionRepository.lastScannedHeight()) ?? initializer.walletBirthday self.network = initializer.network self.subscribeToProcessorNotifications(blockProcessor) } @@ -149,12 +149,12 @@ public class SDKSynchronizer: Synchronizer { public func initialize() throws { try self.initializer.initialize() - try self.blockProcessor.setStartHeight(initializer.walletBirthday.height) + try self.blockProcessor.setStartHeight(initializer.walletBirthday) } public func prepare() throws { try self.initializer.initialize() - try self.blockProcessor.setStartHeight(initializer.walletBirthday.height) + try self.blockProcessor.setStartHeight(initializer.walletBirthday) self.status = .disconnected } diff --git a/Tests/DarksideTests/ReOrgTests.swift b/Tests/DarksideTests/ReOrgTests.swift index 45a8a053..d0063979 100644 --- a/Tests/DarksideTests/ReOrgTests.swift +++ b/Tests/DarksideTests/ReOrgTests.swift @@ -88,7 +88,7 @@ class ReOrgTests: XCTestCase { let mockLatestHeight = BlockHeight(663200) let targetLatestHeight = BlockHeight(663202) let reOrgHeight = BlockHeight(663195) - let walletBirthday = WalletBirthday.birthday(with: 663150, network: network).height + let walletBirthday = Checkpoint.birthday(with: 663150, network: network).height try basicReOrgTest( baseDataset: .beforeReOrg, @@ -104,7 +104,7 @@ class ReOrgTests: XCTestCase { let mockLatestHeight = BlockHeight(663200) let targetLatestHeight = BlockHeight(663250) let reOrgHeight = BlockHeight(663180) - let walletBirthday = WalletBirthday.birthday(with: BlockHeight(663150), network: network).height + let walletBirthday = Checkpoint.birthday(with: BlockHeight(663150), network: network).height try basicReOrgTest( baseDataset: .beforeReOrg, diff --git a/Tests/DarksideTests/TransactionEnhancementTests.swift b/Tests/DarksideTests/TransactionEnhancementTests.swift index 439fca93..575f8b90 100644 --- a/Tests/DarksideTests/TransactionEnhancementTests.swift +++ b/Tests/DarksideTests/TransactionEnhancementTests.swift @@ -54,7 +54,7 @@ class TransactionEnhancementTests: XCTestCase { waitExpectation = XCTestExpectation(description: "\(self.description) waitExpectation") - let birthday = WalletBirthday.birthday(with: walletBirthday, network: network) + let birthday = Checkpoint.birthday(with: walletBirthday, network: network) let config = CompactBlockProcessor.Configuration.standard(for: self.network, walletBirthday: birthday.height) let rustBackend = ZcashRustBackend.self @@ -125,7 +125,7 @@ class TransactionEnhancementTests: XCTestCase { func testBasicEnhacement() throws { let targetLatestHeight = BlockHeight(663250) - let walletBirthday = WalletBirthday.birthday(with: 663151, network: network).height + let walletBirthday = Checkpoint.birthday(with: 663151, network: network).height try basicEnhancementTest(latestHeight: targetLatestHeight, walletBirthday: walletBirthday) } diff --git a/Tests/NetworkTests/BlockScanOperationTests.swift b/Tests/NetworkTests/BlockScanOperationTests.swift index e30e7378..0a1aab2b 100644 --- a/Tests/NetworkTests/BlockScanOperationTests.swift +++ b/Tests/NetworkTests/BlockScanOperationTests.swift @@ -24,7 +24,7 @@ class BlockScanOperationTests: XCTestCase { extpub: "02075a7f5f7507d64022dad5954849f216b0f1b09b2d588be663d8e7faeb5aaf61" ) - var walletBirthDay = WalletBirthday.birthday( + var walletBirthDay = Checkpoint.birthday( with: 1386000, network: ZcashNetworkBuilder.network(for: .testnet) ) diff --git a/Tests/OfflineTests/BirthdayTests.swift b/Tests/OfflineTests/BirthdayTests.swift index 0675309b..70fcb4ed 100644 --- a/Tests/OfflineTests/BirthdayTests.swift +++ b/Tests/OfflineTests/BirthdayTests.swift @@ -3,12 +3,12 @@ import XCTest class BirthdayTests: XCTestCase { func test_BirthdayGetsMostRecentCheckpointBelowIt_Testnet() throws { - let birthday = WalletBirthday.birthday( + let birthday = Checkpoint.birthday( with: 1530003, network: ZcashNetworkBuilder.network(for: .testnet) ) - let expected = WalletBirthday( + let expected = Checkpoint( height: 1530000, hash: "0011f78082f26747e02f0ab3525dc34d8df8f69dde273f462fcbf08fe2aa14d6", time: 1629030383, @@ -19,12 +19,12 @@ class BirthdayTests: XCTestCase { } func test_BirthdayGetsMostRecentCheckpointBelowIt_Mainnet() throws { - let birthday = WalletBirthday.birthday( + let birthday = Checkpoint.birthday( with: 1340004, network: ZcashNetworkBuilder.network(for: .mainnet) ) - let expected = WalletBirthday( + let expected = Checkpoint( height: 1340000, hash: "00000000031bc547da975ebd77d9113b178053f88fb6a1d8511b4f8962c21c4b", time: 1627846248, @@ -35,12 +35,12 @@ class BirthdayTests: XCTestCase { } func test_startBirthdayIsGivenIfTooLow_Testnet() throws { - let birthday = WalletBirthday.birthday( + let birthday = Checkpoint.birthday( with: 4, network: ZcashNetworkBuilder.network(for: .testnet) ) - let expected = WalletBirthday( + let expected = Checkpoint( height: 280000, hash: "000420e7fcc3a49d729479fb0b560dd7b8617b178a08e9e389620a9d1dd6361a", time: 1535262293, @@ -51,12 +51,12 @@ class BirthdayTests: XCTestCase { } func test_startBirthdayIsGivenIfTooLow_Mainnet() throws { - let birthday = WalletBirthday.birthday( + let birthday = Checkpoint.birthday( with: 4, network: ZcashNetworkBuilder.network(for: .mainnet) ) - let expected = WalletBirthday( + let expected = Checkpoint( height: 419200, hash: "00000000025a57200d898ac7f21e26bf29028bbe96ec46e05b2c17cc9db9e4f3", time: 1540779337, @@ -69,7 +69,7 @@ class BirthdayTests: XCTestCase { func test_orchardTreeIsNotNilOnActivation_Mainnet() throws { let activationHeight = 1687104 - let birthday = WalletBirthday.birthday( + let birthday = Checkpoint.birthday( with: activationHeight, network: ZcashNetworkBuilder.network(for: .mainnet) ) @@ -81,7 +81,7 @@ class BirthdayTests: XCTestCase { func test_orchardTreeIsNilBeforeActivation_Mainnet() throws { let activationHeight = 1_687_104 - let birthday = WalletBirthday.birthday( + let birthday = Checkpoint.birthday( with: activationHeight - 1, network: ZcashNetworkBuilder.network(for: .mainnet) ) @@ -92,7 +92,7 @@ class BirthdayTests: XCTestCase { func test_orchardTreeIsNotNilOnActivation_Testnet() throws { let activationHeight = 1_842_420 - let birthday = WalletBirthday.birthday( + let birthday = Checkpoint.birthday( with: activationHeight, network: ZcashNetworkBuilder.network(for: .testnet) ) @@ -104,11 +104,31 @@ class BirthdayTests: XCTestCase { func test_orchardTreeIsNilBeforeActivation_Testnet() throws { let activationHeight = 1687104 - let birthday = WalletBirthday.birthday( + let birthday = Checkpoint.birthday( with: activationHeight - 1, network: ZcashNetworkBuilder.network(for: .testnet) ) XCTAssertNil(birthday.orchardTree) } + + func test_CheckpointParsingFailsIfIntegerOverflows() throws { + let jsonData = Self.integerOverflowJSON.data(using: .utf8)! + let decoder = JSONDecoder() + + XCTAssertThrowsError(try decoder.decode(Checkpoint.self, from: jsonData)) + } + + /// this has height with `Int64.max + 1` + static let integerOverflowJSON: String = + """ + { + "network": "main", + "height": "9223372036854775808", + "hash": "0000000000aefe1d3aaa6908bd9fe99d476afd72ec49be60090d6ee48f9272bf", + "time": 1656499365, + "saplingTree": "0175848b81090fee135ed81677520ee555e72814ec47d77107779f1d958f72903001a5f61227d13a351d6b9744e572fbddfa60de77650f3b355838771ded6e929a401400000000016a6c28ea8d49d0b32f9c8fad56f239fd6f1dabd0eb2771d7877d4b3d714aee6b0001e4cf1a347436f1b700976cfa387833d3dd53eb3e9e7201d68d1fa15735db411e0140e3461535e2391a82f6cc29221432f53cdc62c697f36cd1077bc5c40bed2d230138cbb3e580d3292e0c783e8e37077be9a1e2c7c95cbc6a4a58cf6f51af755170012896f2157ea42af3ce58fa133917712564a148708702ba3dc5235e49ae36be15000001b9db8a4ba52f5cdea9d142ce71601c2890e9dc306018b4a730a9c8c0a7e258260000000134e093f073973de750e85daff57c07a102dbc7bbc77436e99b0074f36f1cbf130000015ec9e9b1295908beed437df4126032ca57ada8e3ebb67067cd22a73c79a84009", + "orchardTree": "01492e49f873e033c8baf296de1a78f34618d2ac1a64eca3769b93918da66e3f37001f000000018d8f2180ce6978074be7b5bec41e993232ac73797ca80ae32f062a5d83ee363c00000114764dc108e86dc2c6d7c20a6e1759027d87029b5dc7e1e6b5be970ecbed913a01186d95ac66b184f8844e57fece62a4d64cfeb73e7ed6e99146e79aacae7f5e00012f9cd86767aea1f11147f65c588f94dce188315c40b22c0fa8751365ba453c280001130cfb41380fdd7836985e2c4c488fdc3d1d1bd4390f350f0e1b8a448f47ac1c012bcbdd308beca04006b18928c4418aad2b3650677289b1b45ea5a21095c5310301100ed4d0a8a440b03f1254ce1efb2d82a94cf001cffa0e7fd6ada813a2688b240130a69de998b87aebcd4c556503a45e559a422ecfbdf2f0e6318a8427e41a7b09017676cfe97afff13797f82f8d631bd29edde424854139c64ab8241e2a2331551401da371f0d3294843fd8f645019a04c07607342c70cf4cc793068355eaccdd671601bc79f0119f97113775379bf58f3f5d9d122766909b797947127b28248ff0720501c1eb3aa1717c2a696ce0aba6c5b5bda44c4eda5cf69ae376cc8b334a2c23fb2b0001374feb2041bfd423c6cc3e064ee2b4705748a082836d39dd723515357fb06e300000000000000000000000" + } + """ } diff --git a/Tests/OfflineTests/WalletTests.swift b/Tests/OfflineTests/WalletTests.swift index b12001a7..000d9c37 100644 --- a/Tests/OfflineTests/WalletTests.swift +++ b/Tests/OfflineTests/WalletTests.swift @@ -57,9 +57,3 @@ class WalletTests: XCTestCase { // XCTAssertNoThrow( try FileManager.default.removeItem(at: cacheData!) ) } } - -enum WalletBirthdayProvider { - static var testBirthday: WalletBirthday { - WalletBirthday() - } -} diff --git a/Tests/TestUtils/TestCoordinator.swift b/Tests/TestUtils/TestCoordinator.swift index e6aa08dd..512c3a75 100644 --- a/Tests/TestUtils/TestCoordinator.swift +++ b/Tests/TestUtils/TestCoordinator.swift @@ -125,7 +125,7 @@ class TestCoordinator { outputParamsURL: try __outputParamsURL(), spendingKey: spendingKey, unifiedViewingKey: unifiedViewingKey, - walletBirthday: WalletBirthday.birthday(with: birthday, network: network), + walletBirthday: Checkpoint.birthday(with: birthday, network: network), network: network, loggerProxy: SampleLogger(logLevel: .debug) ) @@ -289,7 +289,7 @@ enum TestSynchronizerBuilder { outputParamsURL: URL, spendingKey: String, unifiedViewingKey: UnifiedViewingKey, - walletBirthday: WalletBirthday, + walletBirthday: Checkpoint, network: ZcashNetwork, loggerProxy: Logger? = nil ) throws -> (spendingKeys: [String]?, synchronizer: SDKSynchronizer) { @@ -361,7 +361,7 @@ enum TestSynchronizerBuilder { spendParamsURL: URL, outputParamsURL: URL, seedBytes: [UInt8], - walletBirthday: WalletBirthday, + walletBirthday: Checkpoint, network: ZcashNetwork, loggerProxy: Logger? = nil ) throws -> (spendingKeys: [String]?, synchronizer: SDKSynchronizer) {