demo: autosave wallet state

This commit is contained in:
Oscar Mira 2023-04-26 23:50:20 +02:00
parent 529184abaf
commit 5cf5c71b90
7 changed files with 68 additions and 47 deletions

View File

@ -20,13 +20,23 @@ class MoneroSdkClient(
val provider = providerDeferred.await() val provider = providerDeferred.await()
return withContext(ioDispatcher) { return withContext(ioDispatcher) {
val wallet = provider.createNewWallet(moneroNetwork) val wallet = provider.createNewWallet(moneroNetwork)
walletDataFileStorage.tryWriteData(wallet.publicAddress, false) { output -> saveToFile(wallet, provider, false)
provider.saveWallet(wallet, output)
}
wallet wallet
} }
} }
suspend fun saveWallet(wallet: MoneroWallet) {
withContext(ioDispatcher) {
saveToFile(wallet, providerDeferred.await(), true)
}
}
private fun saveToFile(wallet: MoneroWallet, provider: WalletProvider, canOverwrite: Boolean) {
walletDataFileStorage.tryWriteData(wallet.publicAddress, canOverwrite) { output ->
provider.saveWallet(wallet, output)
}
}
suspend fun openWallet( suspend fun openWallet(
publicAddress: String, publicAddress: String,
remoteNodes: Flow<List<RemoteNode>>, remoteNodes: Flow<List<RemoteNode>>,

View File

@ -58,7 +58,7 @@ class WalletRepository(
moneroNetwork: MoneroNetwork, moneroNetwork: MoneroNetwork,
name: String, name: String,
remoteNodeIds: List<Long>, remoteNodeIds: List<Long>,
): Pair<Long, IWallet> { ): Pair<Long, MoneroWallet> {
val wallet = moneroSdkClient.createWallet(moneroNetwork) val wallet = moneroSdkClient.createWallet(moneroNetwork)
val walletId = walletDataSource.createWalletConfig( val walletId = walletDataSource.createWalletConfig(
publicAddress = wallet.publicAddress, publicAddress = wallet.publicAddress,
@ -68,6 +68,9 @@ class WalletRepository(
return walletId to wallet return walletId to wallet
} }
suspend fun saveWallet(wallet: MoneroWallet) =
moneroSdkClient.saveWallet(wallet)
suspend fun updateWalletConfig(walletConfig: WalletConfig) = suspend fun updateWalletConfig(walletConfig: WalletConfig) =
walletDataSource.updateWalletConfig(walletConfig) walletDataSource.updateWalletConfig(walletConfig)
} }

View File

@ -9,16 +9,39 @@ import androidx.lifecycle.LifecycleService
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import im.molly.monero.demo.AppModule import im.molly.monero.demo.AppModule
import im.molly.monero.demo.data.WalletRepository import im.molly.monero.demo.data.WalletRepository
import kotlinx.coroutines.delay import kotlinx.coroutines.*
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds
const val TAG = "SyncService" const val TAG = "SyncService"
class SyncService( class SyncService(
private val walletRepository: WalletRepository = AppModule.walletRepository, private val walletRepository: WalletRepository = AppModule.walletRepository,
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO,
) : LifecycleService() { ) : LifecycleService() {
private suspend fun doSync() = coroutineScope {
val syncedWalletIds = mutableSetOf<Long>()
walletRepository.getWalletIdList().collect {
val idSet = it.toSet()
val toSync = idSet subtract syncedWalletIds
toSync.forEach { walletId ->
val wallet = walletRepository.getWallet(walletId)
launch {
while (isActive) {
val result = wallet.awaitRefresh()
if (result.isError()) {
break
}
walletRepository.saveWallet(wallet)
delay(10.seconds)
}
}
}
syncedWalletIds.addAll(toSync)
}
}
private val binder: IBinder = LocalBinder() private val binder: IBinder = LocalBinder()
inner class LocalBinder : Binder() { inner class LocalBinder : Binder() {
@ -41,25 +64,8 @@ class SyncService(
Log.d(TAG, "onCreate") Log.d(TAG, "onCreate")
super.onCreate() super.onCreate()
lifecycleScope.launch { lifecycleScope.launch(ioDispatcher) {
val syncedWalletIds = mutableSetOf<Long>() doSync()
walletRepository.getWalletIdList().collect {
val idSet = it.toSet()
val toSync = idSet subtract syncedWalletIds
toSync.forEach { walletId ->
val wallet = walletRepository.getWallet(walletId)
lifecycleScope.launch {
while (isActive) {
val result = wallet.awaitRefresh()
if (result.isError()) {
break
}
delay(10.seconds)
}
}
}
syncedWalletIds.addAll(toSync)
}
} }
} }

View File

@ -7,7 +7,7 @@ interface IWallet {
String getPrimaryAccountAddress(); String getPrimaryAccountAddress();
void addBalanceListener(in IBalanceListener listener); void addBalanceListener(in IBalanceListener listener);
void removeBalanceListener(in IBalanceListener listener); void removeBalanceListener(in IBalanceListener listener);
void save(in ParcelFileDescriptor destination); oneway void save(in ParcelFileDescriptor destination);
oneway void resumeRefresh(boolean skipCoinbaseOutputs, in IRefreshCallback callback); oneway void resumeRefresh(boolean skipCoinbaseOutputs, in IRefreshCallback callback);
void cancelRefresh(); void cancelRefresh();
void setRefreshSince(long heightOrTimestamp); void setRefreshSince(long heightOrTimestamp);

View File

@ -88,6 +88,7 @@ bool Wallet::parseFrom(std::istream& input) {
return false; return false;
if (!serialization::serialize(ar, m_wallet)) if (!serialization::serialize(ar, m_wallet))
return false; return false;
m_blockchain_height = m_wallet.get_blockchain_current_height();
m_wallet.get_transfers(m_tx_outs); m_wallet.get_transfers(m_tx_outs);
m_account_ready = true; m_account_ready = true;
return true; return true;
@ -130,31 +131,30 @@ void Wallet::handleBalanceChanged(uint64_t at_block_height) {
m_wallet.get_transfers(m_tx_outs); m_wallet.get_transfers(m_tx_outs);
m_tx_outs_mutex.unlock(); m_tx_outs_mutex.unlock();
m_blockchain_height = at_block_height; m_blockchain_height = at_block_height;
JNIEnv* env = getJniEnv(); callOnRefresh(true);
m_callback.callVoidMethod(env, WalletNative_onRefresh, at_block_height, true);
} }
void Wallet::handleNewBlock(uint64_t height, bool debounce) { void Wallet::handleNewBlock(uint64_t height) {
m_blockchain_height = height; m_blockchain_height = height;
bool notify = false; // Notify the blockchain height once every 200 ms if the height is a multiple of 100.
if (debounce) { bool debounce = true;
// Notify the blockchain height once every 200 ms if the height is a multiple of 100. if (height % 100 == 0) {
if (height % 100 == 0) { static std::chrono::steady_clock::time_point last_time;
static std::chrono::steady_clock::time_point last_time; auto now = std::chrono::steady_clock::now();
auto now = std::chrono::steady_clock::now(); if (now - last_time >= 200.ms) {
if (now - last_time >= 200.ms) { last_time = now;
last_time = now; debounce = false;
notify = true;
}
} }
} else {
notify = true;
} }
if (notify) { if (!debounce) {
m_callback.callVoidMethod(getJniEnv(), WalletNative_onRefresh, height, false); callOnRefresh(false);
} }
} }
void Wallet::callOnRefresh(bool balance_changed) {
m_callback.callVoidMethod(getJniEnv(), WalletNative_onRefresh, m_blockchain_height, balance_changed);
}
Wallet::Status Wallet::nonReentrantRefresh(bool skip_coinbase) { Wallet::Status Wallet::nonReentrantRefresh(bool skip_coinbase) {
LOG_FATAL_IF(m_refresh_running.exchange(true), LOG_FATAL_IF(m_refresh_running.exchange(true),
"Refresh should not be called concurrently"); "Refresh should not be called concurrently");
@ -187,7 +187,7 @@ Wallet::Status Wallet::nonReentrantRefresh(bool skip_coinbase) {
} }
m_refresh_running.store(false); m_refresh_running.store(false);
// Always notify the last block height. // Always notify the last block height.
handleNewBlock(m_blockchain_height, false); callOnRefresh(false);
return ret; return ret;
} }

View File

@ -75,7 +75,9 @@ class Wallet : tools::i_wallet2_callback {
auto suspendRefreshAndRunLocked(T block) -> decltype(block()); auto suspendRefreshAndRunLocked(T block) -> decltype(block());
void handleBalanceChanged(uint64_t at_block_height); void handleBalanceChanged(uint64_t at_block_height);
void handleNewBlock(uint64_t height, bool debounce = true); void handleNewBlock(uint64_t height);
void callOnRefresh(bool balance_changed);
// Implementation of i_wallet2_callback follows. // Implementation of i_wallet2_callback follows.
private: private:

2
vendor/monero vendored

@ -1 +1 @@
Subproject commit 1bfa33ad2b4b2b4bd752a6dda74102defabba063 Subproject commit 4ba282d12182bc3c907e3e77ef1091e2e806f086