forked from fekt/hush-android-wallet-sdk
Hush SDK for Android lite wallets
https://hush.is
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
140 lines
5.5 KiB
140 lines
5.5 KiB
package cash.z.ecc.android.sdk.internal
|
|
|
|
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 {
|
|
/**
|
|
* Checks the given directory for the output and spending params and calls [fetchParams] if
|
|
* they're missing.
|
|
*
|
|
* @param destinationDir the directory where the params should be stored.
|
|
*/
|
|
suspend fun ensureParams(destinationDir: String) {
|
|
var hadError = false
|
|
arrayOf(
|
|
ZcashSdk.SPEND_PARAM_FILE_NAME,
|
|
ZcashSdk.OUTPUT_PARAM_FILE_NAME
|
|
).forEach { paramFileName ->
|
|
if (!File(destinationDir, paramFileName).existsSuspend()) {
|
|
twig("WARNING: $paramFileName not found at location: $destinationDir")
|
|
hadError = true
|
|
}
|
|
}
|
|
if (hadError) {
|
|
try {
|
|
Bush.trunk.twigTask("attempting to download missing params") {
|
|
fetchParams(destinationDir)
|
|
}
|
|
} catch (e: Throwable) {
|
|
twig("failed to fetch params due to: $e")
|
|
throw TransactionEncoderException.MissingParamsException
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Download and store the params 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.
|
|
*/
|
|
suspend fun fetchParams(destinationDir: String) {
|
|
val client = createHttpClient()
|
|
var failureMessage = ""
|
|
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()
|
|
}
|
|
withContext(Dispatchers.IO) {
|
|
response.body?.let { body ->
|
|
body.source().use { source ->
|
|
file.sink().buffer().use { sink ->
|
|
twig("writing to $file")
|
|
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) {
|
|
if (validate(destinationDir)) {
|
|
arrayOf(
|
|
ZcashSdk.SPEND_PARAM_FILE_NAME,
|
|
ZcashSdk.OUTPUT_PARAM_FILE_NAME
|
|
).forEach { paramFileName ->
|
|
val file = File(destinationDir, paramFileName)
|
|
if (file.deleteRecursivelySuspend()) {
|
|
twig("Files deleted successfully")
|
|
} else {
|
|
twig("Error: Files not able to be deleted!")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
suspend fun validate(destinationDir: String): Boolean {
|
|
return arrayOf(
|
|
ZcashSdk.SPEND_PARAM_FILE_NAME,
|
|
ZcashSdk.OUTPUT_PARAM_FILE_NAME
|
|
).all { paramFileName ->
|
|
File(destinationDir, paramFileName).existsSuspend()
|
|
}.also {
|
|
println("Param files${if (!it) "did not" else ""} both exist!")
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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()
|
|
}
|
|
}
|
|
}
|
|
|
|
suspend fun File.existsSuspend() = withContext(Dispatchers.IO) { exists() }
|
|
suspend fun File.mkdirsSuspend() = withContext(Dispatchers.IO) { mkdirs() }
|
|
suspend fun File.deleteRecursivelySuspend() = withContext(Dispatchers.IO) { deleteRecursively() }
|
|
|