diff --git a/sdk-lib/src/main/assets/params/sapling-output.params b/sdk-lib/src/main/assets/params/sapling-output.params new file mode 100644 index 0000000..01760fa Binary files /dev/null and b/sdk-lib/src/main/assets/params/sapling-output.params differ diff --git a/sdk-lib/src/main/assets/params/sapling-spend.params b/sdk-lib/src/main/assets/params/sapling-spend.params new file mode 100644 index 0000000..b91cd77 Binary files /dev/null and b/sdk-lib/src/main/assets/params/sapling-spend.params differ diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/Initializer.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/Initializer.kt index b3b4882..9e66bd1 100644 --- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/Initializer.kt +++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/Initializer.kt @@ -3,6 +3,7 @@ package cash.z.ecc.android.sdk import android.content.Context import cash.z.ecc.android.sdk.exception.InitializerException import cash.z.ecc.android.sdk.ext.ZcashSdk +import cash.z.ecc.android.sdk.internal.SaplingParamTool import cash.z.ecc.android.sdk.internal.SdkDispatchers import cash.z.ecc.android.sdk.internal.ext.getCacheDirSuspend import cash.z.ecc.android.sdk.internal.ext.getDatabasePathSuspend @@ -319,6 +320,7 @@ class Initializer private constructor( config: Config ): Initializer { config.validate() + SaplingParamTool.init(context) val loadedCheckpoint = run { val height = config.birthdayHeight diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/SaplingParamTool.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/SaplingParamTool.kt index 58e2676..dcad7e3 100644 --- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/SaplingParamTool.kt +++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/SaplingParamTool.kt @@ -1,21 +1,28 @@ package cash.z.ecc.android.sdk.internal +import android.content.Context import cash.z.ecc.android.sdk.exception.TransactionEncoderException import cash.z.ecc.android.sdk.ext.ZcashSdk import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -import okhttp3.OkHttpClient -import okhttp3.Request -import okio.buffer -import okio.sink import java.io.File class SaplingParamTool { companion object { + + private var appContext: Context? = null + /** - * Checks the given directory for the output and spending params and calls [fetchParams] if - * they're missing. + * Initialize with application context so params can be copied from bundled assets. + */ + fun init(context: Context) { + appContext = context.applicationContext + } + + /** + * Checks the given directory for the output and spending params and copies them from + * bundled assets if they're missing. * * @param destinationDir the directory where the params should be stored. */ @@ -32,64 +39,44 @@ class SaplingParamTool { } if (hadError) { try { - Bush.trunk.twigTask("attempting to download missing params") { - fetchParams(destinationDir) + Bush.trunk.twigTask("copying bundled sapling params") { + copyBundledParams(destinationDir) } } catch (e: Throwable) { - twig("failed to fetch params due to: $e") + twig("failed to copy bundled params due to: $e") throw TransactionEncoderException.MissingParamsException } } } /** - * Download and store the params into the given directory. + * Copy the sapling params from bundled assets into the given directory. * - * @param destinationDir the directory where the params will be stored. It's assumed that we - * have write access to this directory. Typically, this should be the app's cache directory - * because it is not harmful if these files are cleared by the user since they are downloaded - * on-demand. + * @param destinationDir the directory where the params will be stored. */ - suspend fun fetchParams(destinationDir: String) { - val client = createHttpClient() - var failureMessage = "" + private suspend fun copyBundledParams(destinationDir: String) { + val context = appContext + ?: throw IllegalStateException("SaplingParamTool not initialized. Call init(context) first.") + arrayOf( ZcashSdk.SPEND_PARAM_FILE_NAME, ZcashSdk.OUTPUT_PARAM_FILE_NAME ).forEach { paramFileName -> - val url = "${ZcashSdk.CLOUD_PARAM_DIR_URL.random()}/$paramFileName" - twig("Downloading Sapling params from ${url}...") - val request = Request.Builder().url(url).build() - val response = withContext(Dispatchers.IO) { client.newCall(request).execute() } - if (response.isSuccessful) { - twig("fetch succeeded", -1) - val file = File(destinationDir, paramFileName) - if (file.parentFile?.existsSuspend() == true) { - twig("directory exists!", -1) - } else { - twig("directory did not exist attempting to make it") - file.parentFile?.mkdirsSuspend() + val destFile = File(destinationDir, paramFileName) + if (!destFile.existsSuspend()) { + if (destFile.parentFile?.existsSuspend() != true) { + destFile.parentFile?.mkdirsSuspend() } withContext(Dispatchers.IO) { - response.body?.let { body -> - body.source().use { source -> - file.sink().buffer().use { sink -> - twig("writing to $file") - sink.writeAll(source) - } + context.assets.open("params/$paramFileName").use { input -> + destFile.outputStream().use { output -> + twig("copying bundled $paramFileName to $destFile") + input.copyTo(output) } } } - } else { - failureMessage += "Error while fetching $paramFileName : $response\n" - twig(failureMessage) } - - twig("fetch succeeded, done writing $paramFileName") } - if (failureMessage.isNotEmpty()) throw TransactionEncoderException.FetchParamsException( - failureMessage - ) } suspend fun clear(destinationDir: String) { @@ -119,19 +106,6 @@ class SaplingParamTool { } } - // - // Helpers - // - /** - * Http client is only used for downloading sapling spend and output params data, which are - * necessary for the wallet to scan blocks. - * - * @return an http client suitable for downloading params data. - */ - private fun createHttpClient(): OkHttpClient { - // TODO: add logging and timeouts - return OkHttpClient() - } } }