[#795] Include sapling-spend file into bundle for tests

Closes #795

- Added structure `SaplingParamsSourceURL` which holds source URLs for
  sapling-spend file and sapling-output file. URLs are ultimately passed
  to `SaplingParameterDownloader`.
- This is done so it's possible to pass different URLs when running
  tests.
- sapling-spend file and sapling-output file are in tests bundle. And
  when running tests these files are loaded from bundle and not from
  network.
This commit is contained in:
Michal Fousek 2023-02-16 17:27:49 +01:00
parent bb0e27dc93
commit d09b00cabe
22 changed files with 131 additions and 76 deletions

2
.gitignore vendored
View File

@ -10,8 +10,6 @@
## These are backup files generated by rustfmt
**/*.rs.bk
# params files
*.params
# Xcode
## Build generated

View File

@ -4,6 +4,12 @@
`synchronizerStopped` notification is now sent after the sync process stops. It's
not sent right when `stop()` method is called.
- [#795] Include sapling-spend file into bundle for tests
This is only an internal change and doesn't change the behavior of the SDK. `Initializer`'s
constructor has a new parameter `saplingParamsSourceURL`. Use `SaplingParamsSourceURL.default`
value for this parameter.
- [#764] Refactor communication between components inside th SDK
This is mostly an internal change. A consequence of this change is that all the notifications

View File

@ -44,6 +44,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
network: kZcashNetwork,
spendParamsURL: try! spendParamsURLHelper(),
outputParamsURL: try! outputParamsURLHelper(),
saplingParamsSourceURL: SaplingParamsSourceURL.default,
viewingKeys: [ufvk],
walletBirthday: DemoAppConfig.birthdayHeight,
loggerProxy: loggerProxy

View File

@ -84,7 +84,9 @@ class SaplingParametersViewController: UIViewController {
do {
let urls = try await SaplingParameterDownloader.downloadParamsIfnotPresent(
spendURL: spendParameter,
outputURL: outputParameter
spendSourceURL: SaplingParamsSourceURL.default.spendParamFileURL,
outputURL: outputParameter,
outputSourceURL: SaplingParamsSourceURL.default.outputParamFileURL
)
spendPath.text = urls.spend.path
outputPath.text = urls.output.path

View File

@ -50,7 +50,9 @@ let package = Package(
.copy("Resources/sandblasted_mainnet_block.json"),
.copy("Resources/txBase64String.txt"),
.copy("Resources/txFromAndroidSDK.txt"),
.copy("Resources/integerOverflowJSON.json")
.copy("Resources/integerOverflowJSON.json"),
.copy("Resources/sapling-spend.params"),
.copy("Resources/sapling-output.params")
]
),
.testTarget(

View File

@ -142,6 +142,7 @@ actor CompactBlockProcessor {
/// - parameter spendParamsURL: absolute file path of the sapling-spend.params file
/// - parameter outputParamsURL: absolute file path of the sapling-output.params file
struct Configuration {
let saplingParamsSourceURL: SaplingParamsSourceURL
public var fsBlockCacheRoot: URL
public var dataDb: URL
public var spendParamsURL: URL
@ -167,6 +168,7 @@ actor CompactBlockProcessor {
dataDb: URL,
spendParamsURL: URL,
outputParamsURL: URL,
saplingParamsSourceURL: SaplingParamsSourceURL,
downloadBatchSize: Int,
retries: Int,
maxBackoffInterval: TimeInterval,
@ -179,6 +181,7 @@ actor CompactBlockProcessor {
self.dataDb = dataDb
self.spendParamsURL = spendParamsURL
self.outputParamsURL = outputParamsURL
self.saplingParamsSourceURL = saplingParamsSourceURL
self.network = network
self.downloadBatchSize = downloadBatchSize
self.retries = retries
@ -195,6 +198,7 @@ actor CompactBlockProcessor {
dataDb: URL,
spendParamsURL: URL,
outputParamsURL: URL,
saplingParamsSourceURL: SaplingParamsSourceURL,
walletBirthday: BlockHeight,
network: ZcashNetwork
) {
@ -202,6 +206,7 @@ actor CompactBlockProcessor {
self.dataDb = dataDb
self.spendParamsURL = spendParamsURL
self.outputParamsURL = outputParamsURL
self.saplingParamsSourceURL = saplingParamsSourceURL
self.walletBirthday = walletBirthday
self.saplingActivation = network.constants.saplingActivationHeight
self.network = network
@ -346,6 +351,7 @@ actor CompactBlockProcessor {
dataDb: initializer.dataDbURL,
spendParamsURL: initializer.spendParamsURL,
outputParamsURL: initializer.outputParamsURL,
saplingParamsSourceURL: initializer.saplingParamsSourceURL,
walletBirthday: Checkpoint.birthday(
with: initializer.walletBirthday,
network: initializer.network
@ -417,7 +423,8 @@ actor CompactBlockProcessor {
dataDb: config.dataDb,
networkType: config.network.networkType,
outputParamsURL: config.outputParamsURL,
spendParamsURL: config.spendParamsURL
spendParamsURL: config.spendParamsURL,
saplingParamsSourceURL: config.saplingParamsSourceURL
)
self.saplingParametersHandler = SaplingParametersHandlerImpl(config: saplingParametersHandlerConfig, rustBackend: backend)
@ -1006,23 +1013,6 @@ actor CompactBlockProcessor {
// TODO: [#713] encapsulate service errors better, https://github.com/zcash/ZcashLightClientKit/issues/713
}
extension CompactBlockProcessor.Configuration {
/**
Standard configuration for most compact block processors
*/
static func standard(for network: ZcashNetwork, walletBirthday: BlockHeight) -> CompactBlockProcessor.Configuration {
let pathProvider = DefaultResourceProvider(network: network)
return CompactBlockProcessor.Configuration(
fsBlockCacheRoot: pathProvider.fsCacheURL,
dataDb: pathProvider.dataDbURL,
spendParamsURL: pathProvider.spendParamsURL,
outputParamsURL: pathProvider.outputParamsURL,
walletBirthday: walletBirthday,
network: network
)
}
}
extension LightWalletServiceError {
func mapToProcessorError() -> CompactBlockProcessorError {
switch self {

View File

@ -12,6 +12,7 @@ struct SaplingParametersHandlerConfig {
let networkType: NetworkType
let outputParamsURL: URL
let spendParamsURL: URL
let saplingParamsSourceURL: SaplingParamsSourceURL
}
protocol SaplingParametersHandler {
@ -50,6 +51,11 @@ extension SaplingParametersHandlerImpl: SaplingParametersHandler {
return
}
try await SaplingParameterDownloader.downloadParamsIfnotPresent(spendURL: config.spendParamsURL, outputURL: config.outputParamsURL)
try await SaplingParameterDownloader.downloadParamsIfnotPresent(
spendURL: config.spendParamsURL,
spendSourceURL: config.saplingParamsSourceURL.spendParamFileURL,
outputURL: config.outputParamsURL,
outputSourceURL: config.saplingParamsSourceURL.outputParamFileURL
)
}
}

View File

@ -131,16 +131,20 @@ public enum ZcashSDK {
/// Default name for pending transactions db
public static var defaultPendingDbName = "pending.db"
/// File name for the sapling spend params
public static var spendParamFilename = "sapling-spend.params"
/// File name for the sapling output params
public static var outputParamFilename = "sapling-output.params"
/// The Url that is used by default in zcashd.
/// We'll want to make this externally configurable, rather than baking it into the SDK but
/// this will do for now, since we're using a cloudfront URL that already redirects.
public static var cloudParameterURL = "https://z.cash/downloads/"
public static let cloudParameterURL = "https://z.cash/downloads/"
/// File name for the sapling spend params
public static let spendParamFilename = "sapling-spend.params"
// swiftlint:disable:next force_unwrapping
public static let spendParamFileURL = URL(string: cloudParameterURL)!.appendingPathComponent(spendParamFilename)
/// File name for the sapling output params
public static let outputParamFilename = "sapling-output.params"
// swiftlint:disable:next force_unwrapping
public static let outputParamFileURL = URL(string: cloudParameterURL)!.appendingPathComponent(outputParamFilename)
}
public protocol NetworkConstants {

View File

@ -57,6 +57,17 @@ extension Notification.Name {
static let connectionStatusChanged = Notification.Name("LightWalletServiceConnectivityStatusChanged")
}
/// This contains URLs from which can the SDK fetch files that contain sapling parameters.
/// Use `SaplingParamsSourceURL.default` when initilizing the SDK.
public struct SaplingParamsSourceURL {
public let spendParamFileURL: URL
public let outputParamFileURL: URL
public static var `default`: SaplingParamsSourceURL {
return SaplingParamsSourceURL(spendParamFileURL: ZcashSDK.spendParamFileURL, outputParamFileURL: ZcashSDK.outputParamFileURL)
}
}
/**
Wrapper for all the Rust backend functionality that does not involve processing blocks. This
class initializes the Rust backend and the supporting data required to exercise those abilities.
@ -70,27 +81,28 @@ public class Initializer {
case seedRequired
}
private(set) var rustBackend: ZcashRustBackendWelding.Type
private(set) var alias: String
private(set) var endpoint: LightWalletEndpoint
private var lowerBoundHeight: BlockHeight
private(set) var fsBlockDbRoot: URL
private(set) var dataDbURL: URL
private(set) var pendingDbURL: URL
private(set) var spendParamsURL: URL
private(set) var outputParamsURL: URL
private(set) var lightWalletService: LightWalletService
private(set) var transactionRepository: TransactionRepository
private(set) var accountRepository: AccountRepository
private(set) var storage: CompactBlockRepository
private(set) var blockDownloaderService: BlockDownloaderService
private(set) var network: ZcashNetwork
private(set) public var viewingKeys: [UnifiedFullViewingKey]
let rustBackend: ZcashRustBackendWelding.Type
let alias: String
let endpoint: LightWalletEndpoint
let fsBlockDbRoot: URL
let dataDbURL: URL
let pendingDbURL: URL
let spendParamsURL: URL
let outputParamsURL: URL
let saplingParamsSourceURL: SaplingParamsSourceURL
let lightWalletService: LightWalletService
let transactionRepository: TransactionRepository
let accountRepository: AccountRepository
let storage: CompactBlockRepository
let blockDownloaderService: BlockDownloaderService
let network: ZcashNetwork
public let viewingKeys: [UnifiedFullViewingKey]
/// The effective birthday of the wallet based on the height provided when initializing
/// and the checkpoints available on this SDK
private(set) public var walletBirthday: BlockHeight
private var lowerBoundHeight: BlockHeight
/// The purpose of this to migrate from cacheDb to fsBlockDb
private var cacheDbURL: URL?
@ -110,6 +122,7 @@ public class Initializer {
network: ZcashNetwork,
spendParamsURL: URL,
outputParamsURL: URL,
saplingParamsSourceURL: SaplingParamsSourceURL,
viewingKeys: [UnifiedFullViewingKey],
walletBirthday: BlockHeight,
alias: String = "",
@ -142,6 +155,7 @@ public class Initializer {
),
spendParamsURL: spendParamsURL,
outputParamsURL: outputParamsURL,
saplingParamsSourceURL: saplingParamsSourceURL,
viewingKeys: viewingKeys,
walletBirthday: walletBirthday,
alias: alias,
@ -171,6 +185,7 @@ public class Initializer {
network: ZcashNetwork,
spendParamsURL: URL,
outputParamsURL: URL,
saplingParamsSourceURL: SaplingParamsSourceURL,
viewingKeys: [UnifiedFullViewingKey],
walletBirthday: BlockHeight,
alias: String = "",
@ -203,6 +218,7 @@ public class Initializer {
),
spendParamsURL: spendParamsURL,
outputParamsURL: outputParamsURL,
saplingParamsSourceURL: saplingParamsSourceURL,
viewingKeys: viewingKeys,
walletBirthday: walletBirthday,
alias: alias,
@ -228,6 +244,7 @@ public class Initializer {
storage: CompactBlockRepository,
spendParamsURL: URL,
outputParamsURL: URL,
saplingParamsSourceURL: SaplingParamsSourceURL,
viewingKeys: [UnifiedFullViewingKey],
walletBirthday: BlockHeight,
alias: String = "",
@ -243,6 +260,7 @@ public class Initializer {
self.endpoint = endpoint
self.spendParamsURL = spendParamsURL
self.outputParamsURL = outputParamsURL
self.saplingParamsSourceURL = saplingParamsSourceURL
self.alias = alias
self.lightWalletService = service
self.transactionRepository = repository

View File

@ -397,7 +397,9 @@ public class SDKSynchronizer: Synchronizer {
do {
try await SaplingParameterDownloader.downloadParamsIfnotPresent(
spendURL: initializer.spendParamsURL,
outputURL: initializer.outputParamsURL
spendSourceURL: initializer.saplingParamsSourceURL.spendParamFileURL,
outputURL: initializer.outputParamsURL,
outputSourceURL: initializer.saplingParamsSourceURL.outputParamFileURL
)
} catch {
throw SynchronizerError.parameterMissing(underlyingError: error)

View File

@ -21,30 +21,17 @@ extension Digest {
/// Helper class to handle the download of Sapling parameters
public enum SaplingParameterDownloader {
public enum Errors: Error {
case invalidURL(url: String)
case failed(error: Error)
case spendParamsInvalidSHA1
case outputParamsInvalidSHA1
}
public static var spendParamsURLString: String {
return ZcashSDK.cloudParameterURL + ZcashSDK.spendParamFilename
}
public static var outputParamsURLString: String {
return ZcashSDK.cloudParameterURL + ZcashSDK.outputParamFilename
}
/// Download a Spend parameter from default host and stores it at given URL
/// - Parameters:
/// - at: The destination URL for the download
@discardableResult
public static func downloadSpendParameter(_ at: URL) async throws -> URL {
guard let url = URL(string: spendParamsURLString) else {
throw Errors.invalidURL(url: spendParamsURLString)
}
let resultURL = try await downloadFileWithRequestWithContinuation(URLRequest(url: url), at: at)
public static func downloadSpendParameter(_ at: URL, sourceURL: URL) async throws -> URL {
let resultURL = try await downloadFileWithRequestWithContinuation(URLRequest(url: sourceURL), at: at)
try isSpendParamsSHA1Valid(url: resultURL)
return resultURL
}
@ -53,12 +40,8 @@ public enum SaplingParameterDownloader {
/// - Parameters:
/// - at: The destination URL for the download
@discardableResult
public static func downloadOutputParameter(_ at: URL) async throws -> URL {
guard let url = URL(string: outputParamsURLString) else {
throw Errors.invalidURL(url: outputParamsURLString)
}
let resultURL = try await downloadFileWithRequestWithContinuation(URLRequest(url: url), at: at)
public static func downloadOutputParameter(_ at: URL, sourceURL: URL) async throws -> URL {
let resultURL = try await downloadFileWithRequestWithContinuation(URLRequest(url: sourceURL), at: at)
try isOutputParamsSHA1Valid(url: resultURL)
return resultURL
}
@ -70,11 +53,13 @@ public enum SaplingParameterDownloader {
@discardableResult
public static func downloadParamsIfnotPresent(
spendURL: URL,
outputURL: URL
spendSourceURL: URL,
outputURL: URL,
outputSourceURL: URL
) async throws -> (spend: URL, output: URL) {
do {
async let spendResultURL = ensureSpendParameter(at: spendURL)
async let outputResultURL = ensureOutputParameter(at: outputURL)
async let spendResultURL = ensureSpendParameter(at: spendURL, sourceURL: spendSourceURL)
async let outputResultURL = ensureOutputParameter(at: outputURL, sourceURL: outputSourceURL)
let results = try await [spendResultURL, outputResultURL]
return (spend: results[0], output: results[1])
@ -83,21 +68,21 @@ public enum SaplingParameterDownloader {
}
}
static func ensureSpendParameter(at url: URL) async throws -> URL {
static func ensureSpendParameter(at url: URL, sourceURL: URL) async throws -> URL {
if isFilePresent(url: url) {
try isSpendParamsSHA1Valid(url: url)
return url
} else {
return try await downloadSpendParameter(url)
return try await downloadSpendParameter(url, sourceURL: sourceURL)
}
}
static func ensureOutputParameter(at url: URL) async throws -> URL {
static func ensureOutputParameter(at url: URL, sourceURL: URL) async throws -> URL {
if isFilePresent(url: url) {
try isOutputParamsSHA1Valid(url: url)
return url
} else {
return try await downloadOutputParameter(url)
return try await downloadOutputParameter(url, sourceURL: sourceURL)
}
}
@ -147,6 +132,7 @@ private extension SaplingParameterDownloader {
at destination: URL,
result: @escaping (Result<URL, Error>) -> Void
) {
LoggerProxy.debug("Downloading sapling file from \(request.url)")
let task = URLSession.shared.downloadTask(with: request) { url, _, error in
if let error = error {
result(.failure(Errors.failed(error: error)))

View File

@ -66,6 +66,7 @@ class TransactionEnhancementTests: XCTestCase {
dataDb: pathProvider.dataDbURL,
spendParamsURL: pathProvider.spendParamsURL,
outputParamsURL: pathProvider.outputParamsURL,
saplingParamsSourceURL: SaplingParamsSourceURL.tests,
walletBirthday: birthday.height,
network: network
)

View File

@ -95,6 +95,7 @@ class BlockScanTests: XCTestCase {
dataDb: dataDbURL,
spendParamsURL: spendParamsURL,
outputParamsURL: outputParamsURL,
saplingParamsSourceURL: SaplingParamsSourceURL.tests,
walletBirthday: walletBirthDay.height,
network: network
)
@ -183,6 +184,7 @@ class BlockScanTests: XCTestCase {
dataDb: dataDbURL,
spendParamsURL: spendParamsURL,
outputParamsURL: outputParamsURL,
saplingParamsSourceURL: SaplingParamsSourceURL.tests,
walletBirthday: network.constants.saplingActivationHeight,
network: network
)

View File

@ -19,6 +19,7 @@ class CompactBlockProcessorTests: XCTestCase {
dataDb: pathProvider.dataDbURL,
spendParamsURL: pathProvider.spendParamsURL,
outputParamsURL: pathProvider.outputParamsURL,
saplingParamsSourceURL: SaplingParamsSourceURL.tests,
walletBirthday: ZcashNetworkBuilder.network(for: .testnet).constants.saplingActivationHeight,
network: ZcashNetworkBuilder.network(for: .testnet)
)

View File

@ -19,6 +19,7 @@ class CompactBlockReorgTests: XCTestCase {
dataDb: pathProvider.dataDbURL,
spendParamsURL: pathProvider.spendParamsURL,
outputParamsURL: pathProvider.outputParamsURL,
saplingParamsSourceURL: SaplingParamsSourceURL.tests,
walletBirthday: ZcashNetworkBuilder.network(for: .testnet).constants.saplingActivationHeight,
network: ZcashNetworkBuilder.network(for: .testnet)
)

View File

@ -55,6 +55,7 @@ class BlockBatchValidationTests: XCTestCase {
dataDb: try! __dataDbURL(),
spendParamsURL: try! __spendParamsURL(),
outputParamsURL: try! __outputParamsURL(),
saplingParamsSourceURL: SaplingParamsSourceURL.tests,
downloadBatchSize: 100,
retries: 5,
maxBackoffInterval: 10,
@ -124,6 +125,7 @@ class BlockBatchValidationTests: XCTestCase {
dataDb: try! __dataDbURL(),
spendParamsURL: try! __spendParamsURL(),
outputParamsURL: try! __outputParamsURL(),
saplingParamsSourceURL: SaplingParamsSourceURL.tests,
downloadBatchSize: 100,
retries: 5,
maxBackoffInterval: 10,
@ -193,6 +195,7 @@ class BlockBatchValidationTests: XCTestCase {
dataDb: try! __dataDbURL(),
spendParamsURL: try! __spendParamsURL(),
outputParamsURL: try! __outputParamsURL(),
saplingParamsSourceURL: SaplingParamsSourceURL.tests,
downloadBatchSize: 100,
retries: 5,
maxBackoffInterval: 10,
@ -262,6 +265,7 @@ class BlockBatchValidationTests: XCTestCase {
dataDb: try! __dataDbURL(),
spendParamsURL: try! __spendParamsURL(),
outputParamsURL: try! __outputParamsURL(),
saplingParamsSourceURL: SaplingParamsSourceURL.tests,
downloadBatchSize: 100,
retries: 5,
maxBackoffInterval: 10,
@ -329,6 +333,7 @@ class BlockBatchValidationTests: XCTestCase {
dataDb: try! __dataDbURL(),
spendParamsURL: try! __spendParamsURL(),
outputParamsURL: try! __outputParamsURL(),
saplingParamsSourceURL: SaplingParamsSourceURL.tests,
downloadBatchSize: 100,
retries: 5,
maxBackoffInterval: 10,
@ -418,6 +423,7 @@ class BlockBatchValidationTests: XCTestCase {
dataDb: try! __dataDbURL(),
spendParamsURL: try! __spendParamsURL(),
outputParamsURL: try! __outputParamsURL(),
saplingParamsSourceURL: SaplingParamsSourceURL.tests,
downloadBatchSize: 100,
retries: 5,
maxBackoffInterval: 10,
@ -498,6 +504,7 @@ class BlockBatchValidationTests: XCTestCase {
dataDb: try! __dataDbURL(),
spendParamsURL: try! __spendParamsURL(),
outputParamsURL: try! __outputParamsURL(),
saplingParamsSourceURL: SaplingParamsSourceURL.tests,
downloadBatchSize: 100,
retries: 5,
maxBackoffInterval: 10,

View File

@ -43,6 +43,7 @@ class WalletTests: XCTestCase {
let derivationTool = DerivationTool(networkType: network.networkType)
let ufvk = try derivationTool.deriveUnifiedSpendingKey(seed: seedData.bytes, accountIndex: 0)
.map({ try derivationTool.deriveUnifiedFullViewingKey(from: $0) })
let wallet = Initializer(
fsBlockDbRoot: self.testTempDirectory,
dataDbURL: try __dataDbURL(),
@ -51,6 +52,7 @@ class WalletTests: XCTestCase {
network: network,
spendParamsURL: try __spendParamsURL(),
outputParamsURL: try __outputParamsURL(),
saplingParamsSourceURL: SaplingParamsSourceURL.tests,
viewingKeys: [ufvk],
walletBirthday: 663194
)

View File

@ -58,6 +58,7 @@ class SynchronizerTests: XCTestCase {
network: network,
spendParamsURL: try __spendParamsURL(),
outputParamsURL: try __outputParamsURL(),
saplingParamsSourceURL: SaplingParamsSourceURL.tests,
viewingKeys: [ufvk],
walletBirthday: birthday,
alias: "",

Binary file not shown.

Binary file not shown.

View File

@ -398,3 +398,26 @@ class MockRustBackend: ZcashRustBackendWelding {
false
}
}
extension SaplingParamsSourceURL {
static var tests = SaplingParamsSourceURL(
spendParamFileURL: Bundle.module.url(forResource: "sapling-spend", withExtension: "params")!,
outputParamFileURL: Bundle.module.url(forResource: "sapling-output", withExtension: "params")!
)
}
extension CompactBlockProcessor.Configuration {
/// Standard configuration for most compact block processors
static func standard(for network: ZcashNetwork, walletBirthday: BlockHeight) -> CompactBlockProcessor.Configuration {
let pathProvider = DefaultResourceProvider(network: network)
return CompactBlockProcessor.Configuration(
fsBlockCacheRoot: pathProvider.fsCacheURL,
dataDb: pathProvider.dataDbURL,
spendParamsURL: pathProvider.spendParamsURL,
outputParamsURL: pathProvider.outputParamsURL,
saplingParamsSourceURL: SaplingParamsSourceURL.tests,
walletBirthday: walletBirthday,
network: network
)
}
}

View File

@ -223,6 +223,7 @@ extension TestCoordinator {
dataDb: config.dataDb,
spendParamsURL: config.spendParamsURL,
outputParamsURL: config.outputParamsURL,
saplingParamsSourceURL: config.saplingParamsSourceURL,
downloadBatchSize: config.downloadBatchSize,
retries: config.retries,
maxBackoffInterval: config.maxBackoffInterval,
@ -291,6 +292,7 @@ enum TestSynchronizerBuilder {
network: network,
spendParamsURL: spendParamsURL,
outputParamsURL: outputParamsURL,
saplingParamsSourceURL: SaplingParamsSourceURL.tests,
viewingKeys: [unifiedFullViewingKey],
walletBirthday: walletBirthday,
alias: "",