parent
60ea9d6737
commit
3e2050d0f0
|
@ -0,0 +1,45 @@
|
||||||
|
whitelist_rules:
|
||||||
|
- closure_spacing
|
||||||
|
- colon
|
||||||
|
- empty_enum_arguments
|
||||||
|
- fatal_error_message
|
||||||
|
- force_cast
|
||||||
|
- force_try
|
||||||
|
- force_unwrapping
|
||||||
|
- implicitly_unwrapped_optional
|
||||||
|
- legacy_cggeometry_functions
|
||||||
|
- legacy_constant
|
||||||
|
- legacy_constructor
|
||||||
|
- legacy_nsgeometry_functions
|
||||||
|
- operator_usage_whitespace
|
||||||
|
- redundant_string_enum_value
|
||||||
|
- redundant_void_return
|
||||||
|
- return_arrow_whitespace
|
||||||
|
- trailing_newline
|
||||||
|
- type_name
|
||||||
|
- unused_closure_parameter
|
||||||
|
- unused_optional_binding
|
||||||
|
- vertical_whitespace
|
||||||
|
- void_return
|
||||||
|
- custom_rules
|
||||||
|
|
||||||
|
excluded:
|
||||||
|
- Carthage
|
||||||
|
- Pods
|
||||||
|
- ZcashLightClientKit/Service/ProtoBuf/compact_formats.pb.swift
|
||||||
|
- ZcashLightClientKit/Service/ProtoBuf/service.grpc.swift
|
||||||
|
- ZcashLightClientKit/Service/ProtoBuf/service.pb.swift
|
||||||
|
- Example
|
||||||
|
- ZcashLightClientKitTests
|
||||||
|
|
||||||
|
colon:
|
||||||
|
apply_to_dictionaries: false
|
||||||
|
|
||||||
|
indentation: 2
|
||||||
|
|
||||||
|
custom_rules:
|
||||||
|
no_objcMembers:
|
||||||
|
name: "@objcMembers"
|
||||||
|
regex: "@objcMembers"
|
||||||
|
message: "Explicitly use @objc on each member you want to expose to Objective-C"
|
||||||
|
severity: error
|
|
@ -43,6 +43,8 @@ The ```LIGHTWALLETD_ADDRESS``` environment variable can also be added to your sh
|
||||||
|
|
||||||
We advice setting this value as a secret variable on your CD/CI environment when possible
|
We advice setting this value as a secret variable on your CD/CI environment when possible
|
||||||
|
|
||||||
|
# Swiftlint
|
||||||
|
We don't like reinveing the wheel, so be gently borrowed swift lint rules from AirBnB which we find pretty cool and reasonable.
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
|
|
|
@ -401,6 +401,7 @@
|
||||||
buildPhases = (
|
buildPhases = (
|
||||||
103AFE80228312A30074BC98 /* Headers */,
|
103AFE80228312A30074BC98 /* Headers */,
|
||||||
103AFE9F2283152F0074BC98 /* ShellScript */,
|
103AFE9F2283152F0074BC98 /* ShellScript */,
|
||||||
|
0D3BA6B1235A420B00E0E0F4 /* ShellScript */,
|
||||||
103AFE81228312A30074BC98 /* Sources */,
|
103AFE81228312A30074BC98 /* Sources */,
|
||||||
103AFE82228312A30074BC98 /* Frameworks */,
|
103AFE82228312A30074BC98 /* Frameworks */,
|
||||||
103AFE83228312A30074BC98 /* Resources */,
|
103AFE83228312A30074BC98 /* Resources */,
|
||||||
|
@ -512,6 +513,23 @@
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "/usr/local/bin/carthage copy-frameworks\n";
|
shellScript = "/usr/local/bin/carthage copy-frameworks\n";
|
||||||
};
|
};
|
||||||
|
0D3BA6B1235A420B00E0E0F4 /* ShellScript */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "if which swiftlint >/dev/null; then\n swiftlint lint --config .swiftlint.yml\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n";
|
||||||
|
};
|
||||||
0DC64E65232848F10053EFAC /* ShellScript */ = {
|
0DC64E65232848F10053EFAC /* ShellScript */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import SQLite
|
import SQLite
|
||||||
|
|
||||||
|
|
||||||
struct CompactBlockStorage: CompactBlockDAO {
|
struct CompactBlockStorage: CompactBlockDAO {
|
||||||
var db: Connection
|
var db: Connection
|
||||||
|
|
||||||
|
@ -120,5 +119,4 @@ extension CompactBlockStorage: CompactBlockStoring {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@ class SQLiteStorage: Storage {
|
||||||
self.connection = connection
|
self.connection = connection
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func open(at path: String) throws {
|
func open(at path: String) throws {
|
||||||
do {
|
do {
|
||||||
connection = try Connection(path)
|
connection = try Connection(path)
|
||||||
|
@ -35,15 +34,13 @@ class SQLiteStorage: Storage {
|
||||||
func closeDatabase() {}
|
func closeDatabase() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Set schema version
|
Set schema version
|
||||||
*/
|
*/
|
||||||
// TODO: define a better way to do this
|
// TODO: define a better way to do this
|
||||||
extension Connection {
|
//extension Connection {
|
||||||
public var userVersion: Int32 {
|
// public var userVersion: Int32 {
|
||||||
get { return Int32(try! scalar("PRAGMA user_version") as! Int64)}
|
// get { return Int32(try scalar("PRAGMA user_version") as Int64)}
|
||||||
set { try! run("PRAGMA user_version = \(newValue)")}
|
// set { try! run("PRAGMA user_version = \(newValue)")}
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,6 @@ class CompactBlockDownloader {
|
||||||
|
|
||||||
extension CompactBlockDownloader: CompactBlockDownloading {
|
extension CompactBlockDownloader: CompactBlockDownloading {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Downloads and stores the given block range.
|
Downloads and stores the given block range.
|
||||||
Non-Blocking
|
Non-Blocking
|
||||||
|
@ -89,7 +88,6 @@ extension CompactBlockDownloader: CompactBlockDownloading {
|
||||||
try storage.write(blocks: blocks)
|
try storage.write(blocks: blocks)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func rewind(to height: BlockHeight, completion: @escaping (Error?) -> Void){
|
func rewind(to height: BlockHeight, completion: @escaping (Error?) -> Void){
|
||||||
|
|
||||||
storage.rewind(to: height) { (e) in
|
storage.rewind(to: height) { (e) in
|
||||||
|
@ -121,6 +119,4 @@ extension CompactBlockDownloader: CompactBlockDownloading {
|
||||||
try self.storage.latestHeight()
|
try self.storage.latestHeight()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ class CompactBlockScanningOperation: ZcashOperation {
|
||||||
|
|
||||||
private var cacheDb: URL
|
private var cacheDb: URL
|
||||||
private var dataDb: URL
|
private var dataDb: URL
|
||||||
init(rustWelding: ZcashRustBackendWelding.Type, cacheDb: URL, dataDb:URL) {
|
init(rustWelding: ZcashRustBackendWelding.Type, cacheDb: URL, dataDb: URL) {
|
||||||
rustBackend = rustWelding
|
rustBackend = rustWelding
|
||||||
self.cacheDb = cacheDb
|
self.cacheDb = cacheDb
|
||||||
self.dataDb = dataDb
|
self.dataDb = dataDb
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
enum CompactBlockProcessorError: Error {
|
enum CompactBlockProcessorError: Error {
|
||||||
case invalidConfiguration
|
case invalidConfiguration
|
||||||
case missingDbPath(path: String)
|
case missingDbPath(path: String)
|
||||||
|
@ -16,16 +15,14 @@ enum CompactBlockProcessorError: Error {
|
||||||
|
|
||||||
extension Notification.Name {
|
extension Notification.Name {
|
||||||
static let blockProcessorUpdated = Notification.Name(rawValue: "CompactBlockProcessorUpdated")
|
static let blockProcessorUpdated = Notification.Name(rawValue: "CompactBlockProcessorUpdated")
|
||||||
static let blockProcessorStarted = Notification.Name(rawValue:"CompactBlockProcessorStarted")
|
static let blockProcessorStarted = Notification.Name(rawValue: "CompactBlockProcessorStarted")
|
||||||
static let blockProcessorStopped = Notification.Name(rawValue:"CompactBlockProcessorStopped")
|
static let blockProcessorStopped = Notification.Name(rawValue: "CompactBlockProcessorStopped")
|
||||||
static let blockProcessorFinished = Notification.Name(rawValue:"CompactBlockProcessorFinished")
|
static let blockProcessorFinished = Notification.Name(rawValue: "CompactBlockProcessorFinished")
|
||||||
static let blockProcessorFailed = Notification.Name(rawValue:"CompactBlockProcessorFinished")
|
static let blockProcessorFailed = Notification.Name(rawValue: "CompactBlockProcessorFinished")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class CompactBlockProcessor {
|
class CompactBlockProcessor {
|
||||||
|
|
||||||
|
|
||||||
enum State {
|
enum State {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -66,12 +63,10 @@ class CompactBlockProcessor {
|
||||||
config.retries
|
config.retries
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private var batchSize: BlockHeight {
|
private var batchSize: BlockHeight {
|
||||||
BlockHeight(self.config.downloadBatchSize)
|
BlockHeight(self.config.downloadBatchSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private var processingError: Error?
|
private var processingError: Error?
|
||||||
|
|
||||||
init(downloader: CompactBlockDownloading, backend: ZcashRustBackendWelding.Type, config: Configuration, service: LightWalletService) {
|
init(downloader: CompactBlockDownloading, backend: ZcashRustBackendWelding.Type, config: Configuration, service: LightWalletService) {
|
||||||
|
@ -91,7 +86,6 @@ class CompactBlockProcessor {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
self.queue.suspend()
|
self.queue.suspend()
|
||||||
self.unsuscribeToSystemNotifications()
|
self.unsuscribeToSystemNotifications()
|
||||||
|
@ -180,14 +174,12 @@ class CompactBlockProcessor {
|
||||||
self.processNewBlocks(latestHeight: latestBlockHeight, latestDownloadedHeight: latestDownloadedBlockHeight)
|
self.processNewBlocks(latestHeight: latestBlockHeight, latestDownloadedHeight: latestDownloadedBlockHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func processNewBlocks(latestHeight: BlockHeight, latestDownloadedHeight: BlockHeight) {
|
func processNewBlocks(latestHeight: BlockHeight, latestDownloadedHeight: BlockHeight) {
|
||||||
|
|
||||||
let dispatchGroup = DispatchGroup()
|
let dispatchGroup = DispatchGroup()
|
||||||
|
|
||||||
|
|
||||||
let validateBlocksTask = DispatchWorkItem {
|
let validateBlocksTask = DispatchWorkItem {
|
||||||
dispatchGroup.enter()
|
dispatchGroup.enter()
|
||||||
self.state = .scanning
|
self.state = .scanning
|
||||||
|
@ -252,7 +244,6 @@ class CompactBlockProcessor {
|
||||||
self.state = .stopped
|
self.state = .stopped
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func fail(_ error: Error) {
|
func fail(_ error: Error) {
|
||||||
// todo specify: failure
|
// todo specify: failure
|
||||||
print(error.localizedDescription)
|
print(error.localizedDescription)
|
||||||
|
@ -264,7 +255,6 @@ class CompactBlockProcessor {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
extension CompactBlockProcessor.Configuration {
|
extension CompactBlockProcessor.Configuration {
|
||||||
static var standard: CompactBlockProcessor.Configuration {
|
static var standard: CompactBlockProcessor.Configuration {
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
protocol BlockRepository {
|
protocol BlockRepository {
|
||||||
func lastScannedBlockHeight() -> BlockHeight
|
func lastScannedBlockHeight() -> BlockHeight
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ protocol CompactBlockStoring {
|
||||||
/**
|
/**
|
||||||
Write the given blocks to this store, which may be anything from an in-memory cache to a DB.
|
Write the given blocks to this store, which may be anything from an in-memory cache to a DB.
|
||||||
*/
|
*/
|
||||||
func write(blocks: [ZcashCompactBlock]) throws -> Void
|
func write(blocks: [ZcashCompactBlock]) throws
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Write the given blocks to this store, which may be anything from an in-memory cache to a DB.
|
Write the given blocks to this store, which may be anything from an in-memory cache to a DB.
|
||||||
|
@ -49,7 +49,7 @@ protocol CompactBlockStoring {
|
||||||
Meaning, if max height is 100 block and rewindTo(50) is called, then the highest block remaining will be 49.
|
Meaning, if max height is 100 block and rewindTo(50) is called, then the highest block remaining will be 49.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
func rewind(to height: BlockHeight) throws -> Void
|
func rewind(to height: BlockHeight) throws
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Remove every block above and including the given height.
|
Remove every block above and including the given height.
|
||||||
|
@ -60,4 +60,3 @@ protocol CompactBlockStoring {
|
||||||
*/
|
*/
|
||||||
func rewind(to height: BlockHeight, completion: ((Error?) -> Void)?)
|
func rewind(to height: BlockHeight, completion: ((Error?) -> Void)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|
||||||
protocol Storage {
|
protocol Storage {
|
||||||
|
|
||||||
func createDatabase(at path: String) throws
|
func createDatabase(at path: String) throws
|
||||||
|
@ -24,5 +23,3 @@ enum StorageError: Error {
|
||||||
case closeFailed
|
case closeFailed
|
||||||
case operationFailed
|
case operationFailed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@ public protocol ResourceProvider {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public struct DefaultResourceProvider: ResourceProvider {
|
public struct DefaultResourceProvider: ResourceProvider {
|
||||||
|
|
||||||
public var dataDbPath: String {
|
public var dataDbPath: String {
|
||||||
|
|
|
@ -24,7 +24,7 @@ class ZcashRustBackend: ZcashRustBackendWelding {
|
||||||
let error = UnsafeMutablePointer<Int8>.allocate(capacity: Int(errorLen))
|
let error = UnsafeMutablePointer<Int8>.allocate(capacity: Int(errorLen))
|
||||||
zcashlc_error_message_utf8(error, errorLen)
|
zcashlc_error_message_utf8(error, errorLen)
|
||||||
zcashlc_clear_last_error()
|
zcashlc_clear_last_error()
|
||||||
return String(validatingUTF8: error)!
|
return String(validatingUTF8: error)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -45,9 +45,10 @@ class ZcashRustBackend: ZcashRustBackendWelding {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let extsks = UnsafeBufferPointer(start: extsksCStr, count: Int(accounts)).map {
|
let extsks = UnsafeBufferPointer(start: extsksCStr, count: Int(accounts)).compactMap({ (cStr) -> String? in
|
||||||
String(cString: $0!)
|
guard let str = cStr else { return nil }
|
||||||
}
|
return String(cString: str)
|
||||||
|
})
|
||||||
zcashlc_vec_string_free(extsksCStr, UInt(accounts))
|
zcashlc_vec_string_free(extsksCStr, UInt(accounts))
|
||||||
return extsks
|
return extsks
|
||||||
}
|
}
|
||||||
|
@ -60,12 +61,9 @@ class ZcashRustBackend: ZcashRustBackendWelding {
|
||||||
static func getAddress(dbData: URL, account: Int32) -> String? {
|
static func getAddress(dbData: URL, account: Int32) -> String? {
|
||||||
let dbData = dbData.osStr()
|
let dbData = dbData.osStr()
|
||||||
|
|
||||||
let addressCStr = zcashlc_get_address(dbData.0, dbData.1, account)
|
guard let addressCStr = zcashlc_get_address(dbData.0, dbData.1, account) else { return nil }
|
||||||
if addressCStr == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
let address = String(validatingUTF8: addressCStr!)
|
let address = String(validatingUTF8: addressCStr)
|
||||||
zcashlc_string_free(addressCStr)
|
zcashlc_string_free(addressCStr)
|
||||||
return address
|
return address
|
||||||
}
|
}
|
||||||
|
@ -83,12 +81,9 @@ class ZcashRustBackend: ZcashRustBackendWelding {
|
||||||
static func getReceivedMemoAsUTF8(dbData: URL, idNote: Int64) -> String? {
|
static func getReceivedMemoAsUTF8(dbData: URL, idNote: Int64) -> String? {
|
||||||
let dbData = dbData.osStr()
|
let dbData = dbData.osStr()
|
||||||
|
|
||||||
let memoCStr = zcashlc_get_received_memo_as_utf8(dbData.0, dbData.1, idNote)
|
guard let memoCStr = zcashlc_get_received_memo_as_utf8(dbData.0, dbData.1, idNote) else { return nil }
|
||||||
if memoCStr == nil {
|
|
||||||
return nil
|
let memo = String(validatingUTF8: memoCStr)
|
||||||
}
|
|
||||||
|
|
||||||
let memo = String(validatingUTF8: memoCStr!)
|
|
||||||
zcashlc_string_free(memoCStr)
|
zcashlc_string_free(memoCStr)
|
||||||
return memo
|
return memo
|
||||||
}
|
}
|
||||||
|
@ -96,12 +91,9 @@ class ZcashRustBackend: ZcashRustBackendWelding {
|
||||||
static func getSentMemoAsUTF8(dbData: URL, idNote: Int64) -> String? {
|
static func getSentMemoAsUTF8(dbData: URL, idNote: Int64) -> String? {
|
||||||
let dbData = dbData.osStr()
|
let dbData = dbData.osStr()
|
||||||
|
|
||||||
let memoCStr = zcashlc_get_sent_memo_as_utf8(dbData.0, dbData.1, idNote)
|
guard let memoCStr = zcashlc_get_sent_memo_as_utf8(dbData.0, dbData.1, idNote) else { return nil }
|
||||||
if memoCStr == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
let memo = String(validatingUTF8: memoCStr!)
|
let memo = String(validatingUTF8: memoCStr)
|
||||||
zcashlc_string_free(memoCStr)
|
zcashlc_string_free(memoCStr)
|
||||||
return memo
|
return memo
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ class LightWalletGRPCService {
|
||||||
compactTxStreamer = CompactTxStreamerServiceClient(channel: self.channel)
|
compactTxStreamer = CompactTxStreamerServiceClient(channel: self.channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
func blockRange(startHeight: BlockHeight, endHeight: BlockHeight? = nil, result: @escaping (CallResult)->()) throws -> CompactTxStreamerGetBlockRangeCall {
|
func blockRange(startHeight: BlockHeight, endHeight: BlockHeight? = nil, result: @escaping (CallResult) -> Void) throws -> CompactTxStreamerGetBlockRangeCall {
|
||||||
try compactTxStreamer.getBlockRange(BlockRange(startHeight: startHeight, endHeight: endHeight)) { result($0) }
|
try compactTxStreamer.getBlockRange(BlockRange(startHeight: startHeight, endHeight: endHeight)) { result($0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,17 +29,16 @@ class LightWalletGRPCService {
|
||||||
try compactTxStreamer.getLatestBlock(ChainSpec())
|
try compactTxStreamer.getLatestBlock(ChainSpec())
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTx(hash:String) throws -> RawTransaction {
|
func getTx(hash: String) throws -> RawTransaction {
|
||||||
var filter = TxFilter()
|
var filter = TxFilter()
|
||||||
filter.hash = Data(hash.utf8)
|
filter.hash = Data(hash.utf8)
|
||||||
return try compactTxStreamer.getTransaction(filter)
|
return try compactTxStreamer.getTransaction(filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAllBlocksSinceSaplingLaunch(_ result: @escaping (CallResult)->()) throws -> CompactTxStreamerGetBlockRangeCall {
|
func getAllBlocksSinceSaplingLaunch(_ result: @escaping (CallResult) -> Void) throws -> CompactTxStreamerGetBlockRangeCall {
|
||||||
try compactTxStreamer.getBlockRange(BlockRange.sinceSaplingActivation(), completion: result)
|
try compactTxStreamer.getBlockRange(BlockRange.sinceSaplingActivation(), completion: result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension LightWalletGRPCService: LightWalletService {
|
extension LightWalletGRPCService: LightWalletService {
|
||||||
|
@ -63,9 +62,9 @@ extension LightWalletGRPCService: LightWalletService {
|
||||||
return blocks
|
return blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
func latestBlockHeight(result: @escaping (Result<BlockHeight, LightWalletServiceError>) -> ()) {
|
func latestBlockHeight(result: @escaping (Result<BlockHeight, LightWalletServiceError>) -> Void) {
|
||||||
do {
|
do {
|
||||||
try compactTxStreamer.getLatestBlock(ChainSpec()) { (blockID, callResult) in
|
try compactTxStreamer.getLatestBlock(ChainSpec()) { (blockID, _) in
|
||||||
guard let rawHeight = blockID?.height, let blockHeight = Int(exactly: rawHeight) else {
|
guard let rawHeight = blockID?.height, let blockHeight = Int(exactly: rawHeight) else {
|
||||||
result(.failure(LightWalletServiceError.generalError))
|
result(.failure(LightWalletServiceError.generalError))
|
||||||
return
|
return
|
||||||
|
@ -82,7 +81,6 @@ extension LightWalletGRPCService: LightWalletService {
|
||||||
// TODO: Make cancellable
|
// TODO: Make cancellable
|
||||||
func blockRange(_ range: CompactBlockRange, result: @escaping (Result<[ZcashCompactBlock], LightWalletServiceError>) -> Void) {
|
func blockRange(_ range: CompactBlockRange, result: @escaping (Result<[ZcashCompactBlock], LightWalletServiceError>) -> Void) {
|
||||||
|
|
||||||
|
|
||||||
queue.async {
|
queue.async {
|
||||||
var blocks = [CompactBlock]()
|
var blocks = [CompactBlock]()
|
||||||
var isSyncing = true
|
var isSyncing = true
|
||||||
|
@ -101,7 +99,6 @@ extension LightWalletGRPCService: LightWalletService {
|
||||||
result(.failure(LightWalletServiceError.failed(statusCode: code)))
|
result(.failure(LightWalletServiceError.failed(statusCode: code)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
result(.failure(LightWalletServiceError.generalError))
|
result(.failure(LightWalletServiceError.generalError))
|
||||||
return
|
return
|
||||||
|
@ -129,7 +126,7 @@ extension LightWalletGRPCService: LightWalletService {
|
||||||
|
|
||||||
func latestBlockHeight() throws -> BlockHeight {
|
func latestBlockHeight() throws -> BlockHeight {
|
||||||
|
|
||||||
guard let height = try? latestBlock().compactBlockHeight() else {
|
guard let height = try? latestBlock().compactBlockHeight() else {
|
||||||
throw LightWalletServiceError.invalidBlock
|
throw LightWalletServiceError.invalidBlock
|
||||||
}
|
}
|
||||||
return height
|
return height
|
||||||
|
|
|
@ -51,8 +51,7 @@ public protocol LightWalletService {
|
||||||
|
|
||||||
- Parameter result: a result containing the height or an Error
|
- Parameter result: a result containing the height or an Error
|
||||||
*/
|
*/
|
||||||
func latestBlockHeight(result: @escaping (Result<BlockHeight,LightWalletServiceError>) -> ())
|
func latestBlockHeight(result: @escaping (Result<BlockHeight,LightWalletServiceError>) -> Void)
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Return the latest block height known to the service.
|
Return the latest block height known to the service.
|
||||||
|
@ -81,5 +80,3 @@ public protocol LightWalletService {
|
||||||
func blockRange(_ range: CompactBlockRange) throws -> [ZcashCompactBlock]
|
func blockRange(_ range: CompactBlockRange) throws -> [ZcashCompactBlock]
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,6 @@ import Foundation
|
||||||
public typealias BlockHeight = Int
|
public typealias BlockHeight = Int
|
||||||
public typealias CompactBlockRange = Range<BlockHeight>
|
public typealias CompactBlockRange = Range<BlockHeight>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
enum ZcashCompactBlockError: Error {
|
enum ZcashCompactBlockError: Error {
|
||||||
case unreadableBlock(compactBlock: CompactBlock)
|
case unreadableBlock(compactBlock: CompactBlock)
|
||||||
}
|
}
|
||||||
|
@ -52,5 +50,3 @@ extension ZcashCompactBlock: Hashable {
|
||||||
hasher.combine(data)
|
hasher.combine(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,6 @@ extension CompactBlockRange {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
extension BlockID {
|
extension BlockID {
|
||||||
|
|
||||||
static let saplingActivationHeight: UInt64 = 280_000
|
static let saplingActivationHeight: UInt64 = 280_000
|
||||||
|
@ -41,7 +40,7 @@ extension BlockRange {
|
||||||
|
|
||||||
init(startHeight: Int, endHeight: Int? = nil) {
|
init(startHeight: Int, endHeight: Int? = nil) {
|
||||||
self = BlockRange()
|
self = BlockRange()
|
||||||
self.start = BlockID(height: UInt64(startHeight))
|
self.start = BlockID(height: UInt64(startHeight))
|
||||||
if let endHeight = endHeight {
|
if let endHeight = endHeight {
|
||||||
self.end = BlockID(height: UInt64(endHeight))
|
self.end = BlockID(height: UInt64(endHeight))
|
||||||
}
|
}
|
||||||
|
@ -59,7 +58,6 @@ extension BlockRange {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
extension Array where Element == CompactBlock {
|
extension Array where Element == CompactBlock {
|
||||||
func asZcashCompactBlocks() throws -> [ZcashCompactBlock] {
|
func asZcashCompactBlocks() throws -> [ZcashCompactBlock] {
|
||||||
var result = [ZcashCompactBlock]()
|
var result = [ZcashCompactBlock]()
|
||||||
|
|
|
@ -12,7 +12,6 @@ import Foundation
|
||||||
capabilities and the supporting data required to exercise those abilities.
|
capabilities and the supporting data required to exercise those abilities.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
public enum WalletError: Error {
|
public enum WalletError: Error {
|
||||||
case cacheDbInitFailed
|
case cacheDbInitFailed
|
||||||
case dataDbInitFailed
|
case dataDbInitFailed
|
||||||
|
@ -29,7 +28,7 @@ public class Wallet {
|
||||||
private var walletBirthday: WalletBirthday
|
private var walletBirthday: WalletBirthday
|
||||||
private var storage: Storage?
|
private var storage: Storage?
|
||||||
|
|
||||||
init(rustWelding: ZcashRustBackendWelding.Type, cacheDbURL:URL, dataDbURL: URL, paramDestination: URL, seedProvider: SeedProvider, walletBirthday: WalletBirthday, accountIDs: [Int] = [0]) {
|
init(rustWelding: ZcashRustBackendWelding.Type, cacheDbURL: URL, dataDbURL: URL, paramDestination: URL, seedProvider: SeedProvider, walletBirthday: WalletBirthday, accountIDs: [Int] = [0]) {
|
||||||
|
|
||||||
self.rustBackend = rustWelding.self
|
self.rustBackend = rustWelding.self
|
||||||
self.dataDbURL = dataDbURL
|
self.dataDbURL = dataDbURL
|
||||||
|
@ -41,8 +40,7 @@ public class Wallet {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func initalize(firstRunStartHeight: BlockHeight = SAPLING_ACTIVATION_HEIGHT) throws {
|
||||||
public func initalize(firstRunStartHeight: BlockHeight = SAPLING_ACTIVATION_HEIGHT) throws {
|
|
||||||
|
|
||||||
guard let storage = StorageBuilder.cacheDb(at: cacheDbURL) else {
|
guard let storage = StorageBuilder.cacheDb(at: cacheDbURL) else {
|
||||||
throw WalletError.cacheDbInitFailed
|
throw WalletError.cacheDbInitFailed
|
||||||
|
@ -54,7 +52,6 @@ public class Wallet {
|
||||||
|
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func latestBlockHeight() -> Int? {
|
public func latestBlockHeight() -> Int? {
|
||||||
|
@ -62,27 +59,25 @@ public class Wallet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Represents the wallet's birthday which can be thought of as a checkpoint at the earliest moment in history where
|
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
|
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).
|
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
|
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
|
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
|
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.
|
to avoid downloading all the compact blocks required in order to generate it.
|
||||||
|
|
||||||
New wallets can ignore any blocks created before their birthday.
|
New wallets can ignore any blocks created before their birthday.
|
||||||
|
|
||||||
- Parameter height the height at the time the wallet was born
|
- Parameter height the height at the time the wallet was born
|
||||||
- Parameter hash the block hash corresponding to the given height
|
- Parameter hash the block hash corresponding to the given height
|
||||||
- Parameter time the time the wallet was born, in seconds
|
- 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
|
- 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
|
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.
|
advance it can save the user a lot of time.
|
||||||
*/
|
*/
|
||||||
public struct WalletBirthday {
|
public struct WalletBirthday {
|
||||||
var height: BlockHeight = -1
|
var height: BlockHeight = -1
|
||||||
var hash: String = ""
|
var hash: String = ""
|
||||||
|
|
|
@ -15,7 +15,7 @@ class BlockDownloaderTests: XCTestCase {
|
||||||
var storage: CompactBlockStoring!
|
var storage: CompactBlockStoring!
|
||||||
override func setUp() {
|
override func setUp() {
|
||||||
service = LightWalletGRPCService(channel: ChannelProvider().channel())
|
service = LightWalletGRPCService(channel: ChannelProvider().channel())
|
||||||
storage = try! TestDbBuilder.inMemoryCompactBlockStorage()
|
storage = try! TestDbBuilder.inMemoryCompactBlockStorage()
|
||||||
downloader = CompactBlockDownloader(service: service, storage: storage)
|
downloader = CompactBlockDownloader(service: service, storage: storage)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,6 @@ class BlockDownloaderTests: XCTestCase {
|
||||||
downloader = nil
|
downloader = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func testSmallDownloadAsync() {
|
func testSmallDownloadAsync() {
|
||||||
|
|
||||||
let expect = XCTestExpectation(description: self.description)
|
let expect = XCTestExpectation(description: self.description)
|
||||||
|
@ -55,17 +54,14 @@ class BlockDownloaderTests: XCTestCase {
|
||||||
wait(for: [expect], timeout: 2)
|
wait(for: [expect], timeout: 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func testSmallDownload() {
|
func testSmallDownload() {
|
||||||
|
|
||||||
|
|
||||||
let lowerRange: BlockHeight = SAPLING_ACTIVATION_HEIGHT
|
let lowerRange: BlockHeight = SAPLING_ACTIVATION_HEIGHT
|
||||||
let upperRange: BlockHeight = SAPLING_ACTIVATION_HEIGHT + 99
|
let upperRange: BlockHeight = SAPLING_ACTIVATION_HEIGHT + 99
|
||||||
|
|
||||||
let range = CompactBlockRange(uncheckedBounds: (lowerRange,upperRange))
|
let range = CompactBlockRange(uncheckedBounds: (lowerRange,upperRange))
|
||||||
var latest: BlockHeight = 0
|
var latest: BlockHeight = 0
|
||||||
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
latest = try downloader.latestBlockHeight()
|
latest = try downloader.latestBlockHeight()
|
||||||
} catch {
|
} catch {
|
||||||
|
@ -105,7 +101,6 @@ class BlockDownloaderTests: XCTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Helper functions
|
/// Helper functions
|
||||||
|
|
||||||
extension BlockDownloaderTests {
|
extension BlockDownloaderTests {
|
||||||
|
|
|
@ -28,7 +28,7 @@ class BlockScanOperationTests: XCTestCase {
|
||||||
operationQueue.cancelAllOperations()
|
operationQueue.cancelAllOperations()
|
||||||
|
|
||||||
// try! FileManager.default.removeItem(at: cacheDbURL)
|
// try! FileManager.default.removeItem(at: cacheDbURL)
|
||||||
try! FileManager.default.removeItem(at: dataDbURL)
|
try? FileManager.default.removeItem(at: dataDbURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSingleDownloadAndScanOperation() {
|
func testSingleDownloadAndScanOperation() {
|
||||||
|
|
|
@ -20,8 +20,6 @@ class CompactBlockProcessorTests: XCTestCase {
|
||||||
XCTAssertTrue(MockDbInit.emptyFile(at: processorConfig.cacheDbPath))
|
XCTAssertTrue(MockDbInit.emptyFile(at: processorConfig.cacheDbPath))
|
||||||
XCTAssertTrue(MockDbInit.emptyFile(at: processorConfig.dataDbPath))
|
XCTAssertTrue(MockDbInit.emptyFile(at: processorConfig.dataDbPath))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let service = LightWalletGRPCService(channel: ChannelProvider().channel())
|
let service = LightWalletGRPCService(channel: ChannelProvider().channel())
|
||||||
let storage = ZcashConsoleFakeStorage()
|
let storage = ZcashConsoleFakeStorage()
|
||||||
let downloader = CompactBlockDownloader(service: service, storage: storage)
|
let downloader = CompactBlockDownloader(service: service, storage: storage)
|
||||||
|
@ -43,13 +41,10 @@ class CompactBlockProcessorTests: XCTestCase {
|
||||||
|
|
||||||
expect.unsuscribeFromNotifications()
|
expect.unsuscribeFromNotifications()
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func testStartNotifiesSuscriptors() {
|
func testStartNotifiesSuscriptors() {
|
||||||
|
|
||||||
|
|
||||||
XCTAssertNotNil(processor)
|
XCTAssertNotNil(processor)
|
||||||
expect.suscribe(to: Notification.Name.blockProcessorStarted, object: processor)
|
expect.suscribe(to: Notification.Name.blockProcessorStarted, object: processor)
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@ import XCTest
|
||||||
@testable import ZcashLightClientKit
|
@testable import ZcashLightClientKit
|
||||||
class CompactBlockStorageTests: XCTestCase {
|
class CompactBlockStorageTests: XCTestCase {
|
||||||
|
|
||||||
|
|
||||||
var storage: Storage = try! TestDbBuilder.inMemory()
|
var storage: Storage = try! TestDbBuilder.inMemory()
|
||||||
|
|
||||||
func testEmptyStorage() {
|
func testEmptyStorage() {
|
||||||
|
@ -61,7 +60,6 @@ class CompactBlockStorageTests: XCTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func testRewindTo() {
|
func testRewindTo() {
|
||||||
|
|
||||||
let startHeight = SAPLING_ACTIVATION_HEIGHT
|
let startHeight = SAPLING_ACTIVATION_HEIGHT
|
||||||
|
|
|
@ -12,7 +12,6 @@ import SQLite
|
||||||
class DownloadOperationTests: XCTestCase {
|
class DownloadOperationTests: XCTestCase {
|
||||||
|
|
||||||
var operationQueue = OperationQueue()
|
var operationQueue = OperationQueue()
|
||||||
|
|
||||||
|
|
||||||
override func tearDown() {
|
override func tearDown() {
|
||||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||||
|
|
|
@ -59,7 +59,6 @@ class LightWalletServiceTests: XCTestCase {
|
||||||
wait(for: [expect], timeout: 5)
|
wait(for: [expect], timeout: 5)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func testSyncBlockRange() {
|
func testSyncBlockRange() {
|
||||||
let lowerRange: BlockHeight = SAPLING_ACTIVATION_HEIGHT
|
let lowerRange: BlockHeight = SAPLING_ACTIVATION_HEIGHT
|
||||||
let upperRange: BlockHeight = SAPLING_ACTIVATION_HEIGHT + 99
|
let upperRange: BlockHeight = SAPLING_ACTIVATION_HEIGHT + 99
|
||||||
|
|
|
@ -62,7 +62,6 @@ class ZcashLightClientKitTests: XCTestCase {
|
||||||
func testBlockRangeServiceTilLastest() {
|
func testBlockRangeServiceTilLastest() {
|
||||||
let expectedCount: BlockHeight = 99
|
let expectedCount: BlockHeight = 99
|
||||||
var count: BlockHeight = 0
|
var count: BlockHeight = 0
|
||||||
|
|
||||||
|
|
||||||
let startHeight = latestBlockHeight - expectedCount
|
let startHeight = latestBlockHeight - expectedCount
|
||||||
let endHeight = latestBlockHeight!
|
let endHeight = latestBlockHeight!
|
||||||
|
@ -75,7 +74,6 @@ class ZcashLightClientKitTests: XCTestCase {
|
||||||
XCTFail("failed to create getBlockRange( \(startHeight) ..<= \(endHeight)")
|
XCTFail("failed to create getBlockRange( \(startHeight) ..<= \(endHeight)")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var blocks = [CompactBlock]()
|
var blocks = [CompactBlock]()
|
||||||
while true {
|
while true {
|
||||||
|
|
|
@ -60,6 +60,5 @@ class ZcashRustBackendTests: XCTestCase {
|
||||||
|
|
||||||
XCTAssertTrue(ZcashRustBackend.scanBlocks(dbCache: cacheDb, dbData: dbData))
|
XCTAssertTrue(ZcashRustBackend.scanBlocks(dbCache: cacheDb, dbData: dbData))
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,6 @@ class ZcashConsoleFakeStorage: CompactBlockStoring {
|
||||||
fakeRewind(to: height)
|
fakeRewind(to: height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var latestBlockHeight: BlockHeight = 0
|
var latestBlockHeight: BlockHeight = 0
|
||||||
var delay = DispatchTimeInterval.milliseconds(300)
|
var delay = DispatchTimeInterval.milliseconds(300)
|
||||||
|
|
||||||
|
@ -62,6 +61,4 @@ class ZcashConsoleFakeStorage: CompactBlockStoring {
|
||||||
self.latestBlockHeight = min(self.latestBlockHeight, height)
|
self.latestBlockHeight = min(self.latestBlockHeight, height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ class AwfulLightWalletService: LightWalletService {
|
||||||
throw LightWalletServiceError.invalidBlock
|
throw LightWalletServiceError.invalidBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
func latestBlockHeight(result: @escaping (Result<BlockHeight, LightWalletServiceError>) -> ()) {
|
func latestBlockHeight(result: @escaping (Result<BlockHeight, LightWalletServiceError>) -> Void) {
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||||
result(.failure(LightWalletServiceError.generalError))
|
result(.failure(LightWalletServiceError.generalError))
|
||||||
}
|
}
|
||||||
|
@ -31,5 +31,4 @@ class AwfulLightWalletService: LightWalletService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,14 +33,13 @@ struct TestDbBuilder {
|
||||||
return compactBlockDao
|
return compactBlockDao
|
||||||
}
|
}
|
||||||
|
|
||||||
static func diskCompactBlockStorage(at url:URL) throws -> CompactBlockStorage {
|
static func diskCompactBlockStorage(at url: URL) throws -> CompactBlockStorage {
|
||||||
let connection = try Connection(url.absoluteString)
|
let connection = try Connection(url.absoluteString)
|
||||||
let compactBlockDao = CompactBlockStorage(connection: connection)
|
let compactBlockDao = CompactBlockStorage(connection: connection)
|
||||||
try compactBlockDao.createTable()
|
try compactBlockDao.createTable()
|
||||||
return compactBlockDao
|
return compactBlockDao
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static func seed(db: Storage, with blockRange: CompactBlockRange) throws {
|
static func seed(db: Storage, with blockRange: CompactBlockRange) throws {
|
||||||
|
|
||||||
guard let blocks = StubBlockCreator.createBlockRange(blockRange) else {
|
guard let blocks = StubBlockCreator.createBlockRange(blockRange) else {
|
||||||
|
|
|
@ -16,14 +16,11 @@ class ChannelProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct MockDbInit {
|
struct MockDbInit {
|
||||||
@discardableResult static func emptyFile(at path: String) -> Bool {
|
@discardableResult static func emptyFile(at path: String) -> Bool {
|
||||||
|
|
||||||
|
|
||||||
FileManager.default.createFile(atPath: path, contents: Data("".utf8), attributes: nil)
|
FileManager.default.createFile(atPath: path, contents: Data("".utf8), attributes: nil)
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static func destroy(at path: String) throws {
|
static func destroy(at path: String) throws {
|
||||||
|
@ -46,7 +43,6 @@ extension XCTestExpectation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func __documentsDirectory() throws -> URL {
|
func __documentsDirectory() throws -> URL {
|
||||||
try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
|
try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
# options
|
||||||
|
--self remove # redundantSelf
|
||||||
|
--importgrouping testable-bottom # sortedImports
|
||||||
|
--commas always # trailingCommas
|
||||||
|
--trimwhitespace always # trailingSpace
|
||||||
|
|
||||||
|
# rules
|
||||||
|
--rules redundantParens,redundantSelf,sortedImports,trailingCommas,trailingSpace
|
Loading…
Reference in New Issue