diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c96fb39..12d9ebab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/jni/RustBackend.kt b/backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/jni/RustBackend.kt index a8360e07..ed6dcbf5 100644 --- a/backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/jni/RustBackend.kt +++ b/backend-lib/src/main/java/cash/z/ecc/android/sdk/internal/jni/RustBackend.kt @@ -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. */ diff --git a/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/internal/storage/block/FileCompactBlockRepositoryTest.kt b/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/internal/storage/block/FileCompactBlockRepositoryTest.kt index dfa554ba..3b597419 100644 --- a/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/internal/storage/block/FileCompactBlockRepositoryTest.kt +++ b/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/internal/storage/block/FileCompactBlockRepositoryTest.kt @@ -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() diff --git a/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/jni/BranchIdTest.kt b/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/jni/BranchIdTest.kt index 310288b8..c08feb61 100644 --- a/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/jni/BranchIdTest.kt +++ b/sdk-lib/src/androidTest/java/cash/z/ecc/android/sdk/jni/BranchIdTest.kt @@ -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( diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/SdkSynchronizer.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/SdkSynchronizer.kt index bf3a5f97..4dad23f1 100644 --- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/SdkSynchronizer.kt +++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/SdkSynchronizer.kt @@ -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 diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/block/CompactBlockProcessor.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/block/CompactBlockProcessor.kt index 1ab2c490..39323eb1 100644 --- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/block/CompactBlockProcessor.kt +++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/block/CompactBlockProcessor.kt @@ -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, repository: DerivedDataRepository, - rustBackend: Backend, + backend: TypesafeBackend, downloader: CompactBlockDownloader ): Flow = 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") } diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/BackendExt.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/BackendExt.kt deleted file mode 100644 index d88b36c0..00000000 --- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/BackendExt.kt +++ /dev/null @@ -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 { - 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 = - 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 -) diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/TypesafeBackend.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/TypesafeBackend.kt index 91effc70..97274eda 100644 --- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/TypesafeBackend.kt +++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/TypesafeBackend.kt @@ -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 @@ -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) + + fun isValidShieldedAddr(addr: String): Boolean + + fun isValidTransparentAddr(addr: String): Boolean + + fun isValidUnifiedAddr(addr: String): Boolean } diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/TypesafeBackendImpl.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/TypesafeBackendImpl.kt index bede535f..c4c572c1 100644 --- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/TypesafeBackendImpl.kt +++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/TypesafeBackendImpl.kt @@ -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 = backend.initAccountsTableTypesafe(seed, numberOfAccounts) + ): List { + 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 = - 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 { + 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) = + 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) } diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/db/derived/DerivedDataDb.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/db/derived/DerivedDataDb.kt index 71c19a5c..06656faa 100644 --- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/db/derived/DerivedDataDb.kt +++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/db/derived/DerivedDataDb.kt @@ -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, diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/storage/block/FileCompactBlockRepository.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/storage/block/FileCompactBlockRepository.kt index 373c0abc..d74441cc 100644 --- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/storage/block/FileCompactBlockRepository.kt +++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/storage/block/FileCompactBlockRepository.kt @@ -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) } } } diff --git a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/transaction/TransactionEncoderImpl.kt b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/transaction/TransactionEncoderImpl.kt index 0bd0c482..c43016a4 100644 --- a/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/transaction/TransactionEncoderImpl.kt +++ b/sdk-lib/src/main/java/cash/z/ecc/android/sdk/internal/transaction/TransactionEncoderImpl.kt @@ -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 {