lib: initial support for InProcess provider

This commit is contained in:
Oscar Mira 2023-05-29 01:51:16 +02:00
parent f0c425dca9
commit 6811bdf47a
7 changed files with 58 additions and 41 deletions

View File

@ -8,9 +8,9 @@ import kotlinx.coroutines.*
import kotlinx.coroutines.flow.Flow
import okhttp3.OkHttpClient
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
class MoneroSdkClient(private val context: Context) {
@ -86,7 +86,7 @@ class MoneroSdkClient(private val context: Context) {
throw IOException("Cannot create wallet data directory: ${walletDataDir.path}")
}
override suspend fun write(writer: (FileOutputStream) -> Unit) {
override suspend fun write(writer: (OutputStream) -> Unit) {
val output = file.startWrite()
try {
writer(output)
@ -97,7 +97,7 @@ class MoneroSdkClient(private val context: Context) {
}
}
override suspend fun read(): FileInputStream {
override suspend fun read(): InputStream {
return file.openRead()
}
}

View File

@ -2,5 +2,5 @@ package im.molly.monero;
interface IStorageAdapter {
boolean writeAsync(in ParcelFileDescriptor pfd);
void readAsync(in ParcelFileDescriptor pfd);
oneway void readAsync(in ParcelFileDescriptor pfd);
}

View File

@ -0,0 +1,10 @@
package im.molly.monero
import android.os.IInterface
/**
* Returns whether this interface is in a remote process.
*/
fun IInterface.isRemote(): Boolean {
return asBinder() !== this
}

View File

@ -17,30 +17,28 @@ internal class StorageAdapter(var dataStore: WalletDataStore?) : IStorageAdapter
override fun writeAsync(pfd: ParcelFileDescriptor?): Boolean {
requireNotNull(pfd)
val localDataStore = dataStore
if (localDataStore == null) {
pfd.close()
return false
}
val inputStream = ParcelFileDescriptor.AutoCloseInputStream(pfd)
ioStorageScope.launch {
mutex.withLock {
localDataStore.write { output ->
inputStream.copyTo(output)
return if (localDataStore != null) {
val inputStream = ParcelFileDescriptor.AutoCloseInputStream(pfd)
ioStorageScope.launch {
mutex.withLock {
localDataStore.write { output ->
inputStream.copyTo(output)
}
}
}
}.invokeOnCompletion { inputStream.close() }
return true
}.invokeOnCompletion { inputStream.close() }
true
} else {
pfd.close()
false
}
}
override fun readAsync(pfd: ParcelFileDescriptor?) {
requireNotNull(pfd)
val localDataStore = dataStore
if (localDataStore == null) {
pfd.close()
throw IllegalArgumentException("WalletDataStore cannot be null")
}
val outputStream = ParcelFileDescriptor.AutoCloseOutputStream(pfd)
ioStorageScope.launch {
val localDataStore =
dataStore ?: throw IllegalArgumentException("WalletDataStore cannot be null")
mutex.withLock {
localDataStore.read().use { input ->
input.copyTo(outputStream)

View File

@ -1,9 +1,9 @@
package im.molly.monero
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.InputStream
import java.io.OutputStream
interface WalletDataStore {
suspend fun write(writer: (FileOutputStream) -> Unit)
suspend fun read(): FileInputStream
suspend fun write(writer: (OutputStream) -> Unit)
suspend fun read(): InputStream
}

View File

@ -11,7 +11,7 @@ import kotlin.coroutines.CoroutineContext
class WalletNative private constructor(
networkId: Int,
private val storageAdapter: IStorageAdapter?,
private val storageAdapter: IStorageAdapter,
private val remoteNodeClient: IRemoteNodeClient?,
private val scope: CoroutineScope,
private val ioDispatcher: CoroutineDispatcher,
@ -21,7 +21,7 @@ class WalletNative private constructor(
// TODO: Find better name because this is a local synchronization wallet, not a full node wallet
suspend fun fullNode(
networkId: Int,
storageAdapter: IStorageAdapter? = null,
storageAdapter: IStorageAdapter,
remoteNodeClient: IRemoteNodeClient? = null,
secretSpendKey: SecretKey? = null,
restorePoint: Long? = null,
@ -59,13 +59,17 @@ class WalletNative private constructor(
private val handle: Long = nativeCreate(networkId)
private suspend fun tryWriteState(): Boolean {
requireNotNull(storageAdapter)
return withContext(ioDispatcher) {
val pipe = ParcelFileDescriptor.createReliablePipe()
pipe[1].use { writeSide ->
val storageIsReady = storageAdapter.writeAsync(pipe[0])
val pipe = ParcelFileDescriptor.createPipe()
val readFd = pipe[0]
val writeFd = pipe[1]
val storageIsReady = storageAdapter.writeAsync(readFd)
if (storageAdapter.isRemote()) {
readFd.close()
}
writeFd.use {
if (storageIsReady) {
val result = nativeSave(handle, writeSide.fd)
val result = nativeSave(handle, it.fd)
if (!result) {
logger.e("Wallet data serialization failed")
}
@ -79,15 +83,18 @@ class WalletNative private constructor(
}
private suspend fun readState() {
requireNotNull(storageAdapter)
return withContext(ioDispatcher) {
val pipe = ParcelFileDescriptor.createReliablePipe()
pipe[0].use { readSide ->
pipe[1].use { writeSide ->
storageAdapter.readAsync(writeSide)
withContext(ioDispatcher) {
val pipe = ParcelFileDescriptor.createPipe()
val readFd = pipe[0]
val writeFd = pipe[1]
storageAdapter.readAsync(writeFd)
if (storageAdapter.isRemote()) {
writeFd.close()
}
readFd.use {
if (!nativeLoad(handle, it.fd)) {
throw IllegalStateException("Wallet data deserialization failed")
}
val result = nativeLoad(handle, readSide.fd)
check(result) { "Wallet data deserialization failed" }
}
}
}

View File

@ -78,6 +78,7 @@ internal class WalletServiceImpl(
callback: IWalletServiceCallbacks?,
) {
requireNotNull(config)
requireNotNull(storage)
serviceScope.launch {
val wallet = WalletNative.fullNode(
networkId = config.networkId,
@ -97,6 +98,7 @@ internal class WalletServiceImpl(
restorePoint: Long? = null,
): IWallet {
requireNotNull(config)
requireNotNull(storage)
requireNotNull(secretSpendKey)
return WalletNative.fullNode(
networkId = config.networkId,