2019-05-23 21:37:17 -07:00
|
|
|
package cash.z.wallet.sdk.jni
|
|
|
|
|
2019-11-01 13:25:28 -07:00
|
|
|
import cash.z.wallet.sdk.exception.BirthdayException
|
2019-09-26 09:58:37 -07:00
|
|
|
import cash.z.wallet.sdk.ext.ZcashSdk.OUTPUT_PARAM_FILE_NAME
|
|
|
|
import cash.z.wallet.sdk.ext.ZcashSdk.SPEND_PARAM_FILE_NAME
|
2019-11-01 13:25:28 -07:00
|
|
|
import cash.z.wallet.sdk.ext.twig
|
2019-10-21 03:26:02 -07:00
|
|
|
import java.io.File
|
2019-05-23 21:37:17 -07:00
|
|
|
|
|
|
|
/**
|
2019-09-26 09:58:37 -07:00
|
|
|
* Serves as the JNI boundary between the Kotlin and Rust layers. Functions in this class should
|
|
|
|
* not be called directly by code outside of the SDK. Instead, one of the higher-level components
|
|
|
|
* should be used such as Wallet.kt or CompactBlockProcessor.kt.
|
2019-05-23 21:37:17 -07:00
|
|
|
*/
|
2019-11-01 13:25:28 -07:00
|
|
|
class RustBackend : RustBackendWelding {
|
|
|
|
|
2019-11-23 17:47:50 -08:00
|
|
|
init {
|
|
|
|
load()
|
|
|
|
}
|
|
|
|
|
2020-02-11 16:56:31 -08:00
|
|
|
// Paths
|
|
|
|
internal lateinit var pathDataDb: String
|
|
|
|
internal lateinit var pathCacheDb: String
|
|
|
|
internal lateinit var pathParamsDir: String
|
|
|
|
|
2019-11-01 13:25:28 -07:00
|
|
|
internal var birthdayHeight: Int = -1
|
|
|
|
get() = if (field != -1) field else throw BirthdayException.UninitializedBirthdayException
|
|
|
|
|
2019-10-21 03:26:02 -07:00
|
|
|
/**
|
|
|
|
* Loads the library and initializes path variables. Although it is best to only call this
|
|
|
|
* function once, it is idempotent.
|
|
|
|
*/
|
2019-11-01 13:25:28 -07:00
|
|
|
fun init(
|
2020-02-11 16:56:31 -08:00
|
|
|
cacheDbPath: String,
|
|
|
|
dataDbPath: String,
|
|
|
|
paramsPath: String
|
2019-11-01 13:25:28 -07:00
|
|
|
): RustBackend {
|
2019-09-26 09:58:37 -07:00
|
|
|
twig("Creating RustBackend") {
|
2020-02-11 16:56:31 -08:00
|
|
|
pathCacheDb = cacheDbPath
|
|
|
|
pathDataDb = dataDbPath
|
|
|
|
pathParamsDir = paramsPath
|
2019-09-26 09:58:37 -07:00
|
|
|
}
|
|
|
|
return this
|
|
|
|
}
|
|
|
|
|
2020-02-11 16:56:31 -08:00
|
|
|
fun clear(clearCacheDb: Boolean = true, clearDataDb: Boolean = true) {
|
|
|
|
if (clearCacheDb) {
|
|
|
|
twig("Deleting cache database!")
|
|
|
|
File(pathCacheDb).delete()
|
|
|
|
}
|
|
|
|
if (clearDataDb) {
|
|
|
|
twig("Deleting data database!")
|
|
|
|
File(pathDataDb).delete()
|
|
|
|
}
|
2019-10-21 03:26:02 -07:00
|
|
|
}
|
|
|
|
|
2019-09-26 09:58:37 -07:00
|
|
|
|
|
|
|
//
|
|
|
|
// Wrapper Functions
|
|
|
|
//
|
|
|
|
|
2020-02-11 16:56:31 -08:00
|
|
|
override fun initDataDb() = initDataDb(pathDataDb)
|
2019-11-01 13:25:28 -07:00
|
|
|
|
|
|
|
// override fun initAccountsTable(extfvks: Array<String>) =
|
|
|
|
// initAccountsTableWithKeys(dbDataPath, extfvks)
|
2019-09-26 09:58:37 -07:00
|
|
|
|
2019-11-01 13:25:28 -07:00
|
|
|
override fun initAccountsTable(
|
|
|
|
seed: ByteArray,
|
|
|
|
numberOfAccounts: Int
|
2020-02-11 16:56:31 -08:00
|
|
|
) = initAccountsTable(pathDataDb, seed, numberOfAccounts)
|
2019-09-26 09:58:37 -07:00
|
|
|
|
|
|
|
override fun initBlocksTable(
|
|
|
|
height: Int,
|
|
|
|
hash: String,
|
|
|
|
time: Long,
|
|
|
|
saplingTree: String
|
2019-11-01 13:25:28 -07:00
|
|
|
): Boolean {
|
|
|
|
birthdayHeight = height
|
2020-02-11 16:56:31 -08:00
|
|
|
return initBlocksTable(pathDataDb, height, hash, time, saplingTree)
|
2019-11-01 13:25:28 -07:00
|
|
|
}
|
2019-09-26 09:58:37 -07:00
|
|
|
|
2020-02-11 16:56:31 -08:00
|
|
|
override fun getAddress(account: Int) = getAddress(pathDataDb, account)
|
2019-05-23 21:37:17 -07:00
|
|
|
|
2020-02-11 16:56:31 -08:00
|
|
|
override fun getBalance(account: Int) = getBalance(pathDataDb, account)
|
2019-05-23 21:37:17 -07:00
|
|
|
|
2020-02-11 16:56:31 -08:00
|
|
|
override fun getVerifiedBalance(account: Int) = getVerifiedBalance(pathDataDb, account)
|
2019-09-26 09:58:37 -07:00
|
|
|
|
2019-11-01 13:25:28 -07:00
|
|
|
override fun getReceivedMemoAsUtf8(idNote: Long) =
|
2020-02-11 16:56:31 -08:00
|
|
|
getReceivedMemoAsUtf8(pathDataDb, idNote)
|
2019-09-26 09:58:37 -07:00
|
|
|
|
2020-02-11 16:56:31 -08:00
|
|
|
override fun getSentMemoAsUtf8(idNote: Long) = getSentMemoAsUtf8(pathDataDb, idNote)
|
2019-09-26 09:58:37 -07:00
|
|
|
|
2020-02-11 16:56:31 -08:00
|
|
|
override fun validateCombinedChain() = validateCombinedChain(pathCacheDb, pathDataDb)
|
2019-09-26 09:58:37 -07:00
|
|
|
|
2020-02-11 16:56:31 -08:00
|
|
|
override fun rewindToHeight(height: Int) = rewindToHeight(pathDataDb, height)
|
2019-09-26 09:58:37 -07:00
|
|
|
|
2020-01-14 09:56:03 -08:00
|
|
|
override fun scanBlocks(limit: Int): Boolean {
|
|
|
|
return if (limit > 0) {
|
2020-02-11 16:56:31 -08:00
|
|
|
scanBlockBatch(pathCacheDb, pathDataDb, limit)
|
2020-01-14 09:56:03 -08:00
|
|
|
} else {
|
2020-02-11 16:56:31 -08:00
|
|
|
scanBlocks(pathCacheDb, pathDataDb)
|
2020-01-14 09:56:03 -08:00
|
|
|
}
|
|
|
|
}
|
2019-09-26 09:58:37 -07:00
|
|
|
|
2020-03-12 21:41:17 -07:00
|
|
|
override fun decryptAndStoreTransaction(tx: ByteArray) = decryptAndStoreTransaction(pathDataDb, tx)
|
|
|
|
|
2019-09-26 09:58:37 -07:00
|
|
|
override fun createToAddress(
|
2020-06-09 19:14:22 -07:00
|
|
|
consensusBranchId: Long,
|
2019-09-26 09:58:37 -07:00
|
|
|
account: Int,
|
|
|
|
extsk: String,
|
|
|
|
to: String,
|
|
|
|
value: Long,
|
2019-11-12 08:58:15 -08:00
|
|
|
memo: ByteArray?
|
2019-09-26 09:58:37 -07:00
|
|
|
): Long = createToAddress(
|
2020-02-11 16:56:31 -08:00
|
|
|
pathDataDb,
|
2020-06-09 19:14:22 -07:00
|
|
|
consensusBranchId,
|
2019-09-26 09:58:37 -07:00
|
|
|
account,
|
|
|
|
extsk,
|
|
|
|
to,
|
|
|
|
value,
|
2019-11-12 08:58:15 -08:00
|
|
|
memo ?: ByteArray(0),
|
2020-02-11 16:56:31 -08:00
|
|
|
"${pathParamsDir}/$SPEND_PARAM_FILE_NAME",
|
|
|
|
"${pathParamsDir}/$OUTPUT_PARAM_FILE_NAME"
|
2019-09-26 09:58:37 -07:00
|
|
|
)
|
|
|
|
|
2019-11-01 13:25:28 -07:00
|
|
|
override fun deriveSpendingKeys(seed: ByteArray, numberOfAccounts: Int) =
|
|
|
|
deriveExtendedSpendingKeys(seed, numberOfAccounts)
|
2019-09-26 09:58:37 -07:00
|
|
|
|
2019-11-01 13:25:28 -07:00
|
|
|
override fun deriveViewingKeys(seed: ByteArray, numberOfAccounts: Int) =
|
|
|
|
deriveExtendedFullViewingKeys(seed, numberOfAccounts)
|
2019-09-26 09:58:37 -07:00
|
|
|
|
2019-11-01 13:25:28 -07:00
|
|
|
override fun deriveViewingKey(spendingKey: String) = deriveExtendedFullViewingKey(spendingKey)
|
2019-09-26 09:58:37 -07:00
|
|
|
|
2020-02-11 16:56:31 -08:00
|
|
|
override fun deriveAddress(seed: ByteArray, accountIndex: Int) =
|
|
|
|
deriveAddressFromSeed(seed, accountIndex)
|
|
|
|
|
|
|
|
override fun deriveAddress(viewingKey: String) = deriveAddressFromViewingKey(viewingKey)
|
|
|
|
|
2020-06-09 19:14:22 -07:00
|
|
|
override fun isValidShieldedAddr(addr: String) = isValidShieldedAddress(addr)
|
2019-05-23 21:37:17 -07:00
|
|
|
|
2020-06-09 19:14:22 -07:00
|
|
|
override fun isValidTransparentAddr(addr: String) = isValidTransparentAddress(addr)
|
|
|
|
|
|
|
|
override fun getBranchIdForHeight(height: Int): Long = branchIdForHeight(height)
|
2019-05-23 21:37:17 -07:00
|
|
|
|
2019-11-01 13:25:28 -07:00
|
|
|
/**
|
|
|
|
* Exposes all of the librustzcash functions along with helpers for loading the static library.
|
|
|
|
*/
|
|
|
|
companion object {
|
|
|
|
private var loaded = false
|
|
|
|
|
|
|
|
fun load() {
|
|
|
|
// It is safe to call these things twice but not efficient. So we add a loose check and
|
|
|
|
// ignore the fact that it's not thread-safe.
|
|
|
|
if (!loaded) {
|
|
|
|
twig("Loading RustBackend") {
|
|
|
|
loadRustLibrary()
|
|
|
|
initLogs()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The first call made to this object in order to load the Rust backend library. All other
|
|
|
|
* external function calls will fail if the libraries have not been loaded.
|
|
|
|
*/
|
|
|
|
private fun loadRustLibrary() {
|
|
|
|
try {
|
|
|
|
System.loadLibrary("zcashwalletsdk")
|
|
|
|
loaded = true
|
|
|
|
} catch (e: Throwable) {
|
|
|
|
twig("Error while loading native library: ${e.message}")
|
|
|
|
}
|
|
|
|
}
|
2019-05-23 21:37:17 -07:00
|
|
|
|
2019-06-04 07:15:19 -07:00
|
|
|
|
2019-11-01 13:25:28 -07:00
|
|
|
//
|
|
|
|
// External Functions
|
|
|
|
//
|
2019-06-04 07:15:19 -07:00
|
|
|
|
2019-11-01 13:25:28 -07:00
|
|
|
@JvmStatic private external fun initDataDb(dbDataPath: String): Boolean
|
2019-05-23 21:37:17 -07:00
|
|
|
|
2019-11-01 13:25:28 -07:00
|
|
|
@JvmStatic private external fun initAccountsTable(
|
|
|
|
dbDataPath: String,
|
|
|
|
seed: ByteArray,
|
|
|
|
accounts: Int
|
|
|
|
): Array<String>
|
2019-05-23 21:37:17 -07:00
|
|
|
|
2019-11-01 13:25:28 -07:00
|
|
|
// @JvmStatic private external fun initAccountsTableWithKeys(
|
|
|
|
// dbDataPath: String,
|
|
|
|
// extfvk: Array<String>
|
|
|
|
// )
|
2019-05-23 21:37:17 -07:00
|
|
|
|
2019-11-01 13:25:28 -07:00
|
|
|
@JvmStatic private external fun initBlocksTable(
|
|
|
|
dbDataPath: String,
|
|
|
|
height: Int,
|
|
|
|
hash: String,
|
|
|
|
time: Long,
|
|
|
|
saplingTree: String
|
|
|
|
): Boolean
|
2019-05-23 21:37:17 -07:00
|
|
|
|
2019-11-01 13:25:28 -07:00
|
|
|
@JvmStatic private external fun getAddress(dbDataPath: String, account: Int): String
|
2019-05-23 21:37:17 -07:00
|
|
|
|
2019-11-01 13:25:28 -07:00
|
|
|
@JvmStatic private external fun isValidShieldedAddress(addr: String): Boolean
|
2019-05-23 21:37:17 -07:00
|
|
|
|
2019-11-01 13:25:28 -07:00
|
|
|
@JvmStatic private external fun isValidTransparentAddress(addr: String): Boolean
|
|
|
|
|
|
|
|
@JvmStatic private external fun getBalance(dbDataPath: String, account: Int): Long
|
|
|
|
|
|
|
|
@JvmStatic private external fun getVerifiedBalance(dbDataPath: String, account: Int): Long
|
|
|
|
|
|
|
|
@JvmStatic private external fun getReceivedMemoAsUtf8(dbDataPath: String, idNote: Long): String
|
|
|
|
|
|
|
|
@JvmStatic private external fun getSentMemoAsUtf8(dbDataPath: String, idNote: Long): String
|
|
|
|
|
|
|
|
@JvmStatic private external fun validateCombinedChain(dbCachePath: String, dbDataPath: String): Int
|
|
|
|
|
|
|
|
@JvmStatic private external fun rewindToHeight(dbDataPath: String, height: Int): Boolean
|
|
|
|
|
|
|
|
@JvmStatic private external fun scanBlocks(dbCachePath: String, dbDataPath: String): Boolean
|
|
|
|
|
2020-01-14 09:56:03 -08:00
|
|
|
@JvmStatic private external fun scanBlockBatch(dbCachePath: String, dbDataPath: String, limit: Int): Boolean
|
|
|
|
|
2020-03-12 21:41:17 -07:00
|
|
|
@JvmStatic private external fun decryptAndStoreTransaction(dbDataPath: String, tx: ByteArray)
|
|
|
|
|
2019-11-01 13:25:28 -07:00
|
|
|
@JvmStatic private external fun createToAddress(
|
|
|
|
dbDataPath: String,
|
2020-06-09 19:14:22 -07:00
|
|
|
consensusBranchId: Long,
|
2019-11-01 13:25:28 -07:00
|
|
|
account: Int,
|
|
|
|
extsk: String,
|
|
|
|
to: String,
|
|
|
|
value: Long,
|
|
|
|
memo: ByteArray,
|
|
|
|
spendParamsPath: String,
|
|
|
|
outputParamsPath: String
|
|
|
|
): Long
|
|
|
|
|
|
|
|
@JvmStatic private external fun initLogs()
|
2019-05-23 21:37:17 -07:00
|
|
|
|
2019-11-01 13:25:28 -07:00
|
|
|
@JvmStatic private external fun deriveExtendedSpendingKeys(seed: ByteArray, numberOfAccounts: Int): Array<String>
|
2019-05-23 21:37:17 -07:00
|
|
|
|
2019-11-01 13:25:28 -07:00
|
|
|
@JvmStatic private external fun deriveExtendedFullViewingKeys(seed: ByteArray, numberOfAccounts: Int): Array<String>
|
|
|
|
|
|
|
|
@JvmStatic private external fun deriveExtendedFullViewingKey(spendingKey: String): String
|
2020-02-11 16:56:31 -08:00
|
|
|
|
|
|
|
@JvmStatic private external fun deriveAddressFromSeed(seed: ByteArray, accountIndex: Int): String
|
|
|
|
|
|
|
|
@JvmStatic private external fun deriveAddressFromViewingKey(key: String): String
|
2020-06-09 19:14:22 -07:00
|
|
|
|
|
|
|
@JvmStatic private external fun branchIdForHeight(height: Int): Long
|
2019-11-01 13:25:28 -07:00
|
|
|
}
|
2020-06-09 19:14:22 -07:00
|
|
|
}
|