Bundle sapling params in APK instead of downloading from remote servers
- Add sapling-spend.params and sapling-output.params to sdk-lib assets - Rewrite SaplingParamTool to copy params from bundled assets instead of HTTP download - Remove OkHttp dependency from SaplingParamTool - Initialize SaplingParamTool with app context from Initializer
This commit is contained in:
BIN
sdk-lib/src/main/assets/params/sapling-output.params
Normal file
BIN
sdk-lib/src/main/assets/params/sapling-output.params
Normal file
Binary file not shown.
BIN
sdk-lib/src/main/assets/params/sapling-spend.params
Normal file
BIN
sdk-lib/src/main/assets/params/sapling-spend.params
Normal file
Binary file not shown.
@@ -3,6 +3,7 @@ package cash.z.ecc.android.sdk
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import cash.z.ecc.android.sdk.exception.InitializerException
|
import cash.z.ecc.android.sdk.exception.InitializerException
|
||||||
import cash.z.ecc.android.sdk.ext.ZcashSdk
|
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.SdkDispatchers
|
||||||
import cash.z.ecc.android.sdk.internal.ext.getCacheDirSuspend
|
import cash.z.ecc.android.sdk.internal.ext.getCacheDirSuspend
|
||||||
import cash.z.ecc.android.sdk.internal.ext.getDatabasePathSuspend
|
import cash.z.ecc.android.sdk.internal.ext.getDatabasePathSuspend
|
||||||
@@ -319,6 +320,7 @@ class Initializer private constructor(
|
|||||||
config: Config
|
config: Config
|
||||||
): Initializer {
|
): Initializer {
|
||||||
config.validate()
|
config.validate()
|
||||||
|
SaplingParamTool.init(context)
|
||||||
|
|
||||||
val loadedCheckpoint = run {
|
val loadedCheckpoint = run {
|
||||||
val height = config.birthdayHeight
|
val height = config.birthdayHeight
|
||||||
|
|||||||
@@ -1,21 +1,28 @@
|
|||||||
package cash.z.ecc.android.sdk.internal
|
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.exception.TransactionEncoderException
|
||||||
import cash.z.ecc.android.sdk.ext.ZcashSdk
|
import cash.z.ecc.android.sdk.ext.ZcashSdk
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import okhttp3.OkHttpClient
|
|
||||||
import okhttp3.Request
|
|
||||||
import okio.buffer
|
|
||||||
import okio.sink
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class SaplingParamTool {
|
class SaplingParamTool {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
|
private var appContext: Context? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks the given directory for the output and spending params and calls [fetchParams] if
|
* Initialize with application context so params can be copied from bundled assets.
|
||||||
* they're missing.
|
*/
|
||||||
|
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.
|
* @param destinationDir the directory where the params should be stored.
|
||||||
*/
|
*/
|
||||||
@@ -32,64 +39,44 @@ class SaplingParamTool {
|
|||||||
}
|
}
|
||||||
if (hadError) {
|
if (hadError) {
|
||||||
try {
|
try {
|
||||||
Bush.trunk.twigTask("attempting to download missing params") {
|
Bush.trunk.twigTask("copying bundled sapling params") {
|
||||||
fetchParams(destinationDir)
|
copyBundledParams(destinationDir)
|
||||||
}
|
}
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
twig("failed to fetch params due to: $e")
|
twig("failed to copy bundled params due to: $e")
|
||||||
throw TransactionEncoderException.MissingParamsException
|
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
|
* @param destinationDir the directory where the params will be stored.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
suspend fun fetchParams(destinationDir: String) {
|
private suspend fun copyBundledParams(destinationDir: String) {
|
||||||
val client = createHttpClient()
|
val context = appContext
|
||||||
var failureMessage = ""
|
?: throw IllegalStateException("SaplingParamTool not initialized. Call init(context) first.")
|
||||||
|
|
||||||
arrayOf(
|
arrayOf(
|
||||||
ZcashSdk.SPEND_PARAM_FILE_NAME,
|
ZcashSdk.SPEND_PARAM_FILE_NAME,
|
||||||
ZcashSdk.OUTPUT_PARAM_FILE_NAME
|
ZcashSdk.OUTPUT_PARAM_FILE_NAME
|
||||||
).forEach { paramFileName ->
|
).forEach { paramFileName ->
|
||||||
val url = "${ZcashSdk.CLOUD_PARAM_DIR_URL.random()}/$paramFileName"
|
val destFile = File(destinationDir, paramFileName)
|
||||||
twig("Downloading Sapling params from ${url}...")
|
if (!destFile.existsSuspend()) {
|
||||||
val request = Request.Builder().url(url).build()
|
if (destFile.parentFile?.existsSuspend() != true) {
|
||||||
val response = withContext(Dispatchers.IO) { client.newCall(request).execute() }
|
destFile.parentFile?.mkdirsSuspend()
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
response.body?.let { body ->
|
context.assets.open("params/$paramFileName").use { input ->
|
||||||
body.source().use { source ->
|
destFile.outputStream().use { output ->
|
||||||
file.sink().buffer().use { sink ->
|
twig("copying bundled $paramFileName to $destFile")
|
||||||
twig("writing to $file")
|
input.copyTo(output)
|
||||||
sink.writeAll(source)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} 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) {
|
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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user