[#1014] Decorate Backend with Typesafe APIs
* [#1014] Decorate Backend with Typesafe APIs
This commit is contained in:
parent
0edaaafad1
commit
f1b5e3aade
|
@ -1,5 +1,8 @@
|
|||
# Change Log
|
||||
|
||||
## Unreleased
|
||||
- The SDK internally migrated from `BackendExt` rust backend extension functions to more type-safe `TypesafeBackend`.
|
||||
|
||||
## 1.19.0-beta01
|
||||
### Changed
|
||||
- Adopted the latest Bip39 version 1.0.5
|
||||
|
|
|
@ -12,7 +12,7 @@ import java.io.File
|
|||
/**
|
||||
* 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.
|
||||
* should be used such as WalletCoordinator.kt or CompactBlockProcessor.kt.
|
||||
*/
|
||||
@Suppress("TooManyFunctions")
|
||||
class RustBackend private constructor(
|
||||
|
@ -37,13 +37,11 @@ class RustBackend private constructor(
|
|||
var dataClearResult = true
|
||||
if (clearCache) {
|
||||
fsBlockDbRoot.deleteRecursivelySuspend().also { result ->
|
||||
// Twig.debug { "Deletion of the cache files ${if (result) "succeeded" else "failed"}!" }
|
||||
cacheClearResult = result
|
||||
}
|
||||
}
|
||||
if (clearDataDb) {
|
||||
dataDbFile.deleteSuspend().also { result ->
|
||||
// Twig.debug { "Deletion of the data database ${if (result) "succeeded" else "failed"}!" }
|
||||
dataClearResult = result
|
||||
}
|
||||
}
|
||||
|
@ -356,28 +354,6 @@ class RustBackend private constructor(
|
|||
override fun getBranchIdForHeight(height: Long): Long =
|
||||
branchIdForHeight(height, networkId = networkId)
|
||||
|
||||
// /**
|
||||
// * This is a proof-of-concept for doing Local RPC, where we are effectively using the JNI
|
||||
// * boundary as a grpc server. It is slightly inefficient in terms of both space and time but
|
||||
// * given that it is all done locally, on the heap, it seems to be a worthwhile tradeoff because
|
||||
// * it reduces the complexity and expands the capacity for the two layers to communicate.
|
||||
// *
|
||||
// * We're able to keep the "unsafe" byteArray functions private and wrap them in typeSafe
|
||||
// * equivalents and, eventually, surface any parse errors (for now, errors are only logged).
|
||||
// */
|
||||
// override fun parseTransactionDataList(
|
||||
// tdl: LocalRpcTypes.TransactionDataList
|
||||
// ): LocalRpcTypes.TransparentTransactionList {
|
||||
// return try {
|
||||
// // serialize the list, send it over to rust and get back a serialized set of results that we parse out
|
||||
// // and return
|
||||
// return LocalRpcTypes.TransparentTransactionList.parseFrom(parseTransactionDataList(tdl.toByteArray()))
|
||||
// } catch (t: Throwable) {
|
||||
// twig("ERROR: failed to parse transaction data list due to: $t caused by: ${t.cause}")
|
||||
// LocalRpcTypes.TransparentTransactionList.newBuilder().build()
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* Exposes all of the librustzcash functions along with helpers for loading the static library.
|
||||
*/
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package cash.z.ecc.android.sdk.internal.storage.block
|
||||
|
||||
import cash.z.ecc.android.sdk.internal.Backend
|
||||
import cash.z.ecc.android.sdk.internal.TypesafeBackend
|
||||
import cash.z.ecc.android.sdk.internal.TypesafeBackendImpl
|
||||
import cash.z.ecc.android.sdk.internal.ext.deleteRecursivelySuspend
|
||||
import cash.z.ecc.android.sdk.internal.ext.existsSuspend
|
||||
import cash.z.ecc.android.sdk.internal.ext.listSuspend
|
||||
|
@ -10,7 +11,6 @@ import cash.z.ecc.android.sdk.model.ZcashNetwork
|
|||
import cash.z.ecc.fixture.FakeRustBackendFixture
|
||||
import cash.z.ecc.fixture.FilePathFixture
|
||||
import co.electriccoin.lightwallet.client.fixture.ListOfCompactBlocksFixture
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.count
|
||||
import kotlinx.coroutines.flow.drop
|
||||
import kotlinx.coroutines.flow.first
|
||||
|
@ -29,7 +29,6 @@ import kotlin.test.assertTrue
|
|||
|
||||
class FileCompactBlockRepositoryTest {
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Before
|
||||
fun setup() = runTest {
|
||||
val blocksDirectory = FilePathFixture.newBlocksDir()
|
||||
|
@ -40,27 +39,28 @@ class FileCompactBlockRepositoryTest {
|
|||
blocksDirectory.mkdirsSuspend()
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@After
|
||||
fun tearDown() = runTest {
|
||||
FilePathFixture.newBlocksDir().deleteRecursivelySuspend()
|
||||
}
|
||||
|
||||
private fun getMockedFileCompactBlockRepository(
|
||||
rustBackend: Backend,
|
||||
backend: TypesafeBackend,
|
||||
rootBlocksDirectory: File
|
||||
): FileCompactBlockRepository = runBlocking {
|
||||
FileCompactBlockRepository(
|
||||
rootBlocksDirectory,
|
||||
rustBackend
|
||||
backend
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Test
|
||||
fun getLatestHeightTest() = runTest {
|
||||
val rustBackend = FakeRustBackendFixture().new()
|
||||
val blockRepository = getMockedFileCompactBlockRepository(rustBackend, FilePathFixture.newBlocksDir())
|
||||
val blockRepository = getMockedFileCompactBlockRepository(
|
||||
TypesafeBackendImpl(rustBackend),
|
||||
FilePathFixture.newBlocksDir()
|
||||
)
|
||||
|
||||
val blocks = ListOfCompactBlocksFixture.newFlow()
|
||||
|
||||
|
@ -69,12 +69,14 @@ class FileCompactBlockRepositoryTest {
|
|||
assertEquals(blocks.last().height, blockRepository.getLatestHeight()?.value)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Test
|
||||
fun findCompactBlockTest() = runTest {
|
||||
val network = ZcashNetwork.Testnet
|
||||
val rustBackend = FakeRustBackendFixture().new()
|
||||
val blockRepository = getMockedFileCompactBlockRepository(rustBackend, FilePathFixture.newBlocksDir())
|
||||
val blockRepository = getMockedFileCompactBlockRepository(
|
||||
TypesafeBackendImpl(rustBackend),
|
||||
FilePathFixture.newBlocksDir()
|
||||
)
|
||||
|
||||
val blocks = ListOfCompactBlocksFixture.newFlow()
|
||||
|
||||
|
@ -99,11 +101,13 @@ class FileCompactBlockRepositoryTest {
|
|||
assertNull(blockRepository.findCompactBlock(notPersistedBlockHeight))
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Test
|
||||
fun writeBlocksTest() = runTest {
|
||||
val rustBackend = FakeRustBackendFixture().new()
|
||||
val blockRepository = getMockedFileCompactBlockRepository(rustBackend, FilePathFixture.newBlocksDir())
|
||||
val blockRepository = getMockedFileCompactBlockRepository(
|
||||
TypesafeBackendImpl(rustBackend),
|
||||
FilePathFixture.newBlocksDir()
|
||||
)
|
||||
|
||||
assertTrue { rustBackend.metadata.isEmpty() }
|
||||
|
||||
|
@ -114,11 +118,13 @@ class FileCompactBlockRepositoryTest {
|
|||
assertEquals(blocks.count(), rustBackend.metadata.size)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Test
|
||||
fun writeFewBlocksTest() = runTest {
|
||||
val rustBackend = FakeRustBackendFixture().new()
|
||||
val blockRepository = getMockedFileCompactBlockRepository(rustBackend, FilePathFixture.newBlocksDir())
|
||||
val blockRepository = getMockedFileCompactBlockRepository(
|
||||
TypesafeBackendImpl(rustBackend),
|
||||
FilePathFixture.newBlocksDir()
|
||||
)
|
||||
|
||||
assertTrue { rustBackend.metadata.isEmpty() }
|
||||
|
||||
|
@ -134,12 +140,14 @@ class FileCompactBlockRepositoryTest {
|
|||
assertEquals(reducedBlocksList.count(), rustBackend.metadata.size)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Test
|
||||
fun writeBlocksAndCheckStorageTest() = runTest {
|
||||
val rustBackend = FakeRustBackendFixture().new()
|
||||
val rootBlocksDirectory = FilePathFixture.newBlocksDir()
|
||||
val blockRepository = getMockedFileCompactBlockRepository(rustBackend, rootBlocksDirectory)
|
||||
val blockRepository = getMockedFileCompactBlockRepository(
|
||||
TypesafeBackendImpl(rustBackend),
|
||||
FilePathFixture.newBlocksDir()
|
||||
)
|
||||
|
||||
assertTrue { rootBlocksDirectory.exists() }
|
||||
assertTrue { rootBlocksDirectory.list()!!.isEmpty() }
|
||||
|
@ -153,14 +161,16 @@ class FileCompactBlockRepositoryTest {
|
|||
assertEquals(blocks.count(), rootBlocksDirectory.list()!!.size)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Test
|
||||
fun deleteCompactBlockFilesTest() = runTest {
|
||||
val rustBackend = FakeRustBackendFixture().new()
|
||||
val blocksDirectory = FilePathFixture.newBlocksDir()
|
||||
val parentDirectory = blocksDirectory.parentFile!!
|
||||
|
||||
val blockRepository = getMockedFileCompactBlockRepository(rustBackend, blocksDirectory)
|
||||
val blockRepository = getMockedFileCompactBlockRepository(
|
||||
TypesafeBackendImpl(rustBackend),
|
||||
FilePathFixture.newBlocksDir()
|
||||
)
|
||||
|
||||
val testedBlocksRange = ListOfCompactBlocksFixture.DEFAULT_FILE_BLOCK_RANGE
|
||||
val blocks = ListOfCompactBlocksFixture.newFlow(testedBlocksRange)
|
||||
|
@ -190,11 +200,13 @@ class FileCompactBlockRepositoryTest {
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Test
|
||||
fun rewindToTest() = runTest {
|
||||
val rustBackend = FakeRustBackendFixture().new()
|
||||
val blockRepository = getMockedFileCompactBlockRepository(rustBackend, FilePathFixture.newBlocksDir())
|
||||
val blockRepository = getMockedFileCompactBlockRepository(
|
||||
TypesafeBackendImpl(rustBackend),
|
||||
FilePathFixture.newBlocksDir()
|
||||
)
|
||||
|
||||
val testedBlocksRange = ListOfCompactBlocksFixture.DEFAULT_FILE_BLOCK_RANGE
|
||||
|
||||
|
@ -229,7 +241,6 @@ class FileCompactBlockRepositoryTest {
|
|||
assertEquals(expectedKeptMetadataCount, keptMetadataBelowRewindHeight.size)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Test
|
||||
fun createTemporaryFileTest() = runTest {
|
||||
val blocksDir = FilePathFixture.newBlocksDir()
|
||||
|
@ -241,7 +252,6 @@ class FileCompactBlockRepositoryTest {
|
|||
assertTrue { file.existsSuspend() }
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Test
|
||||
fun finalizeFileTest() = runTest {
|
||||
val blocksDir = FilePathFixture.newBlocksDir()
|
||||
|
|
|
@ -2,10 +2,9 @@ package cash.z.ecc.android.sdk.jni
|
|||
|
||||
import cash.z.ecc.android.sdk.annotation.MaintainedTest
|
||||
import cash.z.ecc.android.sdk.annotation.TestPurpose
|
||||
import cash.z.ecc.android.sdk.internal.Backend
|
||||
import cash.z.ecc.android.sdk.internal.getBranchIdForHeight
|
||||
import cash.z.ecc.android.sdk.internal.TypesafeBackend
|
||||
import cash.z.ecc.android.sdk.internal.TypesafeBackendImpl
|
||||
import cash.z.ecc.android.sdk.internal.jni.RustBackend
|
||||
import cash.z.ecc.android.sdk.internal.network
|
||||
import cash.z.ecc.android.sdk.model.BlockHeight
|
||||
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
@ -25,15 +24,15 @@ class BranchIdTest internal constructor(
|
|||
private val height: BlockHeight,
|
||||
private val branchId: Long,
|
||||
private val branchHex: String,
|
||||
private val rustBackend: Backend
|
||||
private val backend: TypesafeBackend
|
||||
) {
|
||||
|
||||
@Test
|
||||
fun testBranchId_Hex() {
|
||||
val branchId = rustBackend.getBranchIdForHeight(height)
|
||||
val branchId = backend.getBranchIdForHeight(height)
|
||||
val clientBranch = "%x".format(branchId)
|
||||
assertEquals(
|
||||
"Invalid branch Id Hex value for $networkName at height $height on ${rustBackend.network.networkName}",
|
||||
"Invalid branch Id Hex value for $networkName at height $height on ${backend.network.networkName}",
|
||||
branchHex,
|
||||
clientBranch
|
||||
)
|
||||
|
@ -41,9 +40,9 @@ class BranchIdTest internal constructor(
|
|||
|
||||
@Test
|
||||
fun testBranchId_Numeric() {
|
||||
val actual = rustBackend.getBranchIdForHeight(height)
|
||||
val actual = backend.getBranchIdForHeight(height)
|
||||
assertEquals(
|
||||
"Invalid branch ID for $networkName at height $height on ${rustBackend.network.networkName}",
|
||||
"Invalid branch ID for $networkName at height $height on ${backend.network.networkName}",
|
||||
branchId,
|
||||
actual
|
||||
)
|
||||
|
@ -60,21 +59,25 @@ class BranchIdTest internal constructor(
|
|||
// However, due to quirks on certain devices, we created this test at the Android level,
|
||||
// as a sanity check
|
||||
val testnetBackend = runBlocking {
|
||||
RustBackend.new(
|
||||
File(""),
|
||||
File(""),
|
||||
File(""),
|
||||
File(""),
|
||||
ZcashNetwork.Testnet.id,
|
||||
TypesafeBackendImpl(
|
||||
RustBackend.new(
|
||||
File(""),
|
||||
File(""),
|
||||
File(""),
|
||||
File(""),
|
||||
ZcashNetwork.Testnet.id,
|
||||
)
|
||||
)
|
||||
}
|
||||
val mainnetBackend = runBlocking {
|
||||
RustBackend.new(
|
||||
File(""),
|
||||
File(""),
|
||||
File(""),
|
||||
File(""),
|
||||
ZcashNetwork.Mainnet.id,
|
||||
TypesafeBackendImpl(
|
||||
RustBackend.new(
|
||||
File(""),
|
||||
File(""),
|
||||
File(""),
|
||||
File(""),
|
||||
ZcashNetwork.Mainnet.id,
|
||||
)
|
||||
)
|
||||
}
|
||||
return listOf(
|
||||
|
|
|
@ -15,9 +15,10 @@ import cash.z.ecc.android.sdk.exception.TransactionEncoderException
|
|||
import cash.z.ecc.android.sdk.exception.TransactionSubmitException
|
||||
import cash.z.ecc.android.sdk.ext.ConsensusBranchId
|
||||
import cash.z.ecc.android.sdk.ext.ZcashSdk
|
||||
import cash.z.ecc.android.sdk.internal.Backend
|
||||
import cash.z.ecc.android.sdk.internal.SaplingParamTool
|
||||
import cash.z.ecc.android.sdk.internal.Twig
|
||||
import cash.z.ecc.android.sdk.internal.TypesafeBackend
|
||||
import cash.z.ecc.android.sdk.internal.TypesafeBackendImpl
|
||||
import cash.z.ecc.android.sdk.internal.block.CompactBlockDownloader
|
||||
import cash.z.ecc.android.sdk.internal.db.DatabaseCoordinator
|
||||
import cash.z.ecc.android.sdk.internal.db.derived.DbDerivedDataRepository
|
||||
|
@ -94,7 +95,7 @@ class SdkSynchronizer private constructor(
|
|||
private val storage: DerivedDataRepository,
|
||||
private val txManager: OutboundTransactionManager,
|
||||
val processor: CompactBlockProcessor,
|
||||
private val backend: Backend
|
||||
private val backend: TypesafeBackend
|
||||
) : CloseableSynchronizer {
|
||||
|
||||
companion object {
|
||||
|
@ -120,7 +121,7 @@ class SdkSynchronizer private constructor(
|
|||
repository: DerivedDataRepository,
|
||||
txManager: OutboundTransactionManager,
|
||||
processor: CompactBlockProcessor,
|
||||
backend: Backend
|
||||
backend: TypesafeBackend
|
||||
): CloseableSynchronizer {
|
||||
val synchronizerKey = SynchronizerKey(zcashNetwork, alias)
|
||||
|
||||
|
@ -510,7 +511,7 @@ class SdkSynchronizer private constructor(
|
|||
|
||||
// Not ready to be a public API; internal for testing only
|
||||
internal suspend fun createAccount(seed: ByteArray): UnifiedSpendingKey =
|
||||
CompactBlockProcessor.createAccount(backend, seed)
|
||||
backend.createAccountAndGetSpendingKey(seed)
|
||||
|
||||
/**
|
||||
* Returns the current Unified Address for this account.
|
||||
|
@ -634,20 +635,22 @@ internal object DefaultSynchronizerFactory {
|
|||
alias: String,
|
||||
saplingParamTool: SaplingParamTool,
|
||||
coordinator: DatabaseCoordinator
|
||||
): Backend {
|
||||
return RustBackend.new(
|
||||
coordinator.fsBlockDbRoot(network, alias),
|
||||
coordinator.dataDbFile(network, alias),
|
||||
saplingOutputFile = saplingParamTool.outputParamsFile,
|
||||
saplingSpendFile = saplingParamTool.spendParamsFile,
|
||||
zcashNetworkId = network.id
|
||||
): TypesafeBackend {
|
||||
return TypesafeBackendImpl(
|
||||
RustBackend.new(
|
||||
coordinator.fsBlockDbRoot(network, alias),
|
||||
coordinator.dataDbFile(network, alias),
|
||||
saplingOutputFile = saplingParamTool.outputParamsFile,
|
||||
saplingSpendFile = saplingParamTool.spendParamsFile,
|
||||
zcashNetworkId = network.id
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal suspend fun defaultDerivedDataRepository(
|
||||
context: Context,
|
||||
rustBackend: Backend,
|
||||
rustBackend: TypesafeBackend,
|
||||
databaseFile: File,
|
||||
zcashNetwork: ZcashNetwork,
|
||||
checkpoint: Checkpoint,
|
||||
|
@ -666,7 +669,10 @@ internal object DefaultSynchronizerFactory {
|
|||
)
|
||||
)
|
||||
|
||||
internal suspend fun defaultCompactBlockRepository(blockCacheRoot: File, backend: Backend): CompactBlockRepository =
|
||||
internal suspend fun defaultCompactBlockRepository(
|
||||
blockCacheRoot: File,
|
||||
backend: TypesafeBackend
|
||||
): CompactBlockRepository =
|
||||
FileCompactBlockRepository.new(
|
||||
blockCacheRoot,
|
||||
backend
|
||||
|
@ -676,7 +682,7 @@ internal object DefaultSynchronizerFactory {
|
|||
LightWalletClient.new(context, lightWalletEndpoint)
|
||||
|
||||
internal fun defaultEncoder(
|
||||
backend: Backend,
|
||||
backend: TypesafeBackend,
|
||||
saplingParamTool: SaplingParamTool,
|
||||
repository: DerivedDataRepository
|
||||
): TransactionEncoder = TransactionEncoderImpl(backend, saplingParamTool, repository)
|
||||
|
@ -697,7 +703,7 @@ internal object DefaultSynchronizerFactory {
|
|||
}
|
||||
|
||||
internal fun defaultProcessor(
|
||||
backend: Backend,
|
||||
backend: TypesafeBackend,
|
||||
downloader: CompactBlockDownloader,
|
||||
repository: DerivedDataRepository,
|
||||
birthdayHeight: BlockHeight
|
||||
|
|
|
@ -13,35 +13,23 @@ import cash.z.ecc.android.sdk.exception.RustLayerException
|
|||
import cash.z.ecc.android.sdk.ext.ZcashSdk
|
||||
import cash.z.ecc.android.sdk.ext.ZcashSdk.MAX_BACKOFF_INTERVAL
|
||||
import cash.z.ecc.android.sdk.ext.ZcashSdk.POLL_INTERVAL
|
||||
import cash.z.ecc.android.sdk.internal.Backend
|
||||
import cash.z.ecc.android.sdk.internal.Twig
|
||||
import cash.z.ecc.android.sdk.internal.TypesafeBackend
|
||||
import cash.z.ecc.android.sdk.internal.block.CompactBlockDownloader
|
||||
import cash.z.ecc.android.sdk.internal.createAccountAndGetSpendingKey
|
||||
import cash.z.ecc.android.sdk.internal.ext.isNullOrEmpty
|
||||
import cash.z.ecc.android.sdk.internal.ext.length
|
||||
import cash.z.ecc.android.sdk.internal.ext.retryUpTo
|
||||
import cash.z.ecc.android.sdk.internal.ext.retryWithBackoff
|
||||
import cash.z.ecc.android.sdk.internal.ext.toHexReversed
|
||||
import cash.z.ecc.android.sdk.internal.getBalance
|
||||
import cash.z.ecc.android.sdk.internal.getBranchIdForHeight
|
||||
import cash.z.ecc.android.sdk.internal.getCurrentAddress
|
||||
import cash.z.ecc.android.sdk.internal.getDownloadedUtxoBalance
|
||||
import cash.z.ecc.android.sdk.internal.getNearestRewindHeight
|
||||
import cash.z.ecc.android.sdk.internal.getVerifiedBalance
|
||||
import cash.z.ecc.android.sdk.internal.listTransparentReceivers
|
||||
import cash.z.ecc.android.sdk.internal.model.BlockBatch
|
||||
import cash.z.ecc.android.sdk.internal.model.DbTransactionOverview
|
||||
import cash.z.ecc.android.sdk.internal.model.JniBlockMeta
|
||||
import cash.z.ecc.android.sdk.internal.model.ext.from
|
||||
import cash.z.ecc.android.sdk.internal.model.ext.toBlockHeight
|
||||
import cash.z.ecc.android.sdk.internal.network
|
||||
import cash.z.ecc.android.sdk.internal.repository.DerivedDataRepository
|
||||
import cash.z.ecc.android.sdk.internal.rewindToHeight
|
||||
import cash.z.ecc.android.sdk.internal.validateCombinedChainOrErrorBlockHeight
|
||||
import cash.z.ecc.android.sdk.model.Account
|
||||
import cash.z.ecc.android.sdk.model.BlockHeight
|
||||
import cash.z.ecc.android.sdk.model.PercentDecimal
|
||||
import cash.z.ecc.android.sdk.model.UnifiedSpendingKey
|
||||
import cash.z.ecc.android.sdk.model.WalletBalance
|
||||
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
||||
import co.electriccoin.lightwallet.client.ext.BenchmarkingExt
|
||||
|
@ -94,7 +82,7 @@ import kotlin.time.toDuration
|
|||
class CompactBlockProcessor internal constructor(
|
||||
val downloader: CompactBlockDownloader,
|
||||
private val repository: DerivedDataRepository,
|
||||
private val backend: Backend,
|
||||
private val backend: TypesafeBackend,
|
||||
minimumHeight: BlockHeight
|
||||
) {
|
||||
/**
|
||||
|
@ -627,7 +615,7 @@ class CompactBlockProcessor internal constructor(
|
|||
utxo.index,
|
||||
utxo.script,
|
||||
utxo.valueZat,
|
||||
utxo.height
|
||||
BlockHeight.new(backend.network, utxo.height)
|
||||
)
|
||||
true
|
||||
} catch (t: Throwable) {
|
||||
|
@ -685,7 +673,7 @@ class CompactBlockProcessor internal constructor(
|
|||
/**
|
||||
* Requests, processes and persists all blocks from the given range.
|
||||
*
|
||||
* @param rustBackend the Rust backend component
|
||||
* @param backend the Rust backend component
|
||||
* @param downloader the compact block downloader component
|
||||
* @param repository the derived data repository component
|
||||
* @param network the network in which the sync mechanism operates
|
||||
|
@ -700,7 +688,7 @@ class CompactBlockProcessor internal constructor(
|
|||
@VisibleForTesting
|
||||
@Suppress("LongParameterList", "LongMethod")
|
||||
internal suspend fun runSyncingAndEnhancing(
|
||||
backend: Backend,
|
||||
backend: TypesafeBackend,
|
||||
downloader: CompactBlockDownloader,
|
||||
repository: DerivedDataRepository,
|
||||
network: ZcashNetwork,
|
||||
|
@ -819,7 +807,7 @@ class CompactBlockProcessor internal constructor(
|
|||
enhanceTransactionDetails(
|
||||
range = currentEnhancingRange,
|
||||
repository = repository,
|
||||
rustBackend = backend,
|
||||
backend = backend,
|
||||
downloader = downloader
|
||||
).collect { enhancingResult ->
|
||||
Twig.debug { "Enhancing result: $enhancingResult" }
|
||||
|
@ -917,7 +905,7 @@ class CompactBlockProcessor internal constructor(
|
|||
}
|
||||
|
||||
@VisibleForTesting
|
||||
internal suspend fun validateBatchOfBlocks(batch: BlockBatch, backend: Backend): BlockProcessingResult {
|
||||
internal suspend fun validateBatchOfBlocks(batch: BlockBatch, backend: TypesafeBackend): BlockProcessingResult {
|
||||
Twig.verbose { "Starting to validate batch $batch" }
|
||||
|
||||
val result = backend.validateCombinedChainOrErrorBlockHeight(batch.range.length())
|
||||
|
@ -931,7 +919,7 @@ class CompactBlockProcessor internal constructor(
|
|||
}
|
||||
|
||||
@VisibleForTesting
|
||||
internal suspend fun scanBatchOfBlocks(batch: BlockBatch, backend: Backend): BlockProcessingResult {
|
||||
internal suspend fun scanBatchOfBlocks(batch: BlockBatch, backend: TypesafeBackend): BlockProcessingResult {
|
||||
return runCatching {
|
||||
backend.scanBlocks(batch.range.length())
|
||||
}.onSuccess {
|
||||
|
@ -980,7 +968,7 @@ class CompactBlockProcessor internal constructor(
|
|||
internal suspend fun enhanceTransactionDetails(
|
||||
range: ClosedRange<BlockHeight>,
|
||||
repository: DerivedDataRepository,
|
||||
rustBackend: Backend,
|
||||
backend: TypesafeBackend,
|
||||
downloader: CompactBlockDownloader
|
||||
): Flow<BlockProcessingResult> = flow {
|
||||
Twig.debug { "Enhancing transaction details for blocks $range" }
|
||||
|
@ -998,7 +986,7 @@ class CompactBlockProcessor internal constructor(
|
|||
}
|
||||
|
||||
newTxs.filter { it.minedHeight != null }.onEach { newTransaction ->
|
||||
val trEnhanceResult = enhanceTransaction(newTransaction, rustBackend, downloader)
|
||||
val trEnhanceResult = enhanceTransaction(newTransaction, backend, downloader)
|
||||
if (trEnhanceResult is BlockProcessingResult.FailedEnhance) {
|
||||
Twig.error { "Encountered transaction enhancing error: ${trEnhanceResult.error}" }
|
||||
emit(trEnhanceResult)
|
||||
|
@ -1013,7 +1001,7 @@ class CompactBlockProcessor internal constructor(
|
|||
|
||||
private suspend fun enhanceTransaction(
|
||||
transaction: DbTransactionOverview,
|
||||
backend: Backend,
|
||||
backend: TypesafeBackend,
|
||||
downloader: CompactBlockDownloader
|
||||
): BlockProcessingResult {
|
||||
Twig.debug { "Starting enhancing transaction (id:${transaction.id} block:${transaction.minedHeight})" }
|
||||
|
@ -1083,7 +1071,7 @@ class CompactBlockProcessor internal constructor(
|
|||
private suspend fun decryptTransaction(
|
||||
transactionData: ByteArray,
|
||||
minedHeight: BlockHeight,
|
||||
backend: Backend,
|
||||
backend: TypesafeBackend,
|
||||
) {
|
||||
runCatching {
|
||||
backend.decryptAndStoreTransaction(transactionData)
|
||||
|
@ -1119,27 +1107,22 @@ class CompactBlockProcessor internal constructor(
|
|||
internal suspend fun getLastDownloadedHeight(downloader: CompactBlockDownloader) =
|
||||
downloader.getLastDownloadedHeight()
|
||||
|
||||
// CompactBlockProcessor is the wrong place for this, but it's where all the other APIs that need
|
||||
// access to the RustBackend live. This should be refactored.
|
||||
internal suspend fun createAccount(rustBackend: Backend, seed: ByteArray): UnifiedSpendingKey =
|
||||
rustBackend.createAccountAndGetSpendingKey(seed)
|
||||
|
||||
/**
|
||||
* Get the current unified address for the given wallet account.
|
||||
*
|
||||
* @return the current unified address of this account.
|
||||
*/
|
||||
internal suspend fun getCurrentAddress(rustBackend: Backend, account: Account) =
|
||||
rustBackend.getCurrentAddress(account)
|
||||
internal suspend fun getCurrentAddress(backend: TypesafeBackend, account: Account) =
|
||||
backend.getCurrentAddress(account)
|
||||
|
||||
/**
|
||||
* Get the legacy Sapling address corresponding to the current unified address for the given wallet account.
|
||||
*
|
||||
* @return a Sapling address.
|
||||
*/
|
||||
internal suspend fun getLegacySaplingAddress(rustBackend: Backend, account: Account) =
|
||||
rustBackend.getSaplingReceiver(
|
||||
rustBackend.getCurrentAddress(account)
|
||||
internal suspend fun getLegacySaplingAddress(backend: TypesafeBackend, account: Account) =
|
||||
backend.getSaplingReceiver(
|
||||
backend.getCurrentAddress(account)
|
||||
)
|
||||
?: throw InitializeException.MissingAddressException("legacy Sapling")
|
||||
|
||||
|
@ -1148,9 +1131,9 @@ class CompactBlockProcessor internal constructor(
|
|||
*
|
||||
* @return a transparent address.
|
||||
*/
|
||||
internal suspend fun getTransparentAddress(rustBackend: Backend, account: Account) =
|
||||
rustBackend.getTransparentReceiver(
|
||||
rustBackend.getCurrentAddress(account)
|
||||
internal suspend fun getTransparentAddress(backend: TypesafeBackend, account: Account) =
|
||||
backend.getTransparentReceiver(
|
||||
backend.getCurrentAddress(account)
|
||||
)
|
||||
?: throw InitializeException.MissingAddressException("legacy transparent")
|
||||
}
|
||||
|
|
|
@ -1,143 +0,0 @@
|
|||
@file:Suppress("TooManyFunctions")
|
||||
|
||||
package cash.z.ecc.android.sdk.internal
|
||||
|
||||
import cash.z.ecc.android.sdk.internal.model.Checkpoint
|
||||
import cash.z.ecc.android.sdk.internal.model.JniBlockMeta
|
||||
import cash.z.ecc.android.sdk.model.Account
|
||||
import cash.z.ecc.android.sdk.model.BlockHeight
|
||||
import cash.z.ecc.android.sdk.model.UnifiedFullViewingKey
|
||||
import cash.z.ecc.android.sdk.model.UnifiedSpendingKey
|
||||
import cash.z.ecc.android.sdk.model.WalletBalance
|
||||
import cash.z.ecc.android.sdk.model.Zatoshi
|
||||
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
||||
import cash.z.ecc.android.sdk.tool.DerivationTool
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
internal val Backend.network: ZcashNetwork
|
||||
get() = ZcashNetwork.from(networkId)
|
||||
|
||||
internal suspend fun Backend.initAccountsTable(vararg keys: UnifiedFullViewingKey) {
|
||||
val ufvks = Array(keys.size) { keys[it].encoding }
|
||||
|
||||
@Suppress("SpreadOperator")
|
||||
return initAccountsTable(*ufvks)
|
||||
}
|
||||
|
||||
internal suspend fun Backend.initAccountsTableTypesafe(
|
||||
seed: ByteArray,
|
||||
numberOfAccounts: Int
|
||||
): List<UnifiedFullViewingKey> {
|
||||
return DerivationTool.getInstance().deriveUnifiedFullViewingKeys(seed, network, numberOfAccounts)
|
||||
}
|
||||
|
||||
internal suspend fun Backend.initBlocksTable(checkpoint: Checkpoint) = initBlocksTable(
|
||||
checkpoint.height.value,
|
||||
checkpoint.hash,
|
||||
checkpoint.epochSeconds,
|
||||
checkpoint.tree
|
||||
)
|
||||
|
||||
internal suspend fun Backend.createAccountAndGetSpendingKey(seed: ByteArray): UnifiedSpendingKey = UnifiedSpendingKey(
|
||||
createAccount(seed)
|
||||
)
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal suspend fun Backend.createToAddress(
|
||||
usk: UnifiedSpendingKey,
|
||||
to: String,
|
||||
value: Long,
|
||||
memo: ByteArray? = byteArrayOf()
|
||||
): Long = createToAddress(
|
||||
usk.account.value,
|
||||
usk.copyBytes(),
|
||||
to,
|
||||
value,
|
||||
memo
|
||||
)
|
||||
|
||||
internal suspend fun Backend.shieldToAddress(
|
||||
usk: UnifiedSpendingKey,
|
||||
memo: ByteArray? = byteArrayOf()
|
||||
): Long = shieldToAddress(
|
||||
usk.account.value,
|
||||
usk.copyBytes(),
|
||||
memo
|
||||
)
|
||||
|
||||
internal suspend fun Backend.getCurrentAddress(account: Account): String = getCurrentAddress(account.value)
|
||||
|
||||
internal suspend fun Backend.listTransparentReceivers(account: Account): List<String> =
|
||||
listTransparentReceivers(account.value)
|
||||
|
||||
internal suspend fun Backend.getBalance(account: Account): Zatoshi = Zatoshi(getBalance(account.value))
|
||||
|
||||
internal fun Backend.getBranchIdForHeight(height: BlockHeight): Long = getBranchIdForHeight(height.value)
|
||||
|
||||
internal suspend fun Backend.getVerifiedBalance(account: Account): Zatoshi = Zatoshi(
|
||||
getVerifiedBalance
|
||||
(account.value)
|
||||
)
|
||||
|
||||
internal suspend fun Backend.getNearestRewindHeight(height: BlockHeight): BlockHeight = BlockHeight.new(
|
||||
ZcashNetwork.from(networkId),
|
||||
getNearestRewindHeight(height.value)
|
||||
)
|
||||
|
||||
internal suspend fun Backend.rewindToHeight(height: BlockHeight) = rewindToHeight(height.value)
|
||||
|
||||
internal suspend fun Backend.getLatestBlockHeight(): BlockHeight? = getLatestHeight()?.let {
|
||||
BlockHeight.new(
|
||||
ZcashNetwork.from(networkId),
|
||||
it
|
||||
)
|
||||
}
|
||||
|
||||
internal suspend fun Backend.findBlockMetadata(height: BlockHeight): JniBlockMeta? =
|
||||
findBlockMetadata(height.value)
|
||||
|
||||
internal suspend fun Backend.rewindBlockMetadataToHeight(height: BlockHeight) =
|
||||
rewindBlockMetadataToHeight(height.value)
|
||||
|
||||
/**
|
||||
* @param limit The limit provides an efficient way how to restrict the portion of blocks, which will be validated.
|
||||
* @return Null if successful. If an error occurs, the height will be the height where the error was detected.
|
||||
*/
|
||||
internal suspend fun Backend.validateCombinedChainOrErrorBlockHeight(limit: Long?): BlockHeight? =
|
||||
validateCombinedChainOrErrorHeight(limit)?.let {
|
||||
BlockHeight.new(
|
||||
ZcashNetwork.from(networkId),
|
||||
it
|
||||
)
|
||||
}
|
||||
|
||||
internal suspend fun Backend.getDownloadedUtxoBalance(address: String): WalletBalance {
|
||||
// Note this implementation is not ideal because it requires two database queries without a transaction, which makes
|
||||
// the data potentially inconsistent. However the verified amount is queried first which makes this less bad.
|
||||
val verified = withContext(SdkDispatchers.DATABASE_IO) {
|
||||
getVerifiedTransparentBalance(address)
|
||||
}
|
||||
val total = withContext(SdkDispatchers.DATABASE_IO) {
|
||||
getTotalTransparentBalance(
|
||||
address
|
||||
)
|
||||
}
|
||||
return WalletBalance(Zatoshi(total), Zatoshi(verified))
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal suspend fun Backend.putUtxo(
|
||||
tAddress: String,
|
||||
txId: ByteArray,
|
||||
index: Int,
|
||||
script: ByteArray,
|
||||
value: Long,
|
||||
height: BlockHeight
|
||||
) = putUtxo(
|
||||
tAddress,
|
||||
txId,
|
||||
index,
|
||||
script,
|
||||
value,
|
||||
height.value
|
||||
)
|
|
@ -5,12 +5,18 @@ import cash.z.ecc.android.sdk.internal.model.JniBlockMeta
|
|||
import cash.z.ecc.android.sdk.model.Account
|
||||
import cash.z.ecc.android.sdk.model.BlockHeight
|
||||
import cash.z.ecc.android.sdk.model.UnifiedFullViewingKey
|
||||
import cash.z.ecc.android.sdk.model.UnifiedSpendingKey
|
||||
import cash.z.ecc.android.sdk.model.WalletBalance
|
||||
import cash.z.ecc.android.sdk.model.Zatoshi
|
||||
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
||||
import java.lang.RuntimeException
|
||||
import kotlin.jvm.Throws
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
internal interface TypesafeBackend {
|
||||
|
||||
val network: ZcashNetwork
|
||||
|
||||
suspend fun initAccountsTable(vararg keys: UnifiedFullViewingKey)
|
||||
|
||||
suspend fun initAccountsTable(
|
||||
|
@ -20,6 +26,21 @@ internal interface TypesafeBackend {
|
|||
|
||||
suspend fun initBlocksTable(checkpoint: Checkpoint)
|
||||
|
||||
suspend fun createAccountAndGetSpendingKey(seed: ByteArray): UnifiedSpendingKey
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
suspend fun createToAddress(
|
||||
usk: UnifiedSpendingKey,
|
||||
to: String,
|
||||
value: Long,
|
||||
memo: ByteArray? = byteArrayOf()
|
||||
): Long
|
||||
|
||||
suspend fun shieldToAddress(
|
||||
usk: UnifiedSpendingKey,
|
||||
memo: ByteArray? = byteArrayOf()
|
||||
): Long
|
||||
|
||||
suspend fun getCurrentAddress(account: Account): String
|
||||
|
||||
suspend fun listTransparentReceivers(account: Account): List<String>
|
||||
|
@ -47,4 +68,46 @@ internal interface TypesafeBackend {
|
|||
suspend fun validateCombinedChainOrErrorBlockHeight(limit: Long?): BlockHeight?
|
||||
|
||||
suspend fun getDownloadedUtxoBalance(address: String): WalletBalance
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
suspend fun putUtxo(
|
||||
tAddress: String,
|
||||
txId: ByteArray,
|
||||
index: Int,
|
||||
script: ByteArray,
|
||||
value: Long,
|
||||
height: BlockHeight
|
||||
)
|
||||
|
||||
suspend fun getSentMemoAsUtf8(idNote: Long): String?
|
||||
|
||||
suspend fun getReceivedMemoAsUtf8(idNote: Long): String?
|
||||
|
||||
suspend fun initDataDb(seed: ByteArray?): Int
|
||||
|
||||
/**
|
||||
* @throws RuntimeException as a common indicator of the operation failure
|
||||
*/
|
||||
@Throws(RuntimeException::class)
|
||||
suspend fun scanBlocks(limit: Long?)
|
||||
|
||||
suspend fun decryptAndStoreTransaction(tx: ByteArray)
|
||||
|
||||
fun getSaplingReceiver(ua: String): String?
|
||||
|
||||
fun getTransparentReceiver(ua: String): String?
|
||||
|
||||
suspend fun initBlockMetaDb(): Int
|
||||
|
||||
/**
|
||||
* @throws RuntimeException as a common indicator of the operation failure
|
||||
*/
|
||||
@Throws(RuntimeException::class)
|
||||
suspend fun writeBlockMetadata(blockMetadata: List<JniBlockMeta>)
|
||||
|
||||
fun isValidShieldedAddr(addr: String): Boolean
|
||||
|
||||
fun isValidTransparentAddr(addr: String): Boolean
|
||||
|
||||
fun isValidUnifiedAddr(addr: String): Boolean
|
||||
}
|
||||
|
|
|
@ -5,51 +5,185 @@ import cash.z.ecc.android.sdk.internal.model.JniBlockMeta
|
|||
import cash.z.ecc.android.sdk.model.Account
|
||||
import cash.z.ecc.android.sdk.model.BlockHeight
|
||||
import cash.z.ecc.android.sdk.model.UnifiedFullViewingKey
|
||||
import cash.z.ecc.android.sdk.model.UnifiedSpendingKey
|
||||
import cash.z.ecc.android.sdk.model.WalletBalance
|
||||
import cash.z.ecc.android.sdk.model.Zatoshi
|
||||
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
||||
import cash.z.ecc.android.sdk.tool.DerivationTool
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
// This class is currently unused, although the goal is to swap out usages of BackendExt for this throughout the SDK.
|
||||
@Suppress("TooManyFunctions")
|
||||
internal class TypesafeBackendImpl(private val backend: Backend) : TypesafeBackend {
|
||||
override suspend fun initAccountsTable(vararg keys: UnifiedFullViewingKey) =
|
||||
backend.initAccountsTable(*keys)
|
||||
|
||||
override val network: ZcashNetwork
|
||||
get() = ZcashNetwork.from(backend.networkId)
|
||||
|
||||
override suspend fun initAccountsTable(vararg keys: UnifiedFullViewingKey) {
|
||||
val ufvks = Array(keys.size) { keys[it].encoding }
|
||||
@Suppress("SpreadOperator")
|
||||
backend.initAccountsTable(*ufvks)
|
||||
}
|
||||
|
||||
override suspend fun initAccountsTable(
|
||||
seed: ByteArray,
|
||||
numberOfAccounts: Int
|
||||
): List<UnifiedFullViewingKey> = backend.initAccountsTableTypesafe(seed, numberOfAccounts)
|
||||
): List<UnifiedFullViewingKey> {
|
||||
return DerivationTool.getInstance().deriveUnifiedFullViewingKeys(seed, network, numberOfAccounts)
|
||||
}
|
||||
|
||||
override suspend fun initBlocksTable(checkpoint: Checkpoint) = backend.initBlocksTable(checkpoint)
|
||||
override suspend fun initBlocksTable(checkpoint: Checkpoint) {
|
||||
backend.initBlocksTable(
|
||||
checkpoint.height.value,
|
||||
checkpoint.hash,
|
||||
checkpoint.epochSeconds,
|
||||
checkpoint.tree
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getCurrentAddress(account: Account): String = getCurrentAddress(account)
|
||||
override suspend fun createAccountAndGetSpendingKey(seed: ByteArray): UnifiedSpendingKey {
|
||||
return UnifiedSpendingKey(backend.createAccount(seed))
|
||||
}
|
||||
|
||||
override suspend fun listTransparentReceivers(account: Account): List<String> =
|
||||
backend.listTransparentReceivers(account)
|
||||
@Suppress("LongParameterList")
|
||||
override suspend fun createToAddress(
|
||||
usk: UnifiedSpendingKey,
|
||||
to: String,
|
||||
value: Long,
|
||||
memo: ByteArray?
|
||||
): Long = backend.createToAddress(
|
||||
usk.account.value,
|
||||
usk.copyBytes(),
|
||||
to,
|
||||
value,
|
||||
memo
|
||||
)
|
||||
|
||||
override suspend fun getBalance(account: Account): Zatoshi = backend.getBalance(account)
|
||||
override suspend fun shieldToAddress(
|
||||
usk: UnifiedSpendingKey,
|
||||
memo: ByteArray?
|
||||
): Long = backend.shieldToAddress(
|
||||
usk.account.value,
|
||||
usk.copyBytes(),
|
||||
memo
|
||||
)
|
||||
|
||||
override fun getBranchIdForHeight(height: BlockHeight): Long = backend.getBranchIdForHeight(height.value)
|
||||
override suspend fun getCurrentAddress(account: Account): String {
|
||||
return backend.getCurrentAddress(account.value)
|
||||
}
|
||||
|
||||
override suspend fun getVerifiedBalance(account: Account): Zatoshi = backend.getVerifiedBalance(account)
|
||||
override suspend fun listTransparentReceivers(account: Account): List<String> {
|
||||
return backend.listTransparentReceivers(account.value)
|
||||
}
|
||||
|
||||
override suspend fun getNearestRewindHeight(height: BlockHeight): BlockHeight =
|
||||
backend.getNearestRewindHeight(height)
|
||||
override suspend fun getBalance(account: Account): Zatoshi {
|
||||
return Zatoshi(backend.getBalance(account.value))
|
||||
}
|
||||
|
||||
override suspend fun rewindToHeight(height: BlockHeight) = backend.rewindToHeight(height)
|
||||
override fun getBranchIdForHeight(height: BlockHeight): Long {
|
||||
return backend.getBranchIdForHeight(height.value)
|
||||
}
|
||||
|
||||
override suspend fun getLatestBlockHeight(): BlockHeight? = backend.getLatestBlockHeight()
|
||||
override suspend fun getVerifiedBalance(account: Account): Zatoshi {
|
||||
return Zatoshi(backend.getVerifiedBalance(account.value))
|
||||
}
|
||||
|
||||
override suspend fun findBlockMetadata(height: BlockHeight): JniBlockMeta? = backend.findBlockMetadata(height)
|
||||
override suspend fun getNearestRewindHeight(height: BlockHeight): BlockHeight {
|
||||
return BlockHeight.new(
|
||||
ZcashNetwork.from(backend.networkId),
|
||||
backend.getNearestRewindHeight(height.value)
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun rewindBlockMetadataToHeight(height: BlockHeight) = backend.rewindBlockMetadataToHeight(height)
|
||||
override suspend fun rewindToHeight(height: BlockHeight) {
|
||||
backend.rewindToHeight(height.value)
|
||||
}
|
||||
|
||||
override suspend fun getLatestBlockHeight(): BlockHeight? {
|
||||
return backend.getLatestHeight()?.let {
|
||||
BlockHeight.new(
|
||||
ZcashNetwork.from(backend.networkId),
|
||||
it
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun findBlockMetadata(height: BlockHeight): JniBlockMeta? {
|
||||
return backend.findBlockMetadata(height.value)
|
||||
}
|
||||
|
||||
override suspend fun rewindBlockMetadataToHeight(height: BlockHeight) {
|
||||
backend.rewindBlockMetadataToHeight(height.value)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param limit The limit provides an efficient way how to restrict the portion of blocks, which will be validated.
|
||||
* @return Null if successful. If an error occurs, the height will be the height where the error was detected.
|
||||
*/
|
||||
override suspend fun validateCombinedChainOrErrorBlockHeight(limit: Long?): BlockHeight? =
|
||||
backend.validateCombinedChainOrErrorBlockHeight(limit)
|
||||
override suspend fun validateCombinedChainOrErrorBlockHeight(limit: Long?): BlockHeight? {
|
||||
return backend.validateCombinedChainOrErrorHeight(limit)?.let {
|
||||
BlockHeight.new(
|
||||
ZcashNetwork.from(backend.networkId),
|
||||
it
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getDownloadedUtxoBalance(address: String): WalletBalance =
|
||||
backend.getDownloadedUtxoBalance(address)
|
||||
override suspend fun getDownloadedUtxoBalance(address: String): WalletBalance {
|
||||
// Note this implementation is not ideal because it requires two database queries without a transaction, which
|
||||
// makes the data potentially inconsistent. However the verified amount is queried first which makes this less
|
||||
// bad.
|
||||
val verified = withContext(SdkDispatchers.DATABASE_IO) {
|
||||
backend.getVerifiedTransparentBalance(address)
|
||||
}
|
||||
val total = withContext(SdkDispatchers.DATABASE_IO) {
|
||||
backend.getTotalTransparentBalance(
|
||||
address
|
||||
)
|
||||
}
|
||||
return WalletBalance(Zatoshi(total), Zatoshi(verified))
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
override suspend fun putUtxo(
|
||||
tAddress: String,
|
||||
txId: ByteArray,
|
||||
index: Int,
|
||||
script: ByteArray,
|
||||
value: Long,
|
||||
height: BlockHeight
|
||||
) {
|
||||
return backend.putUtxo(
|
||||
tAddress,
|
||||
txId,
|
||||
index,
|
||||
script,
|
||||
value,
|
||||
height.value
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getSentMemoAsUtf8(idNote: Long) = backend.getSentMemoAsUtf8(idNote)
|
||||
|
||||
override suspend fun getReceivedMemoAsUtf8(idNote: Long): String? = backend.getReceivedMemoAsUtf8(idNote)
|
||||
|
||||
override suspend fun initDataDb(seed: ByteArray?): Int = backend.initDataDb(seed)
|
||||
|
||||
override suspend fun scanBlocks(limit: Long?) = backend.scanBlocks(limit)
|
||||
|
||||
override suspend fun decryptAndStoreTransaction(tx: ByteArray) = backend.decryptAndStoreTransaction(tx)
|
||||
|
||||
override fun getSaplingReceiver(ua: String): String? = backend.getSaplingReceiver(ua)
|
||||
|
||||
override fun getTransparentReceiver(ua: String): String? = backend.getTransparentReceiver(ua)
|
||||
|
||||
override suspend fun initBlockMetaDb(): Int = backend.initBlockMetaDb()
|
||||
|
||||
override suspend fun writeBlockMetadata(blockMetadata: List<JniBlockMeta>) =
|
||||
backend.writeBlockMetadata(blockMetadata)
|
||||
|
||||
override fun isValidShieldedAddr(addr: String): Boolean = backend.isValidShieldedAddr(addr)
|
||||
|
||||
override fun isValidTransparentAddr(addr: String): Boolean = backend.isValidTransparentAddr(addr)
|
||||
|
||||
override fun isValidUnifiedAddr(addr: String): Boolean = backend.isValidUnifiedAddr(addr)
|
||||
}
|
||||
|
|
|
@ -2,13 +2,11 @@ package cash.z.ecc.android.sdk.internal.db.derived
|
|||
|
||||
import android.content.Context
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import cash.z.ecc.android.sdk.internal.Backend
|
||||
import cash.z.ecc.android.sdk.internal.NoBackupContextWrapper
|
||||
import cash.z.ecc.android.sdk.internal.Twig
|
||||
import cash.z.ecc.android.sdk.internal.TypesafeBackend
|
||||
import cash.z.ecc.android.sdk.internal.db.ReadOnlySupportSqliteOpenHelper
|
||||
import cash.z.ecc.android.sdk.internal.ext.tryWarn
|
||||
import cash.z.ecc.android.sdk.internal.initAccountsTable
|
||||
import cash.z.ecc.android.sdk.internal.initBlocksTable
|
||||
import cash.z.ecc.android.sdk.internal.model.Checkpoint
|
||||
import cash.z.ecc.android.sdk.model.UnifiedFullViewingKey
|
||||
import cash.z.ecc.android.sdk.model.ZcashNetwork
|
||||
|
@ -44,7 +42,7 @@ internal class DerivedDataDb private constructor(
|
|||
@Suppress("LongParameterList", "SpreadOperator")
|
||||
suspend fun new(
|
||||
context: Context,
|
||||
backend: Backend,
|
||||
backend: TypesafeBackend,
|
||||
databaseFile: File,
|
||||
zcashNetwork: ZcashNetwork,
|
||||
checkpoint: Checkpoint,
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package cash.z.ecc.android.sdk.internal.storage.block
|
||||
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import cash.z.ecc.android.sdk.internal.Backend
|
||||
import cash.z.ecc.android.sdk.internal.Twig
|
||||
import cash.z.ecc.android.sdk.internal.TypesafeBackend
|
||||
import cash.z.ecc.android.sdk.internal.ext.createNewFileSuspend
|
||||
import cash.z.ecc.android.sdk.internal.ext.deleteRecursivelySuspend
|
||||
import cash.z.ecc.android.sdk.internal.ext.deleteSuspend
|
||||
|
@ -13,11 +13,8 @@ import cash.z.ecc.android.sdk.internal.ext.mkdirsSuspend
|
|||
import cash.z.ecc.android.sdk.internal.ext.renameToSuspend
|
||||
import cash.z.ecc.android.sdk.internal.ext.toHexReversed
|
||||
import cash.z.ecc.android.sdk.internal.ext.writeBytesSuspend
|
||||
import cash.z.ecc.android.sdk.internal.findBlockMetadata
|
||||
import cash.z.ecc.android.sdk.internal.getLatestBlockHeight
|
||||
import cash.z.ecc.android.sdk.internal.model.JniBlockMeta
|
||||
import cash.z.ecc.android.sdk.internal.repository.CompactBlockRepository
|
||||
import cash.z.ecc.android.sdk.internal.rewindBlockMetadataToHeight
|
||||
import cash.z.ecc.android.sdk.model.BlockHeight
|
||||
import co.electriccoin.lightwallet.client.model.CompactBlockUnsafe
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
@ -25,7 +22,7 @@ import java.io.File
|
|||
|
||||
internal class FileCompactBlockRepository(
|
||||
private val blocksDirectory: File,
|
||||
private val backend: Backend
|
||||
private val backend: TypesafeBackend
|
||||
) : CompactBlockRepository {
|
||||
|
||||
override suspend fun getLatestHeight() = backend.getLatestBlockHeight()
|
||||
|
@ -139,7 +136,7 @@ internal class FileCompactBlockRepository(
|
|||
*/
|
||||
suspend fun new(
|
||||
blockCacheRoot: File,
|
||||
rustBackend: Backend
|
||||
backend: TypesafeBackend
|
||||
): FileCompactBlockRepository {
|
||||
// create and check cache directories
|
||||
val blocksDirectory = File(blockCacheRoot, BLOCKS_DOWNLOAD_DIRECTORY).also {
|
||||
|
@ -149,9 +146,9 @@ internal class FileCompactBlockRepository(
|
|||
error("${blocksDirectory.path} directory does not exist and could not be created.")
|
||||
}
|
||||
|
||||
rustBackend.initBlockMetaDb()
|
||||
backend.initBlockMetaDb()
|
||||
|
||||
return FileCompactBlockRepository(blocksDirectory, rustBackend)
|
||||
return FileCompactBlockRepository(blocksDirectory, backend)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,15 +2,11 @@ package cash.z.ecc.android.sdk.internal.transaction
|
|||
|
||||
import cash.z.ecc.android.sdk.exception.TransactionEncoderException
|
||||
import cash.z.ecc.android.sdk.ext.masked
|
||||
import cash.z.ecc.android.sdk.internal.Backend
|
||||
import cash.z.ecc.android.sdk.internal.SaplingParamTool
|
||||
import cash.z.ecc.android.sdk.internal.Twig
|
||||
import cash.z.ecc.android.sdk.internal.createToAddress
|
||||
import cash.z.ecc.android.sdk.internal.getBranchIdForHeight
|
||||
import cash.z.ecc.android.sdk.internal.TypesafeBackend
|
||||
import cash.z.ecc.android.sdk.internal.model.EncodedTransaction
|
||||
import cash.z.ecc.android.sdk.internal.network
|
||||
import cash.z.ecc.android.sdk.internal.repository.DerivedDataRepository
|
||||
import cash.z.ecc.android.sdk.internal.shieldToAddress
|
||||
import cash.z.ecc.android.sdk.model.TransactionRecipient
|
||||
import cash.z.ecc.android.sdk.model.UnifiedSpendingKey
|
||||
import cash.z.ecc.android.sdk.model.Zatoshi
|
||||
|
@ -25,7 +21,7 @@ import cash.z.ecc.android.sdk.model.Zatoshi
|
|||
* such as the raw bytes and raw txId.
|
||||
*/
|
||||
internal class TransactionEncoderImpl(
|
||||
private val backend: Backend,
|
||||
private val backend: TypesafeBackend,
|
||||
private val saplingParamTool: SaplingParamTool,
|
||||
private val repository: DerivedDataRepository
|
||||
) : TransactionEncoder {
|
||||
|
|
Loading…
Reference in New Issue