mirror of
https://github.com/mollyim/monero-wallet-sdk.git
synced 2024-10-01 03:45:36 -04:00
lib: initial support for InProcess provider
This commit is contained in:
parent
f0c425dca9
commit
6811bdf47a
@ -8,9 +8,9 @@ import kotlinx.coroutines.*
|
|||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileInputStream
|
|
||||||
import java.io.FileOutputStream
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.OutputStream
|
||||||
|
|
||||||
class MoneroSdkClient(private val context: Context) {
|
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}")
|
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()
|
val output = file.startWrite()
|
||||||
try {
|
try {
|
||||||
writer(output)
|
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()
|
return file.openRead()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,5 +2,5 @@ package im.molly.monero;
|
|||||||
|
|
||||||
interface IStorageAdapter {
|
interface IStorageAdapter {
|
||||||
boolean writeAsync(in ParcelFileDescriptor pfd);
|
boolean writeAsync(in ParcelFileDescriptor pfd);
|
||||||
void readAsync(in ParcelFileDescriptor pfd);
|
oneway void readAsync(in ParcelFileDescriptor pfd);
|
||||||
}
|
}
|
||||||
|
10
lib/android/src/main/kotlin/im/molly/monero/Binder.kt
Normal file
10
lib/android/src/main/kotlin/im/molly/monero/Binder.kt
Normal 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
|
||||||
|
}
|
@ -17,30 +17,28 @@ internal class StorageAdapter(var dataStore: WalletDataStore?) : IStorageAdapter
|
|||||||
override fun writeAsync(pfd: ParcelFileDescriptor?): Boolean {
|
override fun writeAsync(pfd: ParcelFileDescriptor?): Boolean {
|
||||||
requireNotNull(pfd)
|
requireNotNull(pfd)
|
||||||
val localDataStore = dataStore
|
val localDataStore = dataStore
|
||||||
if (localDataStore == null) {
|
return if (localDataStore != null) {
|
||||||
pfd.close()
|
val inputStream = ParcelFileDescriptor.AutoCloseInputStream(pfd)
|
||||||
return false
|
ioStorageScope.launch {
|
||||||
}
|
mutex.withLock {
|
||||||
val inputStream = ParcelFileDescriptor.AutoCloseInputStream(pfd)
|
localDataStore.write { output ->
|
||||||
ioStorageScope.launch {
|
inputStream.copyTo(output)
|
||||||
mutex.withLock {
|
}
|
||||||
localDataStore.write { output ->
|
|
||||||
inputStream.copyTo(output)
|
|
||||||
}
|
}
|
||||||
}
|
}.invokeOnCompletion { inputStream.close() }
|
||||||
}.invokeOnCompletion { inputStream.close() }
|
true
|
||||||
return true
|
} else {
|
||||||
|
pfd.close()
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun readAsync(pfd: ParcelFileDescriptor?) {
|
override fun readAsync(pfd: ParcelFileDescriptor?) {
|
||||||
requireNotNull(pfd)
|
requireNotNull(pfd)
|
||||||
val localDataStore = dataStore
|
|
||||||
if (localDataStore == null) {
|
|
||||||
pfd.close()
|
|
||||||
throw IllegalArgumentException("WalletDataStore cannot be null")
|
|
||||||
}
|
|
||||||
val outputStream = ParcelFileDescriptor.AutoCloseOutputStream(pfd)
|
val outputStream = ParcelFileDescriptor.AutoCloseOutputStream(pfd)
|
||||||
ioStorageScope.launch {
|
ioStorageScope.launch {
|
||||||
|
val localDataStore =
|
||||||
|
dataStore ?: throw IllegalArgumentException("WalletDataStore cannot be null")
|
||||||
mutex.withLock {
|
mutex.withLock {
|
||||||
localDataStore.read().use { input ->
|
localDataStore.read().use { input ->
|
||||||
input.copyTo(outputStream)
|
input.copyTo(outputStream)
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package im.molly.monero
|
package im.molly.monero
|
||||||
|
|
||||||
import java.io.FileInputStream
|
import java.io.InputStream
|
||||||
import java.io.FileOutputStream
|
import java.io.OutputStream
|
||||||
|
|
||||||
interface WalletDataStore {
|
interface WalletDataStore {
|
||||||
suspend fun write(writer: (FileOutputStream) -> Unit)
|
suspend fun write(writer: (OutputStream) -> Unit)
|
||||||
suspend fun read(): FileInputStream
|
suspend fun read(): InputStream
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ import kotlin.coroutines.CoroutineContext
|
|||||||
|
|
||||||
class WalletNative private constructor(
|
class WalletNative private constructor(
|
||||||
networkId: Int,
|
networkId: Int,
|
||||||
private val storageAdapter: IStorageAdapter?,
|
private val storageAdapter: IStorageAdapter,
|
||||||
private val remoteNodeClient: IRemoteNodeClient?,
|
private val remoteNodeClient: IRemoteNodeClient?,
|
||||||
private val scope: CoroutineScope,
|
private val scope: CoroutineScope,
|
||||||
private val ioDispatcher: CoroutineDispatcher,
|
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
|
// TODO: Find better name because this is a local synchronization wallet, not a full node wallet
|
||||||
suspend fun fullNode(
|
suspend fun fullNode(
|
||||||
networkId: Int,
|
networkId: Int,
|
||||||
storageAdapter: IStorageAdapter? = null,
|
storageAdapter: IStorageAdapter,
|
||||||
remoteNodeClient: IRemoteNodeClient? = null,
|
remoteNodeClient: IRemoteNodeClient? = null,
|
||||||
secretSpendKey: SecretKey? = null,
|
secretSpendKey: SecretKey? = null,
|
||||||
restorePoint: Long? = null,
|
restorePoint: Long? = null,
|
||||||
@ -59,13 +59,17 @@ class WalletNative private constructor(
|
|||||||
private val handle: Long = nativeCreate(networkId)
|
private val handle: Long = nativeCreate(networkId)
|
||||||
|
|
||||||
private suspend fun tryWriteState(): Boolean {
|
private suspend fun tryWriteState(): Boolean {
|
||||||
requireNotNull(storageAdapter)
|
|
||||||
return withContext(ioDispatcher) {
|
return withContext(ioDispatcher) {
|
||||||
val pipe = ParcelFileDescriptor.createReliablePipe()
|
val pipe = ParcelFileDescriptor.createPipe()
|
||||||
pipe[1].use { writeSide ->
|
val readFd = pipe[0]
|
||||||
val storageIsReady = storageAdapter.writeAsync(pipe[0])
|
val writeFd = pipe[1]
|
||||||
|
val storageIsReady = storageAdapter.writeAsync(readFd)
|
||||||
|
if (storageAdapter.isRemote()) {
|
||||||
|
readFd.close()
|
||||||
|
}
|
||||||
|
writeFd.use {
|
||||||
if (storageIsReady) {
|
if (storageIsReady) {
|
||||||
val result = nativeSave(handle, writeSide.fd)
|
val result = nativeSave(handle, it.fd)
|
||||||
if (!result) {
|
if (!result) {
|
||||||
logger.e("Wallet data serialization failed")
|
logger.e("Wallet data serialization failed")
|
||||||
}
|
}
|
||||||
@ -79,15 +83,18 @@ class WalletNative private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun readState() {
|
private suspend fun readState() {
|
||||||
requireNotNull(storageAdapter)
|
withContext(ioDispatcher) {
|
||||||
return withContext(ioDispatcher) {
|
val pipe = ParcelFileDescriptor.createPipe()
|
||||||
val pipe = ParcelFileDescriptor.createReliablePipe()
|
val readFd = pipe[0]
|
||||||
pipe[0].use { readSide ->
|
val writeFd = pipe[1]
|
||||||
pipe[1].use { writeSide ->
|
storageAdapter.readAsync(writeFd)
|
||||||
storageAdapter.readAsync(writeSide)
|
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" }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,6 +78,7 @@ internal class WalletServiceImpl(
|
|||||||
callback: IWalletServiceCallbacks?,
|
callback: IWalletServiceCallbacks?,
|
||||||
) {
|
) {
|
||||||
requireNotNull(config)
|
requireNotNull(config)
|
||||||
|
requireNotNull(storage)
|
||||||
serviceScope.launch {
|
serviceScope.launch {
|
||||||
val wallet = WalletNative.fullNode(
|
val wallet = WalletNative.fullNode(
|
||||||
networkId = config.networkId,
|
networkId = config.networkId,
|
||||||
@ -97,6 +98,7 @@ internal class WalletServiceImpl(
|
|||||||
restorePoint: Long? = null,
|
restorePoint: Long? = null,
|
||||||
): IWallet {
|
): IWallet {
|
||||||
requireNotNull(config)
|
requireNotNull(config)
|
||||||
|
requireNotNull(storage)
|
||||||
requireNotNull(secretSpendKey)
|
requireNotNull(secretSpendKey)
|
||||||
return WalletNative.fullNode(
|
return WalletNative.fullNode(
|
||||||
networkId = config.networkId,
|
networkId = config.networkId,
|
||||||
|
Loading…
Reference in New Issue
Block a user