From 7c38c61cc947e54e81d08d3522fc5434de60d815 Mon Sep 17 00:00:00 2001 From: Francisco Gindre Date: Fri, 13 Sep 2019 17:57:41 -0300 Subject: [PATCH] (Failing) init Wallet test --- ZcashLightClientKit.xcodeproj/project.pbxproj | 24 ++++++ ZcashLightClientKit/Constants.swift | 74 +++++++++++++++++++ .../Providers/SeedProvider.swift | 13 ++++ .../Rust/ZcashRustBackend.swift | 28 +++---- ZcashLightClientKit/Wallet.swift | 62 ++++++++++++++++ ZcashLightClientKitTests/WalletTests.swift | 48 ++++++++++++ 6 files changed, 235 insertions(+), 14 deletions(-) create mode 100644 ZcashLightClientKit/Constants.swift create mode 100644 ZcashLightClientKit/Providers/SeedProvider.swift create mode 100644 ZcashLightClientKit/Wallet.swift create mode 100644 ZcashLightClientKitTests/WalletTests.swift diff --git a/ZcashLightClientKit.xcodeproj/project.pbxproj b/ZcashLightClientKit.xcodeproj/project.pbxproj index ff15ab23..ef8a6a08 100644 --- a/ZcashLightClientKit.xcodeproj/project.pbxproj +++ b/ZcashLightClientKit.xcodeproj/project.pbxproj @@ -8,6 +8,10 @@ /* Begin PBXBuildFile section */ 0B45933D22C612CB002A66BA /* ZcashRustBackendTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B45933C22C612CB002A66BA /* ZcashRustBackendTests.swift */; }; + 0DA33493232C0BC200CAC082 /* Wallet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DA33492232C0BC200CAC082 /* Wallet.swift */; }; + 0DA33496232C11DF00CAC082 /* SeedProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DA33495232C11DF00CAC082 /* SeedProvider.swift */; }; + 0DA33498232C1A8200CAC082 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DA33497232C1A8200CAC082 /* Constants.swift */; }; + 0DA3349A232C1B6F00CAC082 /* WalletTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DA33499232C1B6F00CAC082 /* WalletTests.swift */; }; 0DB456F4232A860200057720 /* ZcashCompactBlock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DB456F3232A860200057720 /* ZcashCompactBlock.swift */; }; 0DB456FD232A867800057720 /* service.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DB456F8232A867800057720 /* service.pb.swift */; }; 0DB456FE232A867800057720 /* service.grpc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DB456F9232A867800057720 /* service.grpc.swift */; }; @@ -46,6 +50,10 @@ /* Begin PBXFileReference section */ 0B45933C22C612CB002A66BA /* ZcashRustBackendTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZcashRustBackendTests.swift; sourceTree = ""; }; + 0DA33492232C0BC200CAC082 /* Wallet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Wallet.swift; sourceTree = ""; }; + 0DA33495232C11DF00CAC082 /* SeedProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedProvider.swift; sourceTree = ""; }; + 0DA33497232C1A8200CAC082 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; + 0DA33499232C1B6F00CAC082 /* WalletTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletTests.swift; sourceTree = ""; }; 0DB456F3232A860200057720 /* ZcashCompactBlock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZcashCompactBlock.swift; sourceTree = ""; }; 0DB456F7232A867800057720 /* service.proto */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.protobuf; path = service.proto; sourceTree = ""; }; 0DB456F8232A867800057720 /* service.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = service.pb.swift; sourceTree = ""; }; @@ -102,6 +110,14 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 0DA33494232C11C600CAC082 /* Providers */ = { + isa = PBXGroup; + children = ( + 0DA33495232C11DF00CAC082 /* SeedProvider.swift */, + ); + path = Providers; + sourceTree = ""; + }; 0DB456F2232A85D900057720 /* Model */ = { isa = PBXGroup; children = ( @@ -191,12 +207,15 @@ 103AFE87228312A30074BC98 /* ZcashLightClientKit */ = { isa = PBXGroup; children = ( + 0DA33494232C11C600CAC082 /* Providers */, 0DB45703232AA03C00057720 /* Storage */, 0DB456F5232A863700057720 /* Service */, 103AFEA7228320F00074BC98 /* zcashlc */, 103AFE88228312A30074BC98 /* ZcashLightClientKit.h */, 0DB4570B232ACD1700057720 /* Rust */, 103AFE89228312A30074BC98 /* Info.plist */, + 0DA33492232C0BC200CAC082 /* Wallet.swift */, + 0DA33497232C1A8200CAC082 /* Constants.swift */, ); path = ZcashLightClientKit; sourceTree = ""; @@ -206,6 +225,7 @@ children = ( 103AFE93228312A30074BC98 /* ZcashLightClientKitTests.swift */, 0B45933C22C612CB002A66BA /* ZcashRustBackendTests.swift */, + 0DA33499232C1B6F00CAC082 /* WalletTests.swift */, 103AFE95228312A30074BC98 /* Info.plist */, ); path = ZcashLightClientKitTests; @@ -383,15 +403,18 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 0DA33493232C0BC200CAC082 /* Wallet.swift in Sources */, 0DB45713232AEAF200057720 /* LightWalletService.swift in Sources */, 0DB456FD232A867800057720 /* service.pb.swift in Sources */, 0DB45702232A86EF00057720 /* LightWalletGRPCService.swift in Sources */, + 0DA33496232C11DF00CAC082 /* SeedProvider.swift in Sources */, 0DB456FE232A867800057720 /* service.grpc.swift in Sources */, 103AFEA422831BB00074BC98 /* ZcashRustBackend.swift in Sources */, 0DB45709232AA81200057720 /* Protocolbuffer+Extensions.swift in Sources */, 0DB4570D232ACD3100057720 /* ZcashRustBackendWelding.swift in Sources */, 0DB45711232ADD4B00057720 /* CompactBlockStoring.swift in Sources */, 0DB45705232AA06200057720 /* Storage.swift in Sources */, + 0DA33498232C1A8200CAC082 /* Constants.swift in Sources */, 0DB456FF232A867800057720 /* compact_formats.pb.swift in Sources */, 0DB456F4232A860200057720 /* ZcashCompactBlock.swift in Sources */, ); @@ -402,6 +425,7 @@ buildActionMask = 2147483647; files = ( 0B45933D22C612CB002A66BA /* ZcashRustBackendTests.swift in Sources */, + 0DA3349A232C1B6F00CAC082 /* WalletTests.swift in Sources */, 103AFE94228312A30074BC98 /* ZcashLightClientKitTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/ZcashLightClientKit/Constants.swift b/ZcashLightClientKit/Constants.swift new file mode 100644 index 00000000..a7744669 --- /dev/null +++ b/ZcashLightClientKit/Constants.swift @@ -0,0 +1,74 @@ +// +// Constants.swift +// ZcashLightClientKit +// +// Created by Francisco Gindre on 13/09/2019. +// Copyright © 2019 Electric Coin Company. All rights reserved. +// + +import Foundation + +/** + * Miner's fee in zatoshi. + */ +let MINERS_FEE_ZATOSHI: UInt64 = 10_000 + +/** + * The number of zatoshi that equal 1 ZEC. + */ +let ZATOSHI_PER_ZEC: UInt64 = 100_000_000 + +/** + * The height of the first sapling block. When it comes to shielded transactions, we do not need to consider any blocks + * prior to this height, at all. + */ +let SAPLING_ACTIVATION_HEIGHT: UInt64 = 280_000 + +/** + * The theoretical maximum number of blocks in a reorg, due to other bottlenecks in the protocol design. + */ +let MAX_REORG_SIZE = 100 + +/** + * The amount of blocks ahead of the current height where new transactions are set to expire. This value is controlled + * by the rust backend but it is helpful to know what it is set to and shdould be kept in sync. + */ +let EXPIRY_OFFSET = 20 + +// +// Defaults +// + +/** + * Default size of batches of blocks to request from the compact block service. + */ +let DEFAULT_BATCH_SIZE = 100 + +/** + * Default amount of time, in milliseconds, to poll for new blocks. Typically, this should be about half the average + * block time. + */ +let DEFAULT_POLL_INTERVAL: UInt64 = 75_000 + +/** + * Default attempts at retrying. + */ +let DEFAULT_RETRIES = 5 + +/** + * The default maximum amount of time to wait during retry backoff intervals. Failed loops will never wait longer than + * this before retyring. + */ +let DEFAULT_MAX_BACKOFF_INTERVAL: UInt64 = 600_000 + +/** + * Default number of blocks to rewind when a chain reorg is detected. This should be large enough to recover from the + * reorg but smaller than the theoretical max reorg size of 100. + */ +let DEFAULT_REWIND_DISTANCE = 10 + +/** + * The number of blocks to allow before considering our data to be stale. This usually helps with what to do when + * returning from the background and is exposed via the Synchronizer's isStale function. + */ +let DEFAULT_STALE_TOLERANCE = 10 diff --git a/ZcashLightClientKit/Providers/SeedProvider.swift b/ZcashLightClientKit/Providers/SeedProvider.swift new file mode 100644 index 00000000..111374d0 --- /dev/null +++ b/ZcashLightClientKit/Providers/SeedProvider.swift @@ -0,0 +1,13 @@ +// +// SeedProvider.swift +// ZcashLightClientKit +// +// Created by Francisco Gindre on 13/09/2019. +// Copyright © 2019 Electric Coin Company. All rights reserved. +// + +import Foundation + +public protocol SeedProvider { + func seed() -> [UInt8] +} diff --git a/ZcashLightClientKit/Rust/ZcashRustBackend.swift b/ZcashLightClientKit/Rust/ZcashRustBackend.swift index cffca5b5..f4e313d3 100644 --- a/ZcashLightClientKit/Rust/ZcashRustBackend.swift +++ b/ZcashLightClientKit/Rust/ZcashRustBackend.swift @@ -9,9 +9,9 @@ import Foundation -public class ZcashRustBackend: ZcashRustBackendWelding { +class ZcashRustBackend: ZcashRustBackendWelding { - public static func getLastError() -> String? { + static func getLastError() -> String? { let errorLen = zcashlc_last_error_length() if errorLen > 0 { let error = UnsafeMutablePointer.allocate(capacity: Int(errorLen)) @@ -23,12 +23,12 @@ public class ZcashRustBackend: ZcashRustBackendWelding { } } - public static func initDataDb(dbData: URL) -> Bool { + static func initDataDb(dbData: URL) -> Bool { let dbData = dbData.osStr() return zcashlc_init_data_database(dbData.0, dbData.1) != 0 } - public static func initAccountsTable(dbData: URL, seed: [UInt8], accounts: Int32) -> [String]? { + static func initAccountsTable(dbData: URL, seed: [UInt8], accounts: Int32) -> [String]? { let dbData = dbData.osStr() let extsksCStr = zcashlc_init_accounts_table(dbData.0, dbData.1, seed, UInt(seed.count), accounts) if extsksCStr == nil { @@ -42,12 +42,12 @@ public class ZcashRustBackend: ZcashRustBackendWelding { return extsks } - public static func initBlocksTable(dbData: URL, height: Int32, hash: String, time: UInt32, saplingTree: String) -> Bool { + static func initBlocksTable(dbData: URL, height: Int32, hash: String, time: UInt32, saplingTree: String) -> Bool { let dbData = dbData.osStr() return zcashlc_init_blocks_table(dbData.0, dbData.1, height, [CChar](hash.utf8CString), time, [CChar](saplingTree.utf8CString)) != 0 } - public static func getAddress(dbData: URL, account: Int32) -> String? { + static func getAddress(dbData: URL, account: Int32) -> String? { let dbData = dbData.osStr() let addressCStr = zcashlc_get_address(dbData.0, dbData.1, account) @@ -60,17 +60,17 @@ public class ZcashRustBackend: ZcashRustBackendWelding { return address } - public static func getBalance(dbData: URL, account: Int32) -> Int64 { + static func getBalance(dbData: URL, account: Int32) -> Int64 { let dbData = dbData.osStr() return zcashlc_get_balance(dbData.0, dbData.1, account) } - public static func getVerifiedBalance(dbData: URL, account: Int32) -> Int64 { + static func getVerifiedBalance(dbData: URL, account: Int32) -> Int64 { let dbData = dbData.osStr() return zcashlc_get_verified_balance(dbData.0, dbData.1, account) } - public static func getReceivedMemoAsUTF8(dbData: URL, idNote: Int64) -> String? { + static func getReceivedMemoAsUTF8(dbData: URL, idNote: Int64) -> String? { let dbData = dbData.osStr() let memoCStr = zcashlc_get_received_memo_as_utf8(dbData.0, dbData.1, idNote) @@ -83,7 +83,7 @@ public class ZcashRustBackend: ZcashRustBackendWelding { return memo } - public static func getSentMemoAsUTF8(dbData: URL, idNote: Int64) -> String? { + static func getSentMemoAsUTF8(dbData: URL, idNote: Int64) -> String? { let dbData = dbData.osStr() let memoCStr = zcashlc_get_sent_memo_as_utf8(dbData.0, dbData.1, idNote) @@ -96,24 +96,24 @@ public class ZcashRustBackend: ZcashRustBackendWelding { return memo } - public static func validateCombinedChain(dbCache: URL, dbData: URL) -> Int32 { + static func validateCombinedChain(dbCache: URL, dbData: URL) -> Int32 { let dbCache = dbCache.osStr() let dbData = dbData.osStr() return zcashlc_validate_combined_chain(dbCache.0, dbCache.1, dbData.0, dbData.1) } - public static func rewindToHeight(dbData: URL, height: Int32) -> Bool { + static func rewindToHeight(dbData: URL, height: Int32) -> Bool { let dbData = dbData.osStr() return zcashlc_rewind_to_height(dbData.0, dbData.1, height) != 0 } - public static func scanBlocks(dbCache: URL, dbData: URL) -> Bool { + static func scanBlocks(dbCache: URL, dbData: URL) -> Bool { let dbCache = dbCache.osStr() let dbData = dbData.osStr() return zcashlc_scan_blocks(dbCache.0, dbCache.1, dbData.0, dbData.1) != 0 } - public static func sendToAddress(dbData: URL, account: Int32, extsk: String, to: String, value: Int64, memo: String?, spendParams: URL, outputParams: URL) -> Int64 { + static func sendToAddress(dbData: URL, account: Int32, extsk: String, to: String, value: Int64, memo: String?, spendParams: URL, outputParams: URL) -> Int64 { let dbData = dbData.osStr() let spendParams = spendParams.osStr() let outputParams = outputParams.osStr() diff --git a/ZcashLightClientKit/Wallet.swift b/ZcashLightClientKit/Wallet.swift new file mode 100644 index 00000000..2e6e13ae --- /dev/null +++ b/ZcashLightClientKit/Wallet.swift @@ -0,0 +1,62 @@ +// +// Wallet.swift +// ZcashLightClientKit +// +// Created by Francisco Gindre on 13/09/2019. +// Copyright © 2019 Electric Coin Company. All rights reserved. +// + +import Foundation +/** + Wrapper for the Rust backend. This class basically represents all the Rust-wallet + capabilities and the supporting data required to exercise those abilities. + */ +public class Wallet { + + private var rustBackend: ZcashRustBackendWelding.Type + private var dataDbURL: URL + private var paramDestination: URL + private var accountIDs: [Int] + private var seedProvider: SeedProvider + + init(rustWelding: ZcashRustBackendWelding.Type, dataDbURL: URL, paramDestination: URL, seedProvider: SeedProvider, accountIDs: [Int] = [0]) { + self.rustBackend = rustWelding.self + self.dataDbURL = dataDbURL + self.paramDestination = paramDestination + self.accountIDs = accountIDs + self.seedProvider = seedProvider + } + + + func initalize(firstRunStartHeight: UInt64 = SAPLING_ACTIVATION_HEIGHT) { + + } +} + + + +/** + 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. + + - Parameter height the height at the time the wallet was born + - Parameter hash the block hash corresponding to the given height + - Parameter time the time the wallet was born, in seconds + - Parameter tree 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. + */ +public struct WalletBirthday { + var height: Int64 = -1 + var hash: String = "" + var time: TimeInterval = -1 + var tree: String = "" +} diff --git a/ZcashLightClientKitTests/WalletTests.swift b/ZcashLightClientKitTests/WalletTests.swift new file mode 100644 index 00000000..425ab844 --- /dev/null +++ b/ZcashLightClientKitTests/WalletTests.swift @@ -0,0 +1,48 @@ +// +// WalletTests.swift +// ZcashLightClientKitTests +// +// Created by Francisco Gindre on 13/09/2019. +// Copyright © 2019 Electric Coin Company. All rights reserved. +// + +import Foundation +import XCTest +@testable import ZcashLightClientKit + +class WalletTests: XCTestCase { + + var dbData: URL! = nil + var paramDestination: URL! = nil + var cacheData: URL! = nil + + override func setUp() { + let dataDir = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true) + dbData = dataDir.appendingPathComponent("data.db") + cacheData = dataDir.appendingPathComponent("cache.db") + paramDestination = dataDir.appendingPathComponent("parameters") + } + + override func tearDown() { + if FileManager.default.fileExists(atPath: dbData.absoluteString) { + try! FileManager.default.trashItem(at: dbData, resultingItemURL: nil) + } + } + + func testWalletInitialization() { + + let wallet = Wallet(rustWelding: ZcashRustBackend.self, dataDbURL: dbData, paramDestination: paramDestination, seedProvider: SampleSeedProvider()) + + wallet.initalize() + + XCTAssertTrue(FileManager.default.fileExists(atPath: dbData.absoluteString)) + XCTAssertTrue(FileManager.default.fileExists(atPath: paramDestination.absoluteString)) + XCTAssertTrue(FileManager.default.fileExists(atPath: cacheData.absoluteString)) + } +} + +struct SampleSeedProvider: SeedProvider { + func seed() -> [UInt8] { + Array("seed".utf8) + } +}