From 8578699fb0fdf0ed49e0d518171ac6223ababb92 Mon Sep 17 00:00:00 2001 From: everoddandeven Date: Wed, 10 Dec 2025 20:21:40 +0100 Subject: [PATCH] add MoneroWalletLight --- src/main/cpp/monero_jni_bridge.cpp | 1404 ++++++++++++-- src/main/cpp/monero_jni_bridge.h | 212 ++- .../java/monero/wallet/MoneroWalletFull.java | 1678 +++-------------- .../java/monero/wallet/MoneroWalletJni.java | 1567 +++++++++++++++ .../java/monero/wallet/MoneroWalletLight.java | 506 +++++ src/test/java/TestMoneroWalletCommon.java | 844 +++++++-- src/test/java/TestMoneroWalletFull.java | 684 +------ src/test/java/TestMoneroWalletLight.java | 1053 +++++++++++ src/test/java/TestMoneroWalletRpc.java | 61 + src/test/java/utils/SyncProgressTester.java | 82 + src/test/java/utils/TestUtils.java | 57 +- src/test/java/utils/WalletEqualityUtils.java | 52 +- src/test/java/utils/WalletSyncTester.java | 131 ++ src/test/java/utils/WalletTxTracker.java | 31 +- 14 files changed, 5949 insertions(+), 2413 deletions(-) create mode 100644 src/main/java/monero/wallet/MoneroWalletJni.java create mode 100644 src/main/java/monero/wallet/MoneroWalletLight.java create mode 100644 src/test/java/TestMoneroWalletLight.java create mode 100644 src/test/java/utils/SyncProgressTester.java create mode 100644 src/test/java/utils/WalletSyncTester.java diff --git a/src/main/cpp/monero_jni_bridge.cpp b/src/main/cpp/monero_jni_bridge.cpp index bd620b45..f637d972 100644 --- a/src/main/cpp/monero_jni_bridge.cpp +++ b/src/main/cpp/monero_jni_bridge.cpp @@ -37,6 +37,7 @@ #include #include "monero_jni_bridge.h" #include "wallet/monero_wallet_full.h" +#include "wallet/monero_wallet_light.h" #include "utils/monero_utils.h" using namespace std; @@ -159,7 +160,7 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) { // class_ArrayList = static_cast(env->NewGlobalRef(env->FindClass("java/util/ArrayList"))); // class_TransactionInfo = static_cast(env->NewGlobalRef(env->FindClass("com/m2049r/xmrwallet/model/TransactionInfo"))); // class_Transfer = static_cast(env->NewGlobalRef(env->FindClass("com/m2049r/xmrwallet/model/Transfer"))); - class_WalletListener = static_cast(env->NewGlobalRef(env->FindClass("monero/wallet/MoneroWalletFull$WalletJniListener"))); + class_WalletListener = static_cast(env->NewGlobalRef(env->FindClass("monero/wallet/MoneroWalletJni$WalletJniListener"))); // class_Ledger = static_cast(env->NewGlobalRef(env->FindClass("com/m2049r/xmrwallet/ledger/Ledger"))); return JNI_VERSION_1_6; } @@ -415,7 +416,7 @@ JNIEXPORT void JNICALL Java_monero_common_MoneroUtils_configureLoggingJni(JNIEnv monero_utils::configure_logging(path, console); } -// --------------------------- STATIC WALLET UTILS ---------------------------- +// --------------------------- STATIC FULL WALLET UTILS ---------------------------- JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletFull_walletExistsJni(JNIEnv *env, jclass clazz, jstring jpath) { MTRACE("Java_monero_wallet_MoneroWalletFull_walletExistsJni"); @@ -517,16 +518,99 @@ JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletFull_getSeedLangua return jlanguages; } -// ------------------------ WALLET INSTANCE METHODS -------------------------- -JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletFull_isViewOnlyJni(JNIEnv *env, jobject instance) { - MTRACE("Java_monero_wallet_MoneroWalletFull_isViewOnlyJni"); +// --------------------------- STATIC LIGHT WALLET UTILS ---------------------------- + +JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletLight_walletExistsJni(JNIEnv *env, jclass clazz, jstring jprimary_address, jstring jprivate_view_key, jstring jserver_uri) { + MTRACE("Java_monero_wallet_MoneroWalletLight_walletExistsJni"); + + const char* _primary_address = env->GetStringUTFChars(jprimary_address, NULL); + string primary_address = string(_primary_address); + env->ReleaseStringUTFChars(jprimary_address, _primary_address); + + const char* _private_view_key = env->GetStringUTFChars(jprivate_view_key, NULL); + string private_view_key = string(_private_view_key); + env->ReleaseStringUTFChars(jprivate_view_key, _private_view_key); + + const char* _server_uri = env->GetStringUTFChars(jserver_uri, NULL); + string server_uri = string(_server_uri); + env->ReleaseStringUTFChars(jserver_uri, _server_uri); + + bool wallet_exists = monero_wallet_light::wallet_exists(primary_address, private_view_key, server_uri); + return static_cast(wallet_exists); +} + +JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletLight_openWalletJni(JNIEnv *env, jclass clazz, jstring jconfig) { + MTRACE("Java_monero_wallet_MoneroWalletLight_openWalletJni"); + + // get config as json string + const char* _config = jconfig ? env->GetStringUTFChars(jconfig, NULL) : nullptr; + string config_json = string(_config ? _config : ""); + env->ReleaseStringUTFChars(jconfig, _config); + + // deserialize wallet config + shared_ptr config = monero_wallet_config::deserialize(config_json); + + // load wallet from file + try { + monero_wallet* wallet = monero_wallet_light::open_wallet(*config); + return reinterpret_cast(wallet); + } catch (...) { + rethrow_cpp_exception_as_java_exception(env); + return 0; + } +} + +JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletLight_createWalletJni(JNIEnv *env, jclass clazz, jstring jconfig) { + MTRACE("Java_monero_wallet_MoneroWalletLight_createWalletJni"); + + // get config as json string + const char* _config = jconfig ? env->GetStringUTFChars(jconfig, NULL) : nullptr; + string config_json = string(_config ? _config : ""); + env->ReleaseStringUTFChars(jconfig, _config); + + // deserialize wallet config + shared_ptr config = monero_wallet_config::deserialize(config_json); + + // construct wallet + try { + monero_wallet* wallet = monero_wallet_light::create_wallet(*config); + return reinterpret_cast(wallet); + } catch (...) { + rethrow_cpp_exception_as_java_exception(env); + return 0; + } +} + +JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletLight_getSeedLanguagesJni(JNIEnv *env, jclass clazz) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getLanguagesJni"); + + // get languages + vector languages; + try { + languages = monero_wallet_light::get_seed_languages(); + } catch (...) { + rethrow_cpp_exception_as_java_exception(env); + return 0; + } + + // build java string array + jobjectArray jlanguages = env->NewObjectArray(languages.size(), env->FindClass("java/lang/String"), nullptr); + for (int i = 0; i < languages.size(); i++) { + env->SetObjectArrayElement(jlanguages, i, env->NewStringUTF(languages[i].c_str())); + } + return jlanguages; +} + + +// ------------------------ COMMON WALLET INSTANCE METHODS -------------------------- + +bool is_view_only_jni(JNIEnv *env, jobject instance) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); return wallet->is_view_only(); } -JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_setDaemonConnectionJni(JNIEnv *env, jobject instance, jstring juri, jstring jusername, jstring jpassword, jstring jproxy_uri) { - MTRACE("Java_monero_wallet_MoneroWalletFull_setDaemonConnectionJni"); +void set_daemon_connection_jni(JNIEnv *env, jobject instance, jstring juri, jstring jusername, jstring jpassword, jstring jproxy_uri) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); try { set_daemon_connection(env, wallet, juri, jusername, jpassword, jproxy_uri); @@ -535,9 +619,8 @@ JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_setDaemonConnectionJn } } -JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletFull_getDaemonConnectionJni(JNIEnv *env, jobject instance) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getDaemonConnectionJni()"); +jobjectArray get_daemon_connection_jni(JNIEnv *env, jobject instance) { // get wallet monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); @@ -559,7 +642,7 @@ JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletFull_getDaemonConn } } -JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletFull_isConnectedToDaemonJni(JNIEnv* env, jobject instance) { +jboolean is_connected_to_daemon_jni(JNIEnv* env, jobject instance) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); try { return static_cast(wallet->is_connected_to_daemon()); @@ -569,7 +652,7 @@ JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletFull_isConnectedToDaem } } -JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletFull_isDaemonSyncedJni(JNIEnv* env, jobject instance) { +jboolean is_daemon_synced_jni(JNIEnv* env, jobject instance) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); try { return wallet->is_daemon_synced(); @@ -579,7 +662,7 @@ JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletFull_isDaemonSyncedJni } } -JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletFull_isSyncedJni(JNIEnv* env, jobject instance) { +jboolean is_synced_jni(JNIEnv* env, jobject instance) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); try { return wallet->is_synced(); @@ -589,8 +672,8 @@ JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletFull_isSyncedJni(JNIEn } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getVersionJni(JNIEnv *env, jobject instance) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getVersionJni"); + +jstring get_version_jni(JNIEnv *env, jobject instance) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); try { return env->NewStringUTF(wallet->get_version().serialize().c_str()); @@ -600,20 +683,19 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getVersionJni(JNIE } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getPathJni(JNIEnv *env, jobject instance) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getPathJni"); + +jstring get_path_jni(JNIEnv *env, jobject instance) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); return env->NewStringUTF(wallet->get_path().c_str()); } -JNIEXPORT jint JNICALL Java_monero_wallet_MoneroWalletFull_getNetworkTypeJni(JNIEnv *env, jobject instance) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getNetworkTypeJni"); + +jint get_network_type_jni(JNIEnv *env, jobject instance) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); return wallet->get_network_type(); } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getSeedJni(JNIEnv *env, jobject instance) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getSeedJni"); +jstring get_seed_jni(JNIEnv *env, jobject instance) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); try { return env->NewStringUTF(wallet->get_seed().c_str()); @@ -623,8 +705,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getSeedJni(JNIEnv } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getSeedLanguageJni(JNIEnv *env, jobject instance) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getSeedLanguageJni"); +jstring get_seed_language_jni(JNIEnv *env, jobject instance) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); try { return env->NewStringUTF(wallet->get_seed_language().c_str()); @@ -634,8 +715,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getSeedLanguageJni } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getPublicViewKeyJni(JNIEnv *env, jobject instance) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getPublicViewKeyJni"); +jstring get_public_view_key_jni(JNIEnv *env, jobject instance) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); try { return env->NewStringUTF(wallet->get_public_view_key().c_str()); @@ -645,8 +725,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getPublicViewKeyJn } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getPrivateViewKeyJni(JNIEnv *env, jobject instance) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getPrivateViewKeyJni"); +jstring get_private_view_key_jni(JNIEnv *env, jobject instance) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); try { return env->NewStringUTF(wallet->get_private_view_key().c_str()); @@ -656,8 +735,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getPrivateViewKeyJ } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getPublicSpendKeyJni(JNIEnv *env, jobject instance) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getPublicSpendKeyJni"); +jstring get_public_spend_key_jni(JNIEnv *env, jobject instance) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); try { return env->NewStringUTF(wallet->get_public_spend_key().c_str()); @@ -667,8 +745,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getPublicSpendKeyJ } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getPrivateSpendKeyJni(JNIEnv *env, jobject instance) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getPrivateSpendKeyJni"); +jstring get_private_spend_key_jni(JNIEnv *env, jobject instance) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); try { return env->NewStringUTF(wallet->get_private_spend_key().c_str()); @@ -678,16 +755,14 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getPrivateSpendKey } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getAddressJni(JNIEnv *env, jobject instance, jint account_idx, jint subaddress_idx) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getAddressJni"); +jstring get_address_jni(JNIEnv *env, jobject instance, jint account_idx, jint subaddress_idx) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); string address = wallet->get_address((uint32_t) account_idx, (uint32_t) subaddress_idx); return env->NewStringUTF(address.c_str()); } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getAddressIndexJni(JNIEnv *env, jobject instance, jstring jaddress) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getAddressIndexJni"); +jstring get_address_index_jni(JNIEnv *env, jobject instance, jstring jaddress) { // collect and release string param const char* _address = jaddress ? env->GetStringUTFChars(jaddress, NULL) : nullptr; string address = string(_address ? _address : ""); @@ -709,8 +784,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getAddressIndexJni * Only one listener needs to subscribe over JNI, so this removes the previously registered listener * and registers the new listener. */ -JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletFull_setListenerJni(JNIEnv *env, jobject instance, jobject jlistener) { - MTRACE("Java_monero_wallet_MoneroWalletFull_setListenerJni"); +jlong set_listener_jni(JNIEnv *env, jobject instance, jobject jlistener) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); // remove old listener @@ -727,8 +801,7 @@ JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletFull_setListenerJni(JNIEn return reinterpret_cast(listener); } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getIntegratedAddressJni(JNIEnv *env, jobject instance, jstring jstandard_address, jstring jpayment_id) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getIntegratedAddressJni"); +jstring get_integrated_address_jni(JNIEnv *env, jobject instance, jstring jstandard_address, jstring jpayment_id) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); // collect and release string params @@ -750,8 +823,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getIntegratedAddre } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_decodeIntegratedAddressJni(JNIEnv *env, jobject instance, jstring jintegrated_address) { - MTRACE("Java_monero_wallet_MoneroWalletFull_decodeIntegratedAddressJni"); +jstring decode_integrated_address_jni(JNIEnv *env, jobject instance, jstring jintegrated_address) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); const char* _integratedAddress = jintegrated_address ? env->GetStringUTFChars(jintegrated_address, NULL) : nullptr; string integrated_address_str = string(_integratedAddress ? _integratedAddress : ""); @@ -768,14 +840,12 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_decodeIntegratedAd } } -JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletFull_getHeightJni(JNIEnv *env, jobject instance) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getHeightJni"); +jlong get_height_jni(JNIEnv *env, jobject instance) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); return wallet->get_height(); } -JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletFull_getChainHeightJni(JNIEnv *env, jobject instance) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getChainHeightJni"); +jlong get_chain_height_jni(JNIEnv *env, jobject instance) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); try { return wallet->get_daemon_height(); @@ -785,14 +855,13 @@ JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletFull_getChainHeightJni(JN } } -JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletFull_getRestoreHeightJni(JNIEnv *env, jobject instance) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getRestoreHeightJni"); +jlong get_restore_height_jni(JNIEnv *env, jobject instance) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); return wallet->get_restore_height(); } -JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_setRestoreHeightJni(JNIEnv *env, jobject instance, jlong restore_height) { - MTRACE("Java_monero_wallet_MoneroWalletFull_setRestoreHeightJni"); + +void set_restore_height_jni(JNIEnv *env, jobject instance, jlong restore_height) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); try { wallet->set_restore_height(restore_height); @@ -801,7 +870,7 @@ JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_setRestoreHeightJni(J } } -JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletFull_getDaemonHeightJni(JNIEnv* env, jobject instance) { +jlong get_daemon_height_jni(JNIEnv* env, jobject instance) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); try { return wallet->get_daemon_height(); @@ -811,7 +880,7 @@ JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletFull_getDaemonHeightJni(J } } -JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletFull_getDaemonMaxPeerHeightJni(JNIEnv* env, jobject instance) { +jlong get_daemon_max_peer_height_jni(JNIEnv* env, jobject instance) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); try { return wallet->get_daemon_max_peer_height(); @@ -821,7 +890,8 @@ JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletFull_getDaemonMaxPeerHeig } } -JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletFull_getHeightByDateJni(JNIEnv* env, jobject instance, jint year, jint month, jint day) { + +jlong get_height_by_date_jni(JNIEnv* env, jobject instance, jint year, jint month, jint day) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); try { return wallet->get_height_by_date(year, month, day); @@ -831,8 +901,7 @@ JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletFull_getHeightByDateJni(J } } -JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletFull_syncJni(JNIEnv *env, jobject instance, jlong start_height) { - MTRACE("Java_monero_wallet_MoneroWalletFull_syncJni"); +jobjectArray sync_jni(JNIEnv *env, jobject instance, jlong start_height) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); try { @@ -856,8 +925,7 @@ JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletFull_syncJni(JNIEn } } -JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_startSyncingJni(JNIEnv *env, jobject instance, jlong sync_period_in_ms) { - MTRACE("Java_monero_wallet_MoneroWalletFull_startSyncingJni"); +void start_syncing_jni(JNIEnv *env, jobject instance, jlong sync_period_in_ms) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); try { wallet->start_syncing(sync_period_in_ms); @@ -866,8 +934,7 @@ JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_startSyncingJni(JNIEn } } -JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_stopSyncingJni(JNIEnv *env, jobject instance) { - MTRACE("Java_monero_wallet_MoneroWalletFull_stopSyncingJni"); +void stop_syncing_jni(JNIEnv *env, jobject instance) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); try { wallet->stop_syncing(); @@ -876,8 +943,7 @@ JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_stopSyncingJni(JNIEnv } } -JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_scanTxsJni(JNIEnv* env, jobject instance, jobjectArray jtx_hashes) { - MTRACE("Java_monero_wallet_MoneroWalletFull_scanTxsJni"); +void scan_txs_jni(JNIEnv* env, jobject instance, jobjectArray jtx_hashes) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); // get tx hashes from jobjectArray to vector @@ -903,7 +969,7 @@ JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_scanTxsJni(JNIEnv* en } } -JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_rescanSpentJni(JNIEnv *env, jobject instance) { +void rescan_spent_jni(JNIEnv *env, jobject instance) { MTRACE("Java_monero_wallet_MoneroWalletFull_rescanSpentJni"); monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); try { @@ -913,7 +979,7 @@ JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_rescanSpentJni(JNIEnv } } -JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_rescanBlockchainJni(JNIEnv *env, jobject instance) { +void rescan_blockchain_jni(JNIEnv *env, jobject instance) { MTRACE("Java_monero_wallet_MoneroWalletFull_rescanBlockchainJni"); monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); try { @@ -923,50 +989,43 @@ JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_rescanBlockchainJni(J } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getBalanceWalletJni(JNIEnv *env, jobject instance) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getBalanceWalletJni"); +jstring get_balance_wallet_jni(JNIEnv *env, jobject instance) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); uint64_t balance = wallet->get_balance(); return env->NewStringUTF(boost::lexical_cast(balance).c_str()); } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getBalanceAccountJni(JNIEnv *env, jobject instance, jint account_idx) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getBalanceAccountJni"); +jstring get_balance_account_jni(JNIEnv *env, jobject instance, jint account_idx) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); uint64_t balance = wallet->get_balance(account_idx); return env->NewStringUTF(boost::lexical_cast(balance).c_str()); } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getBalanceSubaddressJni(JNIEnv *env, jobject instance, jint account_idx, jint subaddress_idx) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getBalanceSubaddressJni"); +jstring get_balance_subaddress_jni(JNIEnv *env, jobject instance, jint account_idx, jint subaddress_idx) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); uint64_t balance = wallet->get_balance(account_idx, subaddress_idx); return env->NewStringUTF(boost::lexical_cast(balance).c_str()); } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getUnlockedBalanceWalletJni(JNIEnv *env, jobject instance) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getUnlockedBalanceWalletJni"); +jstring get_unlocked_balance_wallet_jni(JNIEnv *env, jobject instance) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); uint64_t balance = wallet->get_unlocked_balance(); return env->NewStringUTF(boost::lexical_cast(balance).c_str()); } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getUnlockedBalanceAccountJni(JNIEnv *env, jobject instance, jint account_idx) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getUnlockedBalanceAccountJni"); +jstring get_unlocked_balance_account_jni(JNIEnv *env, jobject instance, jint account_idx) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); uint64_t balance = wallet->get_unlocked_balance(account_idx); return env->NewStringUTF(boost::lexical_cast(balance).c_str()); } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getUnlockedBalanceSubaddressJni(JNIEnv *env, jobject instance, jint account_idx, jint subaddress_idx) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getUnlockedBalanceSubaddressJni"); +jstring get_unlocked_balance_subaddress_jni(JNIEnv *env, jobject instance, jint account_idx, jint subaddress_idx) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); uint64_t balance = wallet->get_unlocked_balance(account_idx, subaddress_idx); return env->NewStringUTF(boost::lexical_cast(balance).c_str()); } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getAccountsJni(JNIEnv* env, jobject instance, jboolean include_subaddresses, jstring jtag) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getAccountsJni"); +jstring get_accounts_jni(JNIEnv* env, jobject instance, jboolean include_subaddresses, jstring jtag) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); const char* _tag = jtag ? env->GetStringUTFChars(jtag, NULL) : nullptr; string tag = string(_tag ? _tag : ""); @@ -983,8 +1042,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getAccountsJni(JNI return env->NewStringUTF(accounts_json.c_str()); } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getAccountJni(JNIEnv* env, jobject instance, jint account_idx, jboolean include_subaddresses) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getAccountJni"); +jstring get_account_jni(JNIEnv* env, jobject instance, jint account_idx, jboolean include_subaddresses) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); // get account @@ -995,8 +1053,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getAccountJni(JNIE return env->NewStringUTF(account_json.c_str()); } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_createAccountJni(JNIEnv* env, jobject instance, jstring jlabel) { - MTRACE("Java_monero_wallet_MoneroWalletFull_createAccountJni"); +jstring create_account_jni(JNIEnv* env, jobject instance, jstring jlabel) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); const char* _label = jlabel ? env->GetStringUTFChars(jlabel, NULL) : nullptr; string label = string(_label ? _label : ""); @@ -1010,8 +1067,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_createAccountJni(J return env->NewStringUTF(account_json.c_str()); } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getSubaddressesJni(JNIEnv* env, jobject instance, jint account_idx, jintArray jsubaddressIndices) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getSubaddressesJni"); +jstring get_subaddresses_jni(JNIEnv* env, jobject instance, jint account_idx, jintArray jsubaddressIndices) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); // convert subaddress indices from jintArray to vector @@ -1035,8 +1091,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getSubaddressesJni return env->NewStringUTF(subaddresses_json.c_str()); } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_createSubaddressJni(JNIEnv* env, jobject instance, jint account_idx, jstring jlabel) { - MTRACE("Java_monero_wallet_MoneroWalletFull_createSubaddressJni"); +jstring create_subaddress_jni(JNIEnv* env, jobject instance, jint account_idx, jstring jlabel) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); const char* _label = jlabel ? env->GetStringUTFChars(jlabel, NULL) : nullptr; string label = string(_label ? _label : ""); @@ -1050,8 +1105,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_createSubaddressJn return env->NewStringUTF(subaddress_json.c_str()); } -JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_setSubaddressLabelJni(JNIEnv* env, jobject instance, jint account_idx, jint subaddress_idx, jstring jlabel) { - MTRACE("Java_monero_wallet_MoneroWalletFull_setSubaddressLabelJni"); +void set_subaddress_label_jni(JNIEnv* env, jobject instance, jint account_idx, jint subaddress_idx, jstring jlabel) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); const char* _label = jlabel ? env->GetStringUTFChars(jlabel, NULL) : nullptr; string label = string(_label ? _label : ""); @@ -1063,8 +1117,7 @@ JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_setSubaddressLabelJni } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getTxsJni(JNIEnv* env, jobject instance, jstring jtx_query) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getTxsJni"); +jstring get_txs_jni(JNIEnv* env, jobject instance, jstring jtx_query) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); const char* _tx_query = jtx_query ? env->GetStringUTFChars(jtx_query, NULL) : nullptr; string tx_query_json = string(_tx_query ? _tx_query : ""); @@ -1096,8 +1149,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getTxsJni(JNIEnv* } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getTransfersJni(JNIEnv* env, jobject instance, jstring jtransfer_query) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getTransfersJni"); +jstring get_transfers_jni(JNIEnv* env, jobject instance, jstring jtransfer_query) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); const char* _transfer_query = jtransfer_query ? env->GetStringUTFChars(jtransfer_query, NULL) : nullptr; string transfer_query_json = string(_transfer_query ? _transfer_query : ""); @@ -1133,8 +1185,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getTransfersJni(JN } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getOutputsJni(JNIEnv* env, jobject instance, jstring joutput_query) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getOutputsJni"); +jstring get_outputs_jni(JNIEnv* env, jobject instance, jstring joutput_query) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); const char* _output_query = joutput_query ? env->GetStringUTFChars(joutput_query, NULL) : nullptr; string output_query_json = string(_output_query ? _output_query : ""); @@ -1166,8 +1217,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getOutputsJni(JNIE } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_exportOutputsJni(JNIEnv* env, jobject instance, jboolean all) { - MTRACE("Java_monero_wallet_MoneroWalletFull_exportOutputsJni()"); +jstring export_outputs_jni(JNIEnv* env, jobject instance, jboolean all) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); try { return env->NewStringUTF(wallet->export_outputs(all).c_str()); @@ -1177,8 +1227,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_exportOutputsJni(J } } -JNIEXPORT jint JNICALL Java_monero_wallet_MoneroWalletFull_importOutputsJni(JNIEnv* env, jobject instance, jstring joutputs_hex) { - MTRACE("Java_monero_wallet_MoneroWalletFull_exportOutputsJni()"); +jint import_outputs_jni(JNIEnv* env, jobject instance, jstring joutputs_hex) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); const char* _outputs_hex = joutputs_hex ? env->GetStringUTFChars(joutputs_hex, NULL) : nullptr; string outputs_hex = string(_outputs_hex ? _outputs_hex : ""); @@ -1191,8 +1240,7 @@ JNIEXPORT jint JNICALL Java_monero_wallet_MoneroWalletFull_importOutputsJni(JNIE } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_exportKeyImagesJni(JNIEnv* env, jobject instance, jboolean all) { - MTRACE("Java_monero_wallet_MoneroWalletFull_exportKeyImagesJni"); +jstring export_key_images_jni(JNIEnv* env, jobject instance, jboolean all) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); try { @@ -1212,8 +1260,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_exportKeyImagesJni } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_importKeyImagesJni(JNIEnv* env, jobject instance, jstring jkey_images_json) { - MTRACE("Java_monero_wallet_MoneroWalletFull_importKeyImagesJni"); +jstring import_key_images_jni(JNIEnv* env, jobject instance, jstring jkey_images_json) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); const char* _key_images_json = jkey_images_json ? env->GetStringUTFChars(jkey_images_json, NULL) : nullptr; string key_images_json = string(_key_images_json ? _key_images_json : ""); @@ -1234,7 +1281,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_importKeyImagesJni } } -JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_freezeOutputJni(JNIEnv* env, jobject instance, jstring jkey_image) { +void freeze_output_jni(JNIEnv* env, jobject instance, jstring jkey_image) { MTRACE("Java_monero_wallet_MoneroWalletFull_freezeJni"); monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); const char* _key_image = jkey_image ? env->GetStringUTFChars(jkey_image, NULL) : nullptr; @@ -1249,7 +1296,7 @@ JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_freezeOutputJni(JNIEn } } -JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_thawOutputJni(JNIEnv* env, jobject instance, jstring jkey_image) { +void thaw_output_jni(JNIEnv* env, jobject instance, jstring jkey_image) { MTRACE("Java_monero_wallet_MoneroWalletFull_thawJni"); monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); const char* _key_image = jkey_image ? env->GetStringUTFChars(jkey_image, NULL) : nullptr; @@ -1264,8 +1311,7 @@ JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_thawOutputJni(JNIEnv* } } -JNIEXPORT bool JNICALL Java_monero_wallet_MoneroWalletFull_isOutputFrozenJni(JNIEnv* env, jobject instance, jstring jkey_image) { - MTRACE("Java_monero_wallet_MoneroWalletFull_isFrozenJni"); +bool is_output_frozen_jni(JNIEnv* env, jobject instance, jstring jkey_image) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); const char* _key_image = jkey_image ? env->GetStringUTFChars(jkey_image, NULL) : nullptr; string key_image = string(_key_image ? _key_image : ""); @@ -1280,13 +1326,12 @@ JNIEXPORT bool JNICALL Java_monero_wallet_MoneroWalletFull_isOutputFrozenJni(JNI } } -JNIEXPORT jint JNICALL Java_monero_wallet_MoneroWalletFull_getDefaultFeePriorityJni(JNIEnv* env, jobject instance) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getDefaultFeePriorityJni"); +jint get_default_fee_priority_jni(JNIEnv* env, jobject instance) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); return wallet->get_default_fee_priority(); } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_createTxsJni(JNIEnv* env, jobject instance, jstring jconfig) { +jstring create_txs_jni(JNIEnv* env, jobject instance, jstring jconfig) { MTRACE("Java_monero_wallet_MoneroWalletFull_sendTxsJni(request)"); monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); const char* _config = jconfig ? env->GetStringUTFChars(jconfig, NULL) : nullptr; @@ -1309,8 +1354,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_createTxsJni(JNIEn } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_sweepUnlockedJni(JNIEnv* env, jobject instance, jstring jconfig) { - MTRACE("Java_monero_wallet_MoneroWalletFull_sweepUnlockedJni(config)"); +jstring sweep_unlocked_jni(JNIEnv* env, jobject instance, jstring jconfig) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); const char* _config = jconfig ? env->GetStringUTFChars(jconfig, NULL) : nullptr; string config_json = string(_config ? _config : ""); @@ -1348,8 +1392,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_sweepUnlockedJni(J } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_sweepOutputJni(JNIEnv* env, jobject instance, jstring jconfig) { - MTRACE("Java_monero_wallet_MoneroWalletFull_sweepOutputJni(request)"); +jstring sweep_output_jni(JNIEnv* env, jobject instance, jstring jconfig) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); const char* _config = jconfig ? env->GetStringUTFChars(jconfig, NULL) : nullptr; string config_json = string(_config); @@ -1373,8 +1416,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_sweepOutputJni(JNI } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_sweepDustJni(JNIEnv* env, jobject instance, jboolean relay) { - MTRACE("Java_monero_wallet_MoneroWalletFull_sweepDustJni(request)"); +jstring sweep_dust_jni(JNIEnv* env, jobject instance, jboolean relay) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); // sweep dust @@ -1389,8 +1431,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_sweepDustJni(JNIEn } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_describeTxSetJni(JNIEnv* env, jobject instance, jstring jtx_set_json) { - MTRACE("Java_monero_wallet_MoneroWalletFull_describeTxSetJson(tx_set_json)"); +jstring describe_tx_set_jni(JNIEnv* env, jobject instance, jstring jtx_set_json) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); // get tx set json string @@ -1416,8 +1457,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_describeTxSetJni(J } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_signTxsJni(JNIEnv* env, jobject instance, jstring junsigned_tx_hex) { - MTRACE("Java_monero_wallet_MoneroWalletFull_signTxsJni()"); +jstring sign_txs_jni(JNIEnv* env, jobject instance, jstring junsigned_tx_hex) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); // get unsigned tx set as string @@ -1440,8 +1480,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_signTxsJni(JNIEnv* } } -JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletFull_submitTxsJni(JNIEnv* env, jobject instance, jstring jsigned_tx_hex) { - MTRACE("Java_monero_wallet_MoneroWalletFull_submitTxsJni()"); +jobjectArray submit_txs_jni(JNIEnv* env, jobject instance, jstring jsigned_tx_hex) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); // get signed tx set as string @@ -1464,8 +1503,7 @@ JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletFull_submitTxsJni( } } -JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletFull_relayTxsJni(JNIEnv* env, jobject instance, jobjectArray jtx_metadatas) { - MTRACE("Java_monero_wallet_MoneroWalletFull_relayTxsJni"); +jobjectArray relay_txs_jni(JNIEnv* env, jobject instance, jobjectArray jtx_metadatas) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); // get tx metadatas from jobjectArray to vector @@ -1498,8 +1536,7 @@ JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletFull_relayTxsJni(J return jtx_hashes; } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_signMessageJni(JNIEnv* env, jobject instance, jstring jmsg, jint message_signature_type, jint account_idx, jint subaddress_idx) { - MTRACE("Java_monero_wallet_MoneroWalletFull_signMessageJni"); +jstring sign_message_jni(JNIEnv* env, jobject instance, jstring jmsg, jint message_signature_type, jint account_idx, jint subaddress_idx) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); const char* _msg = jmsg ? env->GetStringUTFChars(jmsg, NULL) : nullptr; string msg = string(_msg ? _msg : ""); @@ -1514,8 +1551,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_signMessageJni(JNI } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_verifyMessageJni(JNIEnv* env, jobject instance, jstring jmsg, jstring jaddress, jstring jsignature) { - MTRACE("Java_monero_wallet_MoneroWalletFull_verifyMessageJni"); +jstring verify_message_jni(JNIEnv* env, jobject instance, jstring jmsg, jstring jaddress, jstring jsignature) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); const char* _msg = jmsg ? env->GetStringUTFChars(jmsg, NULL) : nullptr; const char* _address = jaddress ? env->GetStringUTFChars(jaddress, NULL) : nullptr; @@ -1535,7 +1571,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_verifyMessageJni(J } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getTxKeyJni(JNIEnv* env, jobject instance, jstring jtx_hash) { +jstring get_tx_key_jni(JNIEnv* env, jobject instance, jstring jtx_hash) { MTRACE("Java_monero_wallet_MoneroWalletFull_getTxKeyJniJni"); monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); const char* _tx_hash = jtx_hash ? env->GetStringUTFChars(jtx_hash, NULL) : nullptr; @@ -1549,8 +1585,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getTxKeyJni(JNIEnv } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_checkTxKeyJni(JNIEnv* env, jobject instance, jstring jtx_hash, jstring jtx_key, jstring jaddress) { - MTRACE("Java_monero_wallet_MoneroWalletFull_checktx_keyJni"); +jstring check_tx_key_jni(JNIEnv* env, jobject instance, jstring jtx_hash, jstring jtx_key, jstring jaddress) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); const char* _tx_hash = jtx_hash ? env->GetStringUTFChars(jtx_hash, NULL) : nullptr; const char* _tx_key = jtx_key ? env->GetStringUTFChars(jtx_key, NULL) : nullptr; @@ -1569,8 +1604,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_checkTxKeyJni(JNIE } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getTxProofJni(JNIEnv* env, jobject instance, jstring jtx_hash, jstring jaddress, jstring jmessage) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getTxProofJni"); +jstring get_tx_proof_jni(JNIEnv* env, jobject instance, jstring jtx_hash, jstring jaddress, jstring jmessage) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); const char* _tx_hash = jtx_hash ? env->GetStringUTFChars(jtx_hash, NULL) : nullptr; const char* _address = jaddress ? env->GetStringUTFChars(jaddress, NULL) : nullptr; @@ -1589,8 +1623,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getTxProofJni(JNIE } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_checkTxProofJni(JNIEnv* env, jobject instance, jstring jtx_hash, jstring jaddress, jstring jmessage, jstring jsignature) { - MTRACE("Java_monero_wallet_MoneroWalletFull_checkTxProofJni"); +jstring check_tx_proof_jni(JNIEnv* env, jobject instance, jstring jtx_hash, jstring jaddress, jstring jmessage, jstring jsignature) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); const char* _tx_hash = jtx_hash ? env->GetStringUTFChars(jtx_hash, NULL) : nullptr; const char* _address = jaddress ? env->GetStringUTFChars(jaddress, NULL) : nullptr; @@ -1612,8 +1645,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_checkTxProofJni(JN } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getSpendProofJni(JNIEnv* env, jobject instance, jstring jtx_hash, jstring jmessage) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getSpendProofJni"); +jstring get_spend_proof_jni(JNIEnv* env, jobject instance, jstring jtx_hash, jstring jmessage) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); const char* _tx_hash = jtx_hash ? env->GetStringUTFChars(jtx_hash, NULL) : nullptr; const char* _message = jmessage ? env->GetStringUTFChars(jmessage, NULL) : nullptr; @@ -1629,8 +1661,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getSpendProofJni(J } } -JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletFull_checkSpendProofJni(JNIEnv* env, jobject instance, jstring jtx_hash, jstring jmessage, jstring jsignature) { - MTRACE("Java_monero_wallet_MoneroWalletFull_checkSpendProofJni"); +jboolean check_spend_proof_jni(JNIEnv* env, jobject instance, jstring jtx_hash, jstring jmessage, jstring jsignature) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); const char* _tx_hash = jtx_hash ? env->GetStringUTFChars(jtx_hash, NULL) : nullptr; const char* _message = jmessage ? env->GetStringUTFChars(jmessage, NULL) : nullptr; @@ -1649,8 +1680,7 @@ JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletFull_checkSpendProofJn } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getReserveProofWalletJni(JNIEnv* env, jobject instance, jstring jmessage) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getReserveProofWalletJni"); +jstring get_reserve_proof_wallet_jni(JNIEnv* env, jobject instance, jstring jmessage) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); const char* _message = jmessage ? env->GetStringUTFChars(jmessage, NULL) : nullptr; string message = string(_message == nullptr ? "" : _message); @@ -1663,8 +1693,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getReserveProofWal } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getReserveProofAccountJni(JNIEnv* env, jobject instance, jint account_idx, jstring jamount_str, jstring jmessage) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getReserveProofWalletJni"); +jstring get_reserve_proof_account_jni(JNIEnv* env, jobject instance, jint account_idx, jstring jamount_str, jstring jmessage) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); const char* _amount_str = jamount_str ? env->GetStringUTFChars(jamount_str, NULL) : nullptr; const char* _message = jmessage ? env->GetStringUTFChars(jmessage, NULL) : nullptr; @@ -1681,8 +1710,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getReserveProofAcc } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_checkReserveProofJni(JNIEnv* env, jobject instance, jstring jaddress, jstring jmessage, jstring jsignature) { - MTRACE("Java_monero_wallet_MoneroWalletFull_checkReserveProofAccountJni"); +jstring check_reserve_proof_jni(JNIEnv* env, jobject instance, jstring jaddress, jstring jmessage, jstring jsignature) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); const char* _address = jaddress ? env->GetStringUTFChars(jaddress, NULL) : nullptr; const char* _message = jmessage ? env->GetStringUTFChars(jmessage, NULL) : nullptr; @@ -1701,8 +1729,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_checkReserveProofJ } } -JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletFull_getTxNotesJni(JNIEnv* env, jobject instance, jobjectArray jtx_hashes) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getTxNotesJni"); +jobjectArray get_tx_notes_jni(JNIEnv* env, jobject instance, jobjectArray jtx_hashes) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); // get tx hashes from jobjectArray to vector @@ -1737,8 +1764,7 @@ JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletFull_getTxNotesJni return jtx_notes; } -JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_setTxNotesJni(JNIEnv* env, jobject instance, jobjectArray jtx_hashes, jobjectArray jtx_notes) { - MTRACE("Java_monero_wallet_MoneroWalletFull_setTxNotesJni"); +void set_tx_notes_jni(JNIEnv* env, jobject instance, jobjectArray jtx_hashes, jobjectArray jtx_notes) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); // get tx hashes from jobjectArray to vector @@ -1779,8 +1805,7 @@ JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_setTxNotesJni(JNIEnv* } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getAddressBookEntriesJni(JNIEnv* env, jobject instance, jintArray jindices) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getAddressBookEntriesJni"); +jstring get_address_book_entries_jni(JNIEnv* env, jobject instance, jintArray jindices) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); // convert subaddress indices from jintArray to vector @@ -1811,8 +1836,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getAddressBookEntr } // TODO: return jlong for uint64_t -JNIEXPORT jint JNICALL Java_monero_wallet_MoneroWalletFull_addAddressBookEntryJni(JNIEnv* env, jobject instance, jstring jaddress, jstring jdescription) { - MTRACE("Java_monero_wallet_MoneroWalletFull_addAddressBookEntryJni"); +jint add_address_book_entry_jni(JNIEnv* env, jobject instance, jstring jaddress, jstring jdescription) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); // collect string params @@ -1832,8 +1856,7 @@ JNIEXPORT jint JNICALL Java_monero_wallet_MoneroWalletFull_addAddressBookEntryJn } } -JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_editAddressBookEntryJni(JNIEnv* env, jobject instance, jint index, jboolean set_address, jstring jaddress, jboolean set_description, jstring jdescription) { - MTRACE("Java_monero_wallet_MoneroWalletFull_editAddressBookEntryJni"); +void edit_address_book_entry_jni(JNIEnv* env, jobject instance, jint index, jboolean set_address, jstring jaddress, jboolean set_description, jstring jdescription) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); // collect string params @@ -1852,8 +1875,7 @@ JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_editAddressBookEntryJ } } -JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_deleteAddressBookEntryJni(JNIEnv* env, jobject instance, jint index) { - MTRACE("Java_monero_wallet_MoneroWalletFull_deleteAddressBookEntryJni"); +void delete_address_book_entry_jni(JNIEnv* env, jobject instance, jint index) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); // delete address book entry @@ -1864,8 +1886,7 @@ JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_deleteAddressBookEntr } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getPaymentUriJni(JNIEnv* env, jobject instance, jstring jconfig) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getPaymentUriJni()"); +jstring get_payment_uri_jni(JNIEnv* env, jobject instance, jstring jconfig) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); const char* _config = jconfig ? env->GetStringUTFChars(jconfig, NULL) : nullptr; string config_json = string(_config ? _config : ""); @@ -1888,8 +1909,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getPaymentUriJni(J return env->NewStringUTF(payment_uri.c_str()); } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_parsePaymentUriJni(JNIEnv* env, jobject instance, jstring juri) { - MTRACE("Java_monero_wallet_MoneroWalletFull_parsePaymentUriJni()"); +jstring parse_payment_uri_jni(JNIEnv* env, jobject instance, jstring juri) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); const char* _uri = juri ? env->GetStringUTFChars(juri, NULL) : nullptr; string uri = string(_uri ? _uri : ""); @@ -1908,8 +1928,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_parsePaymentUriJni return env->NewStringUTF(config->serialize().c_str()); } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getAttributeJni(JNIEnv* env, jobject instance, jstring jkey) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getAttribute()"); +jstring get_attribute_jni(JNIEnv* env, jobject instance, jstring jkey) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); const char* _key = jkey ? env->GetStringUTFChars(jkey, NULL) : nullptr; string key = string(_key); @@ -1924,8 +1943,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getAttributeJni(JN } } -JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_setAttributeJni(JNIEnv* env, jobject instance, jstring jkey, jstring jval) { - MTRACE("Java_monero_wallet_MoneroWalletFull_setAttribute()"); +void set_attribute_jni(JNIEnv* env, jobject instance, jstring jkey, jstring jval) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); const char* _key = jkey ? env->GetStringUTFChars(jkey, NULL) : nullptr; const char* _val = jval ? env->GetStringUTFChars(jval, NULL) : nullptr; @@ -1940,8 +1958,7 @@ JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_setAttributeJni(JNIEn } } -JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_startMiningJni(JNIEnv* env, jobject instance, jlong num_threads, jboolean background_mining, jboolean ignore_battery) { - MTRACE("Java_monero_wallet_MoneroWalletFull_startMiningJni()"); +void start_mining_jni(JNIEnv* env, jobject instance, jlong num_threads, jboolean background_mining, jboolean ignore_battery) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); try { wallet->start_mining(num_threads, background_mining, ignore_battery); @@ -1950,7 +1967,7 @@ JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_startMiningJni(JNIEnv } } -JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_stopMiningJni(JNIEnv* env, jobject instance) { +void stop_mining_jni(JNIEnv* env, jobject instance) { MTRACE("Java_monero_wallet_MoneroWalletFull_startMiningJni()"); monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); try { @@ -1960,8 +1977,7 @@ JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_stopMiningJni(JNIEnv* } } -JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletFull_isMultisigImportNeededJni(JNIEnv* env, jobject instance) { - MTRACE("Java_monero_wallet_MoneroWalletFull_isMultisigImportNeededJni"); +jboolean is_multisig_import_needed_jni(JNIEnv* env, jobject instance) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); try { bool is_multisig_import_needed = wallet->is_multisig_import_needed(); @@ -1972,8 +1988,7 @@ JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletFull_isMultisigImportN } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getMultisigInfoJni(JNIEnv* env, jobject instance) { - MTRACE("Java_monero_wallet_MoneroWalletFull_getMultisigInfoJni"); +jstring get_multisig_info_jni(JNIEnv* env, jobject instance) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); try { monero_multisig_info info = wallet->get_multisig_info(); @@ -1984,8 +1999,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getMultisigInfoJni } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_prepareMultisigJni(JNIEnv* env, jobject instance) { - MTRACE("Java_monero_wallet_MoneroWalletFull_prepareMultisigJni"); +jstring prepare_multisig_jni(JNIEnv* env, jobject instance) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); try { string multisig_hex = wallet->prepare_multisig(); @@ -1996,9 +2010,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_prepareMultisigJni } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_makeMultisigJni(JNIEnv* env, jobject instance, jobjectArray jmultisig_hexes, jint threshold, jstring jpassword) { - MTRACE("Java_monero_wallet_MoneroWalletFull_makeMultisigJni"); - +jstring make_multisig_jni(JNIEnv* env, jobject instance, jobjectArray jmultisig_hexes, jint threshold, jstring jpassword) { // get multisig hex as vector vector multisig_hexes; if (jmultisig_hexes != nullptr) { @@ -2030,9 +2042,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_makeMultisigJni(JN } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_exchangeMultisigKeysJni(JNIEnv* env, jobject instance, jobjectArray jmultisig_hexes, jstring jpassword) { - MTRACE("Java_monero_wallet_MoneroWalletFull_exchangeMultisigKeysJni"); - +jstring exchange_multisig_keys_jni(JNIEnv* env, jobject instance, jobjectArray jmultisig_hexes, jstring jpassword) { // get multisig hex as vector vector multisig_hexes; if (jmultisig_hexes != nullptr) { @@ -2064,8 +2074,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_exchangeMultisigKe } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_exportMultisigHexJni(JNIEnv* env, jobject instance) { - MTRACE("Java_monero_wallet_MoneroWalletFull_exportMultisigHexJni"); +jstring export_multisig_hex_jni(JNIEnv* env, jobject instance) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); try { string multisig_hex = wallet->export_multisig_hex(); @@ -2076,9 +2085,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_exportMultisigHexJ } } -JNIEXPORT jint JNICALL Java_monero_wallet_MoneroWalletFull_importMultisigHexJni(JNIEnv* env, jobject instance, jobjectArray jmultisig_hexes) { - MTRACE("Java_monero_wallet_MoneroWalletFull_importMultisigHexJni"); - +jint import_multisig_hex_jni(JNIEnv* env, jobject instance, jobjectArray jmultisig_hexes) { // get peer multisig hex as vector vector multisig_hexes; if (jmultisig_hexes != nullptr) { @@ -2105,9 +2112,7 @@ JNIEXPORT jint JNICALL Java_monero_wallet_MoneroWalletFull_importMultisigHexJni( } } -JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_signMultisigTxHexJni(JNIEnv* env, jobject instance, jstring jmultisig_tx_hex) { - MTRACE("Java_monero_wallet_MoneroWalletFull_signMultisigTxHexJni"); - +jstring sign_multisig_tx_hex_jni(JNIEnv* env, jobject instance, jstring jmultisig_tx_hex) { // get multisig tx hex as string const char* _multisig_tx_hex = jmultisig_tx_hex ? env->GetStringUTFChars(jmultisig_tx_hex, NULL) : nullptr; string multisig_tx_hex = string(_multisig_tx_hex ? _multisig_tx_hex : ""); @@ -2124,9 +2129,7 @@ JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_signMultisigTxHexJ } } -JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletFull_submitMultisigTxHexJni(JNIEnv* env, jobject instance, jstring jsigned_multisig_tx_hex) { - MTRACE("Java_monero_wallet_MoneroWalletFull_submitMultisigTxHexJni"); - +jobjectArray submit_multisig_tx_hex_jni(JNIEnv* env, jobject instance, jstring jsigned_multisig_tx_hex) { // get signed multisig tx hex as string const char* _signed_multisig_tx_hex = jsigned_multisig_tx_hex ? env->GetStringUTFChars(jsigned_multisig_tx_hex, NULL) : nullptr; string signed_multisig_tx_hex = string(_signed_multisig_tx_hex ? _signed_multisig_tx_hex : ""); @@ -2145,8 +2148,7 @@ JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletFull_submitMultisi } } -JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_changePasswordJni(JNIEnv* env, jobject instance, jstring jold_password, jstring jnew_password) { - MTRACE("Java_monero_wallet_MoneroWalletFull_changePasswordJni(oldPassword, newPassword)"); +void change_password_jni(JNIEnv* env, jobject instance, jstring jold_password, jstring jnew_password) { monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); const char* _old_password = jold_password ? env->GetStringUTFChars(jold_password, NULL) : nullptr; const char* _new_password = jnew_password ? env->GetStringUTFChars(jnew_password, NULL) : nullptr; @@ -2159,8 +2161,7 @@ JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_changePasswordJni(JNI } } -JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_moveToJni(JNIEnv* env, jobject instance, jstring jpath, jstring jpassword) { - MTRACE("Java_monero_wallet_MoneroWalletFull_moveToJni(path, password)"); +void move_to_jni(JNIEnv* env, jobject instance, jstring jpath, jstring jpassword) { const char* _path = jpath ? env->GetStringUTFChars(jpath, NULL) : nullptr; const char* _password = jpassword ? env->GetStringUTFChars(jpassword, NULL) : nullptr; string path = string(_path ? _path : ""); @@ -2175,22 +2176,518 @@ JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_moveToJni(JNIEnv* env } } -JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_saveJni(JNIEnv* env, jobject instance) { +void save_jni(JNIEnv* env, jobject instance) { + monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); + try { + wallet->save(); + } catch (...) { + rethrow_cpp_exception_as_java_exception(env); + } +} + +void close_jni(JNIEnv* env, jobject instance, jboolean save) { + monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); + if (save) wallet->save(); + delete wallet; + wallet = nullptr; +} + +// ------------------------ FULL WALLET INSTANCE METHODS -------------------------- + +JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletFull_isViewOnlyJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletFull_isViewOnlyJni"); + return is_view_only_jni(env, instance); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_setDaemonConnectionJni(JNIEnv *env, jobject instance, jstring juri, jstring jusername, jstring jpassword, jstring jproxy_uri) { + MTRACE("Java_monero_wallet_MoneroWalletFull_setDaemonConnectionJni"); + set_daemon_connection_jni(env, instance, juri, jusername, jpassword, jproxy_uri); +} + +JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletFull_getDaemonConnectionJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getDaemonConnectionJni()"); + return get_daemon_connection_jni(env, instance); +} + +JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletFull_isConnectedToDaemonJni(JNIEnv* env, jobject instance) { + return is_connected_to_daemon_jni(env, instance); +} + +JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletFull_isDaemonSyncedJni(JNIEnv* env, jobject instance) { + return is_daemon_synced_jni(env, instance); +} + +JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletFull_isSyncedJni(JNIEnv* env, jobject instance) { + return is_synced_jni(env, instance); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getVersionJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getVersionJni"); + return get_version_jni(env, instance); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getPathJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getPathJni"); + return get_path_jni(env, instance); +} + +JNIEXPORT jint JNICALL Java_monero_wallet_MoneroWalletFull_getNetworkTypeJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getNetworkTypeJni"); + return get_network_type_jni(env, instance); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getSeedJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getSeedJni"); + return get_seed_jni(env, instance); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getSeedLanguageJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getSeedLanguageJni"); + return get_seed_language_jni(env, instance); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getPublicViewKeyJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getPublicViewKeyJni"); + return get_public_view_key_jni(env, instance); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getPrivateViewKeyJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getPrivateViewKeyJni"); + return get_private_view_key_jni(env, instance); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getPublicSpendKeyJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getPublicSpendKeyJni"); + return get_public_spend_key_jni(env, instance); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getPrivateSpendKeyJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getPrivateSpendKeyJni"); + return get_private_spend_key_jni(env, instance); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getAddressJni(JNIEnv *env, jobject instance, jint account_idx, jint subaddress_idx) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getAddressJni"); + return get_address_jni(env, instance, account_idx, subaddress_idx); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getAddressIndexJni(JNIEnv *env, jobject instance, jstring jaddress) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getAddressIndexJni"); + return get_address_index_jni(env, instance, jaddress); +} + +JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletFull_setListenerJni(JNIEnv *env, jobject instance, jobject jlistener) { + MTRACE("Java_monero_wallet_MoneroWalletFull_setListenerJni"); + return set_listener_jni(env, instance, jlistener); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getIntegratedAddressJni(JNIEnv *env, jobject instance, jstring jstandard_address, jstring jpayment_id) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getIntegratedAddressJni"); + return get_integrated_address_jni(env, instance, jstandard_address, jpayment_id); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_decodeIntegratedAddressJni(JNIEnv *env, jobject instance, jstring jintegrated_address) { + MTRACE("Java_monero_wallet_MoneroWalletFull_decodeIntegratedAddressJni"); + return decode_integrated_address_jni(env, instance, jintegrated_address); +} + +JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletFull_getHeightJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getHeightJni"); + return get_height_jni(env, instance); +} + +JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletFull_getChainHeightJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getChainHeightJni"); + return get_chain_height_jni(env, instance); +} + +JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletFull_getRestoreHeightJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getRestoreHeightJni"); + return get_restore_height_jni(env, instance); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_setRestoreHeightJni(JNIEnv *env, jobject instance, jlong restore_height) { + MTRACE("Java_monero_wallet_MoneroWalletFull_setRestoreHeightJni"); + set_restore_height_jni(env, instance, restore_height); +} + +JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletFull_getDaemonHeightJni(JNIEnv* env, jobject instance) { + return get_daemon_height_jni(env, instance); +} + +JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletFull_getDaemonMaxPeerHeightJni(JNIEnv* env, jobject instance) { + return get_daemon_max_peer_height_jni(env, instance); +} + +JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletFull_getHeightByDateJni(JNIEnv* env, jobject instance, jint year, jint month, jint day) { + return get_height_by_date_jni(env, instance, year, month, day); +} + +JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletFull_syncJni(JNIEnv *env, jobject instance, jlong start_height) { + MTRACE("Java_monero_wallet_MoneroWalletFull_syncJni"); + return sync_jni(env, instance, start_height); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_startSyncingJni(JNIEnv *env, jobject instance, jlong sync_period_in_ms) { + MTRACE("Java_monero_wallet_MoneroWalletFull_startSyncingJni"); + start_syncing_jni(env, instance, sync_period_in_ms); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_stopSyncingJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletFull_stopSyncingJni"); + stop_syncing_jni(env, instance); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_scanTxsJni(JNIEnv* env, jobject instance, jobjectArray jtx_hashes) { + MTRACE("Java_monero_wallet_MoneroWalletFull_scanTxsJni"); + scan_txs_jni(env, instance, jtx_hashes); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_rescanSpentJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletFull_rescanSpentJni"); + rescan_spent_jni(env, instance); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_rescanBlockchainJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletFull_rescanBlockchainJni"); + rescan_blockchain_jni(env, instance); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getBalanceWalletJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getBalanceWalletJni"); + return get_balance_wallet_jni(env, instance); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getBalanceAccountJni(JNIEnv *env, jobject instance, jint account_idx) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getBalanceAccountJni"); + return get_balance_account_jni(env, instance, account_idx); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getBalanceSubaddressJni(JNIEnv *env, jobject instance, jint account_idx, jint subaddress_idx) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getBalanceSubaddressJni"); + return get_balance_subaddress_jni(env, instance, account_idx, subaddress_idx); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getUnlockedBalanceWalletJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getUnlockedBalanceWalletJni"); + monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); + uint64_t balance = wallet->get_unlocked_balance(); + return env->NewStringUTF(boost::lexical_cast(balance).c_str()); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getUnlockedBalanceAccountJni(JNIEnv *env, jobject instance, jint account_idx) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getUnlockedBalanceAccountJni"); + return get_unlocked_balance_account_jni(env, instance, account_idx); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getUnlockedBalanceSubaddressJni(JNIEnv *env, jobject instance, jint account_idx, jint subaddress_idx) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getUnlockedBalanceSubaddressJni"); + return get_unlocked_balance_subaddress_jni(env, instance, account_idx, subaddress_idx); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getAccountsJni(JNIEnv* env, jobject instance, jboolean include_subaddresses, jstring jtag) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getAccountsJni"); + return get_accounts_jni(env, instance, include_subaddresses, jtag); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getAccountJni(JNIEnv* env, jobject instance, jint account_idx, jboolean include_subaddresses) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getAccountJni"); + return get_account_jni(env, instance, account_idx, include_subaddresses); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_createAccountJni(JNIEnv* env, jobject instance, jstring jlabel) { + MTRACE("Java_monero_wallet_MoneroWalletFull_createAccountJni"); + return create_account_jni(env, instance, jlabel); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getSubaddressesJni(JNIEnv* env, jobject instance, jint account_idx, jintArray jsubaddressIndices) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getSubaddressesJni"); + return get_subaddresses_jni(env, instance, account_idx, jsubaddressIndices); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_createSubaddressJni(JNIEnv* env, jobject instance, jint account_idx, jstring jlabel) { + MTRACE("Java_monero_wallet_MoneroWalletFull_createSubaddressJni"); + return create_subaddress_jni(env, instance, account_idx, jlabel); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_setSubaddressLabelJni(JNIEnv* env, jobject instance, jint account_idx, jint subaddress_idx, jstring jlabel) { + MTRACE("Java_monero_wallet_MoneroWalletFull_setSubaddressLabelJni"); + set_subaddress_label_jni(env, instance, account_idx, subaddress_idx, jlabel); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getTxsJni(JNIEnv* env, jobject instance, jstring jtx_query) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getTxsJni"); + return get_txs_jni(env, instance, jtx_query); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getTransfersJni(JNIEnv* env, jobject instance, jstring jtransfer_query) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getTransfersJni"); + return get_transfers_jni(env, instance, jtransfer_query); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getOutputsJni(JNIEnv* env, jobject instance, jstring joutput_query) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getOutputsJni"); + return get_outputs_jni(env, instance, joutput_query); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_exportOutputsJni(JNIEnv* env, jobject instance, jboolean all) { + MTRACE("Java_monero_wallet_MoneroWalletFull_exportOutputsJni()"); + return export_outputs_jni(env, instance, all); +} + +JNIEXPORT jint JNICALL Java_monero_wallet_MoneroWalletFull_importOutputsJni(JNIEnv* env, jobject instance, jstring joutputs_hex) { + MTRACE("Java_monero_wallet_MoneroWalletFull_exportOutputsJni()"); + return import_outputs_jni(env, instance, joutputs_hex); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_exportKeyImagesJni(JNIEnv* env, jobject instance, jboolean all) { + MTRACE("Java_monero_wallet_MoneroWalletFull_exportKeyImagesJni"); + return export_key_images_jni(env, instance, all); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_importKeyImagesJni(JNIEnv* env, jobject instance, jstring jkey_images_json) { + MTRACE("Java_monero_wallet_MoneroWalletFull_importKeyImagesJni"); + return import_key_images_jni(env, instance, jkey_images_json); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_freezeOutputJni(JNIEnv* env, jobject instance, jstring jkey_image) { + MTRACE("Java_monero_wallet_MoneroWalletFull_freezeJni"); + freeze_output_jni(env, instance, jkey_image); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_thawOutputJni(JNIEnv* env, jobject instance, jstring jkey_image) { + MTRACE("Java_monero_wallet_MoneroWalletFull_thawJni"); + thaw_output_jni(env, instance, jkey_image); +} + +JNIEXPORT bool JNICALL Java_monero_wallet_MoneroWalletFull_isOutputFrozenJni(JNIEnv* env, jobject instance, jstring jkey_image) { + MTRACE("Java_monero_wallet_MoneroWalletFull_isFrozenJni"); + return is_output_frozen_jni(env, instance, jkey_image); +} + +JNIEXPORT jint JNICALL Java_monero_wallet_MoneroWalletFull_getDefaultFeePriorityJni(JNIEnv* env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getDefaultFeePriorityJni"); + return get_default_fee_priority_jni(env, instance); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_createTxsJni(JNIEnv* env, jobject instance, jstring jconfig) { + MTRACE("Java_monero_wallet_MoneroWalletFull_createTxsJni(request)"); + return create_txs_jni(env, instance, jconfig); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_sweepUnlockedJni(JNIEnv* env, jobject instance, jstring jconfig) { + MTRACE("Java_monero_wallet_MoneroWalletFull_sweepUnlockedJni(config)"); + return sweep_unlocked_jni(env, instance, jconfig); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_sweepOutputJni(JNIEnv* env, jobject instance, jstring jconfig) { + MTRACE("Java_monero_wallet_MoneroWalletFull_sweepOutputJni(request)"); + return sweep_output_jni(env, instance, jconfig); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_sweepDustJni(JNIEnv* env, jobject instance, jboolean relay) { + MTRACE("Java_monero_wallet_MoneroWalletFull_sweepDustJni(request)"); + return sweep_dust_jni(env, instance, relay); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_describeTxSetJni(JNIEnv* env, jobject instance, jstring jtx_set_json) { + MTRACE("Java_monero_wallet_MoneroWalletFull_describeTxSetJson(tx_set_json)"); + return describe_tx_set_jni(env, instance, jtx_set_json); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_signTxsJni(JNIEnv* env, jobject instance, jstring junsigned_tx_hex) { + MTRACE("Java_monero_wallet_MoneroWalletFull_signTxsJni()"); + return sign_txs_jni(env, instance, junsigned_tx_hex); +} + +JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletFull_submitTxsJni(JNIEnv* env, jobject instance, jstring jsigned_tx_hex) { + MTRACE("Java_monero_wallet_MoneroWalletFull_submitTxsJni()"); + return submit_txs_jni(env, instance, jsigned_tx_hex); +} + +JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletFull_relayTxsJni(JNIEnv* env, jobject instance, jobjectArray jtx_metadatas) { + MTRACE("Java_monero_wallet_MoneroWalletFull_relayTxsJni"); + return relay_txs_jni(env, instance, jtx_metadatas); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_signMessageJni(JNIEnv* env, jobject instance, jstring jmsg, jint message_signature_type, jint account_idx, jint subaddress_idx) { + MTRACE("Java_monero_wallet_MoneroWalletFull_signMessageJni"); + return sign_message_jni(env, instance, jmsg, message_signature_type, account_idx, subaddress_idx); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_verifyMessageJni(JNIEnv* env, jobject instance, jstring jmsg, jstring jaddress, jstring jsignature) { + MTRACE("Java_monero_wallet_MoneroWalletFull_verifyMessageJni"); + return verify_message_jni(env, instance, jmsg, jaddress, jsignature); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getTxKeyJni(JNIEnv* env, jobject instance, jstring jtx_hash) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getTxKeyJniJni"); + return get_tx_key_jni(env, instance, jtx_hash); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_checkTxKeyJni(JNIEnv* env, jobject instance, jstring jtx_hash, jstring jtx_key, jstring jaddress) { + MTRACE("Java_monero_wallet_MoneroWalletFull_checktx_keyJni"); + return check_tx_key_jni(env, instance, jtx_hash, jtx_key, jaddress); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getTxProofJni(JNIEnv* env, jobject instance, jstring jtx_hash, jstring jaddress, jstring jmessage) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getTxProofJni"); + return get_tx_proof_jni(env, instance, jtx_hash, jaddress, jmessage); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_checkTxProofJni(JNIEnv* env, jobject instance, jstring jtx_hash, jstring jaddress, jstring jmessage, jstring jsignature) { + MTRACE("Java_monero_wallet_MoneroWalletFull_checkTxProofJni"); + return check_tx_proof_jni(env, instance, jtx_hash, jaddress, jmessage, jsignature); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getSpendProofJni(JNIEnv* env, jobject instance, jstring jtx_hash, jstring jmessage) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getSpendProofJni"); + return get_spend_proof_jni(env, instance, jtx_hash, jmessage); +} + +JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletFull_checkSpendProofJni(JNIEnv* env, jobject instance, jstring jtx_hash, jstring jmessage, jstring jsignature) { + MTRACE("Java_monero_wallet_MoneroWalletFull_checkSpendProofJni"); + return check_spend_proof_jni(env, instance, jtx_hash, jmessage, jsignature); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getReserveProofWalletJni(JNIEnv* env, jobject instance, jstring jmessage) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getReserveProofWalletJni"); + return get_reserve_proof_wallet_jni(env, instance, jmessage); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getReserveProofAccountJni(JNIEnv* env, jobject instance, jint account_idx, jstring jamount_str, jstring jmessage) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getReserveProofWalletJni"); + return get_reserve_proof_account_jni(env, instance, account_idx, jamount_str, jmessage); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_checkReserveProofJni(JNIEnv* env, jobject instance, jstring jaddress, jstring jmessage, jstring jsignature) { + MTRACE("Java_monero_wallet_MoneroWalletFull_checkReserveProofAccountJni"); + return check_reserve_proof_jni(env, instance, jaddress, jmessage, jsignature); +} + +JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletFull_getTxNotesJni(JNIEnv* env, jobject instance, jobjectArray jtx_hashes) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getTxNotesJni"); + return get_tx_notes_jni(env, instance, jtx_hashes); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_setTxNotesJni(JNIEnv* env, jobject instance, jobjectArray jtx_hashes, jobjectArray jtx_notes) { + MTRACE("Java_monero_wallet_MoneroWalletFull_setTxNotesJni"); + set_tx_notes_jni(env, instance, jtx_hashes, jtx_notes); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getAddressBookEntriesJni(JNIEnv* env, jobject instance, jintArray jindices) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getAddressBookEntriesJni"); + return get_address_book_entries_jni(env, instance, jindices); +} + +JNIEXPORT jint JNICALL Java_monero_wallet_MoneroWalletFull_addAddressBookEntryJni(JNIEnv* env, jobject instance, jstring jaddress, jstring jdescription) { + MTRACE("Java_monero_wallet_MoneroWalletFull_addAddressBookEntryJni"); + return add_address_book_entry_jni(env, instance, jaddress, jdescription); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_editAddressBookEntryJni(JNIEnv* env, jobject instance, jint index, jboolean set_address, jstring jaddress, jboolean set_description, jstring jdescription) { + MTRACE("Java_monero_wallet_MoneroWalletFull_editAddressBookEntryJni"); + edit_address_book_entry_jni(env, instance, index, set_address, jaddress, set_description, jdescription); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_deleteAddressBookEntryJni(JNIEnv* env, jobject instance, jint index) { + MTRACE("Java_monero_wallet_MoneroWalletFull_deleteAddressBookEntryJni"); + delete_address_book_entry_jni(env, instance, index); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getPaymentUriJni(JNIEnv* env, jobject instance, jstring jconfig) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getPaymentUriJni()"); + return get_payment_uri_jni(env, instance, jconfig); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_parsePaymentUriJni(JNIEnv* env, jobject instance, jstring juri) { + MTRACE("Java_monero_wallet_MoneroWalletFull_parsePaymentUriJni()"); + return parse_payment_uri_jni(env, instance, juri); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getAttributeJni(JNIEnv* env, jobject instance, jstring jkey) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getAttribute()"); + return get_attribute_jni(env, instance, jkey); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_setAttributeJni(JNIEnv* env, jobject instance, jstring jkey, jstring jval) { + MTRACE("Java_monero_wallet_MoneroWalletFull_setAttribute()"); + set_attribute_jni(env, instance, jkey, jval); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_startMiningJni(JNIEnv* env, jobject instance, jlong num_threads, jboolean background_mining, jboolean ignore_battery) { + MTRACE("Java_monero_wallet_MoneroWalletFull_startMiningJni()"); + start_mining_jni(env, instance, num_threads, background_mining, ignore_battery); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_stopMiningJni(JNIEnv* env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletFull_startMiningJni()"); + stop_mining_jni(env, instance); +} + +JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletFull_isMultisigImportNeededJni(JNIEnv* env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletFull_isMultisigImportNeededJni"); + return is_multisig_import_needed_jni(env, instance); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_getMultisigInfoJni(JNIEnv* env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletFull_getMultisigInfoJni"); + return get_multisig_info_jni(env, instance); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_prepareMultisigJni(JNIEnv* env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletFull_prepareMultisigJni"); + return prepare_multisig_jni(env, instance); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_makeMultisigJni(JNIEnv* env, jobject instance, jobjectArray jmultisig_hexes, jint threshold, jstring jpassword) { + MTRACE("Java_monero_wallet_MoneroWalletFull_makeMultisigJni"); + return make_multisig_jni(env, instance, jmultisig_hexes, threshold, jpassword); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_exchangeMultisigKeysJni(JNIEnv* env, jobject instance, jobjectArray jmultisig_hexes, jstring jpassword) { + MTRACE("Java_monero_wallet_MoneroWalletFull_exchangeMultisigKeysJni"); + return exchange_multisig_keys_jni(env, instance, jmultisig_hexes, jpassword); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_exportMultisigHexJni(JNIEnv* env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletFull_exportMultisigHexJni"); + return export_multisig_hex_jni(env, instance); +} + +JNIEXPORT jint JNICALL Java_monero_wallet_MoneroWalletFull_importMultisigHexJni(JNIEnv* env, jobject instance, jobjectArray jmultisig_hexes) { + MTRACE("Java_monero_wallet_MoneroWalletFull_importMultisigHexJni"); + return import_multisig_hex_jni(env, instance, jmultisig_hexes); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletFull_signMultisigTxHexJni(JNIEnv* env, jobject instance, jstring jmultisig_tx_hex) { + MTRACE("Java_monero_wallet_MoneroWalletFull_signMultisigTxHexJni"); + return sign_multisig_tx_hex_jni(env, instance, jmultisig_tx_hex); +} + +JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletFull_submitMultisigTxHexJni(JNIEnv* env, jobject instance, jstring jsigned_multisig_tx_hex) { + MTRACE("Java_monero_wallet_MoneroWalletFull_submitMultisigTxHexJni"); + return submit_multisig_tx_hex_jni(env, instance, jsigned_multisig_tx_hex); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_changePasswordJni(JNIEnv* env, jobject instance, jstring jold_password, jstring jnew_password) { + MTRACE("Java_monero_wallet_MoneroWalletFull_changePasswordJni(oldPassword, newPassword)"); + change_password_jni(env, instance, jold_password, jnew_password); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_moveToJni(JNIEnv* env, jobject instance, jstring jpath, jstring jpassword) { + MTRACE("Java_monero_wallet_MoneroWalletFull_moveToJni(path, password)"); + move_to_jni(env, instance, jpath, jpassword); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_saveJni(JNIEnv* env, jobject instance) { MTRACE("Java_monero_wallet_MoneroWalletFull_saveJni(path, password)"); - monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); - try { - wallet->save(); - } catch (...) { - rethrow_cpp_exception_as_java_exception(env); - } + save_jni(env, instance); } JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletFull_closeJni(JNIEnv* env, jobject instance, jboolean save) { MTRACE("Java_monero_wallet_MoneroWalletFull_CloseJni"); - monero_wallet* wallet = get_handle(env, instance, JNI_WALLET_HANDLE); - if (save) wallet->save(); - delete wallet; - wallet = nullptr; + close_jni(env, instance, save); } JNIEXPORT jbyteArray JNICALL Java_monero_wallet_MoneroWalletFull_getKeysFileBufferJni(JNIEnv* env, jobject instance, jstring jpassword, jboolean view_only) { @@ -2232,6 +2729,509 @@ JNIEXPORT jbyteArray JNICALL Java_monero_wallet_MoneroWalletFull_getCacheFileBuf } } + +// ------------------------ LIGHT WALLET INSTANCE METHODS -------------------------- + +JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletLight_isViewOnlyJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletLight_isViewOnlyJni"); + return is_view_only_jni(env, instance); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_setDaemonConnectionJni(JNIEnv *env, jobject instance, jstring juri, jstring jusername, jstring jpassword, jstring jproxy_uri) { + MTRACE("Java_monero_wallet_MoneroWalletLight_setDaemonConnectionJni"); + set_daemon_connection_jni(env, instance, juri, jusername, jpassword, jproxy_uri); +} + +JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletLight_getDaemonConnectionJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getDaemonConnectionJni()"); + return get_daemon_connection_jni(env, instance); +} + +JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletLight_isConnectedToDaemonJni(JNIEnv* env, jobject instance) { + return is_connected_to_daemon_jni(env, instance); +} + +JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletLight_isDaemonSyncedJni(JNIEnv* env, jobject instance) { + return is_daemon_synced_jni(env, instance); +} + +JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletLight_isSyncedJni(JNIEnv* env, jobject instance) { + return is_synced_jni(env, instance); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getVersionJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getVersionJni"); + return get_version_jni(env, instance); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getPathJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getPathJni"); + return get_path_jni(env, instance); +} + +JNIEXPORT jint JNICALL Java_monero_wallet_MoneroWalletLight_getNetworkTypeJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getNetworkTypeJni"); + return get_network_type_jni(env, instance); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getSeedJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getSeedJni"); + return get_seed_jni(env, instance); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getSeedLanguageJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getSeedLanguageJni"); + return get_seed_language_jni(env, instance); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getPublicViewKeyJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getPublicViewKeyJni"); + return get_public_view_key_jni(env, instance); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getPrivateViewKeyJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getPrivateViewKeyJni"); + return get_private_view_key_jni(env, instance); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getPublicSpendKeyJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getPublicSpendKeyJni"); + return get_public_spend_key_jni(env, instance); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getPrivateSpendKeyJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getPrivateSpendKeyJni"); + return get_private_spend_key_jni(env, instance); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getAddressJni(JNIEnv *env, jobject instance, jint account_idx, jint subaddress_idx) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getAddressJni"); + return get_address_jni(env, instance, account_idx, subaddress_idx); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getAddressIndexJni(JNIEnv *env, jobject instance, jstring jaddress) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getAddressIndexJni"); + return get_address_index_jni(env, instance, jaddress); +} + +/** + * Only one listener needs to subscribe over JNI, so this removes the previously registered listener + * and registers the new listener. + */ +JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletLight_setListenerJni(JNIEnv *env, jobject instance, jobject jlistener) { + MTRACE("Java_monero_wallet_MoneroWalletLight_setListenerJni"); + return set_listener_jni(env, instance, jlistener); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getIntegratedAddressJni(JNIEnv *env, jobject instance, jstring jstandard_address, jstring jpayment_id) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getIntegratedAddressJni"); + return get_integrated_address_jni(env, instance, jstandard_address, jpayment_id); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_decodeIntegratedAddressJni(JNIEnv *env, jobject instance, jstring jintegrated_address) { + MTRACE("Java_monero_wallet_MoneroWalletLight_decodeIntegratedAddressJni"); + return decode_integrated_address_jni(env, instance, jintegrated_address); +} + +JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletLight_getHeightJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getHeightJni"); + return get_height_jni(env, instance); +} + +JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletLight_getChainHeightJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getChainHeightJni"); + return get_chain_height_jni(env, instance); +} + +JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletLight_getRestoreHeightJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getRestoreHeightJni"); + return get_restore_height_jni(env, instance); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_setRestoreHeightJni(JNIEnv *env, jobject instance, jlong restore_height) { + MTRACE("Java_monero_wallet_MoneroWalletLight_setRestoreHeightJni"); + set_restore_height_jni(env, instance, restore_height); +} + +JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletLight_getDaemonHeightJni(JNIEnv* env, jobject instance) { + return get_daemon_height_jni(env, instance); +} + +JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletLight_getDaemonMaxPeerHeightJni(JNIEnv* env, jobject instance) { + return get_daemon_max_peer_height_jni(env, instance); +} + +JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletLight_getHeightByDateJni(JNIEnv* env, jobject instance, jint year, jint month, jint day) { + return get_height_by_date_jni(env, instance, year, month, day); +} + +JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletLight_syncJni(JNIEnv *env, jobject instance, jlong start_height) { + MTRACE("Java_monero_wallet_MoneroWalletLight_syncJni"); + return sync_jni(env, instance, start_height); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_startSyncingJni(JNIEnv *env, jobject instance, jlong sync_period_in_ms) { + MTRACE("Java_monero_wallet_MoneroWalletLight_startSyncingJni"); + start_syncing_jni(env, instance, sync_period_in_ms); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_stopSyncingJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletLight_stopSyncingJni"); + stop_syncing_jni(env, instance); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_scanTxsJni(JNIEnv* env, jobject instance, jobjectArray jtx_hashes) { + MTRACE("Java_monero_wallet_MoneroWalletLight_scanTxsJni"); + scan_txs_jni(env, instance, jtx_hashes); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_rescanSpentJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletLight_rescanSpentJni"); + rescan_spent_jni(env, instance); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_rescanBlockchainJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletLight_rescanBlockchainJni"); + rescan_blockchain_jni(env, instance); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getBalanceWalletJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getBalanceWalletJni"); + return get_balance_wallet_jni(env, instance); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getBalanceAccountJni(JNIEnv *env, jobject instance, jint account_idx) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getBalanceAccountJni"); + return get_balance_account_jni(env, instance, account_idx); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getBalanceSubaddressJni(JNIEnv *env, jobject instance, jint account_idx, jint subaddress_idx) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getBalanceSubaddressJni"); + return get_balance_subaddress_jni(env, instance, account_idx, subaddress_idx); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getUnlockedBalanceWalletJni(JNIEnv *env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getUnlockedBalanceWalletJni"); + return get_unlocked_balance_wallet_jni(env, instance); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getUnlockedBalanceAccountJni(JNIEnv *env, jobject instance, jint account_idx) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getUnlockedBalanceAccountJni"); + return get_unlocked_balance_account_jni(env, instance, account_idx); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getUnlockedBalanceSubaddressJni(JNIEnv *env, jobject instance, jint account_idx, jint subaddress_idx) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getUnlockedBalanceSubaddressJni"); + return get_unlocked_balance_subaddress_jni(env, instance, account_idx, subaddress_idx); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getAccountsJni(JNIEnv* env, jobject instance, jboolean include_subaddresses, jstring jtag) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getAccountsJni"); + return get_accounts_jni(env, instance, include_subaddresses, jtag); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getAccountJni(JNIEnv* env, jobject instance, jint account_idx, jboolean include_subaddresses) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getAccountJni"); + return get_account_jni(env, instance, account_idx, include_subaddresses); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_createAccountJni(JNIEnv* env, jobject instance, jstring jlabel) { + MTRACE("Java_monero_wallet_MoneroWalletLight_createAccountJni"); + return create_account_jni(env, instance, jlabel); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getSubaddressesJni(JNIEnv* env, jobject instance, jint account_idx, jintArray jsubaddressIndices) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getSubaddressesJni"); + return get_subaddresses_jni(env, instance, account_idx, jsubaddressIndices); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_createSubaddressJni(JNIEnv* env, jobject instance, jint account_idx, jstring jlabel) { + MTRACE("Java_monero_wallet_MoneroWalletLight_createSubaddressJni"); + return create_subaddress_jni(env, instance, account_idx, jlabel); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_setSubaddressLabelJni(JNIEnv* env, jobject instance, jint account_idx, jint subaddress_idx, jstring jlabel) { + MTRACE("Java_monero_wallet_MoneroWalletLight_setSubaddressLabelJni"); + set_subaddress_label_jni(env, instance, account_idx, subaddress_idx, jlabel); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getTxsJni(JNIEnv* env, jobject instance, jstring jtx_query) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getTxsJni"); + return get_txs_jni(env, instance, jtx_query); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getTransfersJni(JNIEnv* env, jobject instance, jstring jtransfer_query) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getTransfersJni"); + return get_transfers_jni(env, instance, jtransfer_query); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getOutputsJni(JNIEnv* env, jobject instance, jstring joutput_query) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getOutputsJni"); + return get_outputs_jni(env, instance, joutput_query); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_exportOutputsJni(JNIEnv* env, jobject instance, jboolean all) { + MTRACE("Java_monero_wallet_MoneroWalletLight_exportOutputsJni()"); + return export_outputs_jni(env, instance, all); +} + +JNIEXPORT jint JNICALL Java_monero_wallet_MoneroWalletLight_importOutputsJni(JNIEnv* env, jobject instance, jstring joutputs_hex) { + MTRACE("Java_monero_wallet_MoneroWalletLight_exportOutputsJni()"); + return import_outputs_jni(env, instance, joutputs_hex); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_exportKeyImagesJni(JNIEnv* env, jobject instance, jboolean all) { + MTRACE("Java_monero_wallet_MoneroWalletLight_exportKeyImagesJni"); + return export_key_images_jni(env, instance, all); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_importKeyImagesJni(JNIEnv* env, jobject instance, jstring jkey_images_json) { + MTRACE("Java_monero_wallet_MoneroWalletLight_importKeyImagesJni"); + return import_key_images_jni(env, instance, jkey_images_json); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_freezeOutputJni(JNIEnv* env, jobject instance, jstring jkey_image) { + MTRACE("Java_monero_wallet_MoneroWalletLight_freezeJni"); + freeze_output_jni(env, instance, jkey_image); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_thawOutputJni(JNIEnv* env, jobject instance, jstring jkey_image) { + MTRACE("Java_monero_wallet_MoneroWalletLight_thawJni"); + thaw_output_jni(env, instance, jkey_image); +} + +JNIEXPORT bool JNICALL Java_monero_wallet_MoneroWalletLight_isOutputFrozenJni(JNIEnv* env, jobject instance, jstring jkey_image) { + MTRACE("Java_monero_wallet_MoneroWalletLight_isFrozenJni"); + return is_output_frozen_jni(env, instance, jkey_image); +} + +JNIEXPORT jint JNICALL Java_monero_wallet_MoneroWalletLight_getDefaultFeePriorityJni(JNIEnv* env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getDefaultFeePriorityJni"); + return get_default_fee_priority_jni(env, instance); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_createTxsJni(JNIEnv* env, jobject instance, jstring jconfig) { + MTRACE("Java_monero_wallet_MoneroWalletLight_createTxsJni(request)"); + return create_txs_jni(env, instance, jconfig); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_sweepUnlockedJni(JNIEnv* env, jobject instance, jstring jconfig) { + MTRACE("Java_monero_wallet_MoneroWalletLight_sweepUnlockedJni(config)"); + return sweep_unlocked_jni(env, instance, jconfig); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_sweepOutputJni(JNIEnv* env, jobject instance, jstring jconfig) { + MTRACE("Java_monero_wallet_MoneroWalletLight_sweepOutputJni(request)"); + return sweep_output_jni(env, instance, jconfig); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_sweepDustJni(JNIEnv* env, jobject instance, jboolean relay) { + MTRACE("Java_monero_wallet_MoneroWalletLight_sweepDustJni(request)"); + return sweep_dust_jni(env, instance, relay); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_describeTxSetJni(JNIEnv* env, jobject instance, jstring jtx_set_json) { + MTRACE("Java_monero_wallet_MoneroWalletLight_describeTxSetJson(tx_set_json)"); + return describe_tx_set_jni(env, instance, jtx_set_json); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_signTxsJni(JNIEnv* env, jobject instance, jstring junsigned_tx_hex) { + MTRACE("Java_monero_wallet_MoneroWalletLight_signTxsJni()"); + return sign_txs_jni(env, instance, junsigned_tx_hex); +} + +JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletLight_submitTxsJni(JNIEnv* env, jobject instance, jstring jsigned_tx_hex) { + MTRACE("Java_monero_wallet_MoneroWalletLight_submitTxsJni()"); + return submit_txs_jni(env, instance, jsigned_tx_hex); +} + +JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletLight_relayTxsJni(JNIEnv* env, jobject instance, jobjectArray jtx_metadatas) { + MTRACE("Java_monero_wallet_MoneroWalletLight_relayTxsJni"); + return relay_txs_jni(env, instance, jtx_metadatas); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_signMessageJni(JNIEnv* env, jobject instance, jstring jmsg, jint message_signature_type, jint account_idx, jint subaddress_idx) { + MTRACE("Java_monero_wallet_MoneroWalletLight_signMessageJni"); + return sign_message_jni(env, instance, jmsg, message_signature_type, account_idx, subaddress_idx); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_verifyMessageJni(JNIEnv* env, jobject instance, jstring jmsg, jstring jaddress, jstring jsignature) { + MTRACE("Java_monero_wallet_MoneroWalletLight_verifyMessageJni"); + return verify_message_jni(env, instance, jmsg, jaddress, jsignature); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getTxKeyJni(JNIEnv* env, jobject instance, jstring jtx_hash) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getTxKeyJniJni"); + return get_tx_key_jni(env, instance, jtx_hash); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_checkTxKeyJni(JNIEnv* env, jobject instance, jstring jtx_hash, jstring jtx_key, jstring jaddress) { + MTRACE("Java_monero_wallet_MoneroWalletLight_checktx_keyJni"); + return check_tx_key_jni(env, instance, jtx_hash, jtx_key, jaddress); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getTxProofJni(JNIEnv* env, jobject instance, jstring jtx_hash, jstring jaddress, jstring jmessage) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getTxProofJni"); + return get_tx_proof_jni(env, instance, jtx_hash, jaddress, jmessage); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_checkTxProofJni(JNIEnv* env, jobject instance, jstring jtx_hash, jstring jaddress, jstring jmessage, jstring jsignature) { + MTRACE("Java_monero_wallet_MoneroWalletLight_checkTxProofJni"); + return check_tx_proof_jni(env, instance, jtx_hash, jaddress, jmessage, jsignature); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getSpendProofJni(JNIEnv* env, jobject instance, jstring jtx_hash, jstring jmessage) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getSpendProofJni"); + return get_spend_proof_jni(env, instance, jtx_hash, jmessage); +} + +JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletLight_checkSpendProofJni(JNIEnv* env, jobject instance, jstring jtx_hash, jstring jmessage, jstring jsignature) { + MTRACE("Java_monero_wallet_MoneroWalletLight_checkSpendProofJni"); + return check_spend_proof_jni(env, instance, jtx_hash, jmessage, jsignature); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getReserveProofWalletJni(JNIEnv* env, jobject instance, jstring jmessage) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getReserveProofWalletJni"); + return get_reserve_proof_wallet_jni(env, instance, jmessage); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getReserveProofAccountJni(JNIEnv* env, jobject instance, jint account_idx, jstring jamount_str, jstring jmessage) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getReserveProofWalletJni"); + return get_reserve_proof_account_jni(env, instance, account_idx, jamount_str, jmessage); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_checkReserveProofJni(JNIEnv* env, jobject instance, jstring jaddress, jstring jmessage, jstring jsignature) { + MTRACE("Java_monero_wallet_MoneroWalletLight_checkReserveProofAccountJni"); + return check_reserve_proof_jni(env, instance, jaddress, jmessage, jsignature); +} + +JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletLight_getTxNotesJni(JNIEnv* env, jobject instance, jobjectArray jtx_hashes) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getTxNotesJni"); + return get_tx_notes_jni(env, instance, jtx_hashes); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_setTxNotesJni(JNIEnv* env, jobject instance, jobjectArray jtx_hashes, jobjectArray jtx_notes) { + MTRACE("Java_monero_wallet_MoneroWalletLight_setTxNotesJni"); + set_tx_notes_jni(env, instance, jtx_hashes, jtx_notes); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getAddressBookEntriesJni(JNIEnv* env, jobject instance, jintArray jindices) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getAddressBookEntriesJni"); + return get_address_book_entries_jni(env, instance, jindices); +} + +// TODO: return jlong for uint64_t +JNIEXPORT jint JNICALL Java_monero_wallet_MoneroWalletLight_addAddressBookEntryJni(JNIEnv* env, jobject instance, jstring jaddress, jstring jdescription) { + MTRACE("Java_monero_wallet_MoneroWalletLight_addAddressBookEntryJni"); + return add_address_book_entry_jni(env, instance, jaddress, jdescription); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_editAddressBookEntryJni(JNIEnv* env, jobject instance, jint index, jboolean set_address, jstring jaddress, jboolean set_description, jstring jdescription) { + MTRACE("Java_monero_wallet_MoneroWalletLight_editAddressBookEntryJni"); + edit_address_book_entry_jni(env, instance, index, set_address, jaddress, set_description, jdescription); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_deleteAddressBookEntryJni(JNIEnv* env, jobject instance, jint index) { + MTRACE("Java_monero_wallet_MoneroWalletLight_deleteAddressBookEntryJni"); + delete_address_book_entry_jni(env, instance, index); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getPaymentUriJni(JNIEnv* env, jobject instance, jstring jconfig) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getPaymentUriJni()"); + return get_payment_uri_jni(env, instance, jconfig); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_parsePaymentUriJni(JNIEnv* env, jobject instance, jstring juri) { + MTRACE("Java_monero_wallet_MoneroWalletLight_parsePaymentUriJni()"); + return parse_payment_uri_jni(env, instance, juri); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getAttributeJni(JNIEnv* env, jobject instance, jstring jkey) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getAttribute()"); + return get_attribute_jni(env, instance, jkey); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_setAttributeJni(JNIEnv* env, jobject instance, jstring jkey, jstring jval) { + MTRACE("Java_monero_wallet_MoneroWalletLight_setAttribute()"); + set_attribute_jni(env, instance, jkey, jval); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_startMiningJni(JNIEnv* env, jobject instance, jlong num_threads, jboolean background_mining, jboolean ignore_battery) { + MTRACE("Java_monero_wallet_MoneroWalletLight_startMiningJni()"); + start_mining_jni(env, instance, num_threads, background_mining, ignore_battery); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_stopMiningJni(JNIEnv* env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletLight_startMiningJni()"); + stop_mining_jni(env, instance); +} + +JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletLight_isMultisigImportNeededJni(JNIEnv* env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletLight_isMultisigImportNeededJni"); + return is_multisig_import_needed_jni(env, instance); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getMultisigInfoJni(JNIEnv* env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletLight_getMultisigInfoJni"); + return get_multisig_info_jni(env, instance); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_prepareMultisigJni(JNIEnv* env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletLight_prepareMultisigJni"); + return prepare_multisig_jni(env, instance); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_makeMultisigJni(JNIEnv* env, jobject instance, jobjectArray jmultisig_hexes, jint threshold, jstring jpassword) { + MTRACE("Java_monero_wallet_MoneroWalletLight_makeMultisigJni"); + return make_multisig_jni(env, instance, jmultisig_hexes, threshold, jpassword); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_exchangeMultisigKeysJni(JNIEnv* env, jobject instance, jobjectArray jmultisig_hexes, jstring jpassword) { + MTRACE("Java_monero_wallet_MoneroWalletLight_exchangeMultisigKeysJni"); + return exchange_multisig_keys_jni(env, instance, jmultisig_hexes, jpassword); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_exportMultisigHexJni(JNIEnv* env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletLight_exportMultisigHexJni"); + return export_multisig_hex_jni(env, instance); +} + +JNIEXPORT jint JNICALL Java_monero_wallet_MoneroWalletLight_importMultisigHexJni(JNIEnv* env, jobject instance, jobjectArray jmultisig_hexes) { + MTRACE("Java_monero_wallet_MoneroWalletLight_importMultisigHexJni"); + return import_multisig_hex_jni(env, instance, jmultisig_hexes); +} + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_signMultisigTxHexJni(JNIEnv* env, jobject instance, jstring jmultisig_tx_hex) { + MTRACE("Java_monero_wallet_MoneroWalletLight_signMultisigTxHexJni"); + return sign_multisig_tx_hex_jni(env, instance, jmultisig_tx_hex); +} + +JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletLight_submitMultisigTxHexJni(JNIEnv* env, jobject instance, jstring jsigned_multisig_tx_hex) { + MTRACE("Java_monero_wallet_MoneroWalletLight_submitMultisigTxHexJni"); + return submit_multisig_tx_hex_jni(env, instance, jsigned_multisig_tx_hex); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_changePasswordJni(JNIEnv* env, jobject instance, jstring jold_password, jstring jnew_password) { + MTRACE("Java_monero_wallet_MoneroWalletLight_changePasswordJni(oldPassword, newPassword)"); + change_password_jni(env, instance, jold_password, jnew_password); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_moveToJni(JNIEnv* env, jobject instance, jstring jpath, jstring jpassword) { + MTRACE("Java_monero_wallet_MoneroWalletLight_moveToJni(path, password)"); + move_to_jni(env, instance, jpath, jpassword); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_saveJni(JNIEnv* env, jobject instance) { + MTRACE("Java_monero_wallet_MoneroWalletLight_saveJni(path, password)"); + save_jni(env, instance); +} + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_closeJni(JNIEnv* env, jobject instance, jboolean save) { + MTRACE("Java_monero_wallet_MoneroWalletLight_CloseJni"); + close_jni(env, instance, save); +} + + #ifdef __cplusplus } #endif diff --git a/src/main/cpp/monero_jni_bridge.h b/src/main/cpp/monero_jni_bridge.h index ed25ca85..1df25e09 100644 --- a/src/main/cpp/monero_jni_bridge.h +++ b/src/main/cpp/monero_jni_bridge.h @@ -57,7 +57,7 @@ JNIEXPORT void JNICALL Java_monero_common_MoneroUtils_setLogLevelJni(JNIEnv *, j JNIEXPORT void JNICALL Java_monero_common_MoneroUtils_configureLoggingJni(JNIEnv *, jclass, jstring jpath, jboolean); -// --------------------------- STATIC WALLET UTILS ---------------------------- +// --------------------------- STATIC FULL WALLET UTILS ---------------------------- JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletFull_walletExistsJni(JNIEnv *, jclass, jstring); @@ -69,7 +69,17 @@ JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletFull_createWalletJni(JNIE JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletFull_getSeedLanguagesJni(JNIEnv *, jclass); -// ----------------------------- INSTANCE METHODS ----------------------------- +// --------------------------- STATIC LIGHT WALLET UTILS ---------------------------- + +JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletLight_walletExistsJni(JNIEnv *, jclass, jstring, jstring, jstring); + +JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletLight_openWalletJni(JNIEnv *, jclass, jstring); + +JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletLight_createWalletJni(JNIEnv *, jclass, jstring); + +JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletLight_getSeedLanguagesJni(JNIEnv *, jclass); + +// ----------------------------- FULL WALLET INSTANCE METHODS ----------------------------- JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletFull_isViewOnlyJni(JNIEnv *, jobject); @@ -273,6 +283,204 @@ JNIEXPORT jbyteArray JNICALL Java_monero_wallet_MoneroWalletFull_getKeysFileBuff JNIEXPORT jbyteArray JNICALL Java_monero_wallet_MoneroWalletFull_getCacheFileBufferJni(JNIEnv *, jobject); +// ----------------------------- LIGHT WALLET INSTANCE METHODS ----------------------------- + +JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletLight_isViewOnlyJni(JNIEnv *, jobject); + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_setDaemonConnectionJni(JNIEnv *, jobject, jstring, jstring, jstring, jstring); + +JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletLight_getDaemonConnectionJni(JNIEnv *, jobject); + +JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletLight_isConnectedToDaemonJni(JNIEnv *, jobject); + +JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletLight_isDaemonSyncedJni(JNIEnv *, jobject); + +JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletLight_isSyncedJni(JNIEnv *, jobject); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getVersionJni(JNIEnv *, jobject); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getPathJni(JNIEnv *, jobject); + +JNIEXPORT jint JNICALL Java_monero_wallet_MoneroWalletLight_getNetworkTypeJni(JNIEnv *, jobject); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getSeedJni(JNIEnv *, jobject); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getSeedLanguageJni(JNIEnv *, jobject); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getPublicViewKeyJni(JNIEnv *, jobject); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getPrivateViewKeyJni(JNIEnv *, jobject); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getPublicSpendKeyJni(JNIEnv *, jobject); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getPrivateSpendKeyJni(JNIEnv *, jobject); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getAddressJni(JNIEnv *, jobject, jint, jint); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getAddressIndexJni(JNIEnv *, jobject, jstring); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getIntegratedAddressJni(JNIEnv *, jobject, jstring, jstring); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_decodeIntegratedAddressJni(JNIEnv *, jobject, jstring); + +JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletLight_getHeightJni(JNIEnv *, jobject); + +JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletLight_getRestoreHeightJni(JNIEnv *, jobject); + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_setRestoreHeightJni(JNIEnv *, jobject, jlong); + +JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletLight_getDaemonHeightJni(JNIEnv *, jobject); + +JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletLight_getDaemonMaxPeerHeightJni(JNIEnv *, jobject); + +JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletLight_getHeightByDateJni(JNIEnv *, jobject, jint, jint, jint); + +JNIEXPORT jlong JNICALL Java_monero_wallet_MoneroWalletLight_setListenerJni(JNIEnv *, jobject, jobject); + +JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletLight_syncJni(JNIEnv *, jobject, jlong); + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_startSyncingJni(JNIEnv *, jobject, jlong); + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_stopSyncingJni(JNIEnv *, jobject); + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_scanTxsJni(JNIEnv *, jobject, jobjectArray); + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_rescanSpentJni(JNIEnv *, jobject); + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_rescanBlockchainJni(JNIEnv *, jobject); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getBalanceWalletJni(JNIEnv *, jobject); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getBalanceAccountJni(JNIEnv *, jobject, jint); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getBalanceSubaddressJni(JNIEnv *, jobject, jint, jint); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getUnlockedBalanceWalletJni(JNIEnv *, jobject); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getUnlockedBalanceAccountJni(JNIEnv *, jobject, jint); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getUnlockedBalanceSubaddressJni(JNIEnv *, jobject, jint, jint); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getAccountsJni(JNIEnv *, jobject, jboolean, jstring); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getAccountJni(JNIEnv *, jobject, jint, jboolean); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_createAccountJni(JNIEnv *, jobject, jstring); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getSubaddressesJni(JNIEnv *, jobject, jint, jintArray); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_createSubaddressJni(JNIEnv *, jobject, jint, jstring); + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_setSubaddressLabelJni(JNIEnv *, jobject, jint, jint, jstring); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getTxsJni(JNIEnv *, jobject, jstring); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getTransfersJni(JNIEnv *, jobject, jstring); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getOutputsJni(JNIEnv *, jobject, jstring); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_exportKeyImagesJni(JNIEnv *, jobject, jboolean); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_importKeyImagesJni(JNIEnv *, jobject, jstring); + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_freezeOutputJni(JNIEnv *, jobject, jstring); + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_thawOutputJni(JNIEnv *, jobject, jstring); + +JNIEXPORT bool JNICALL Java_monero_wallet_MoneroWalletLight_isOutputFrozenJni(JNIEnv *, jobject, jstring); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_createTxsJni(JNIEnv *, jobject, jstring); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_sweepUnlockedJni(JNIEnv *, jobject, jstring); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_sweepOutputJni(JNIEnv *, jobject, jstring); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_sweepDustJni(JNIEnv *, jobject, jboolean); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_describeTxSetJni(JNIEnv *, jobject, jstring); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_signTxsJni(JNIEnv *, jobject, jstring); + +JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletLight_submitTxsJni(JNIEnv *, jobject, jstring); + +JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletLight_relayTxsJni(JNIEnv *, jobject, jobjectArray); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_signMessageJni(JNIEnv *, jobject, jstring, jint, jint, jint); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_verifyMessageJni(JNIEnv *, jobject, jstring, jstring, jstring); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getTxKeyJni(JNIEnv *, jobject, jstring); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_checkTxKeyJni(JNIEnv *, jobject, jstring, jstring, jstring); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getTxProofJni(JNIEnv *, jobject, jstring, jstring, jstring); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_checkTxProofJni(JNIEnv *, jobject, jstring, jstring, jstring, jstring); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getSpendProofJni(JNIEnv *, jobject, jstring, jstring); + +JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletLight_checkSpendProofJni(JNIEnv *, jobject, jstring, jstring, jstring); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getReserveProofWalletJni(JNIEnv *, jobject, jstring); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getReserveProofAccountJni(JNIEnv *, jobject, jint, jstring, jstring); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_checkReserveProofJni(JNIEnv *, jobject, jstring, jstring, jstring); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getPaymentUriJni(JNIEnv *, jobject, jstring); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_parsePaymentUriJni(JNIEnv *, jobject, jstring); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_exportOutputsJni(JNIEnv *, jobject, jboolean); + +JNIEXPORT jint JNICALL Java_monero_wallet_MoneroWalletLight_importOutputsJni(JNIEnv *, jobject, jstring); + +JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletLight_getTxNotesJni(JNIEnv *, jobject, jobjectArray); + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_setTxNotesJni(JNIEnv *, jobject, jobjectArray, jobjectArray); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getAddressBookEntriesJni(JNIEnv *, jobject, jintArray); + +JNIEXPORT jint JNICALL Java_monero_wallet_MoneroWalletLight_addAddressBookEntryJni(JNIEnv *, jobject, jstring, jstring); + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_editAddressBookEntryJni(JNIEnv *, jobject, jint, jboolean, jstring, jboolean, jstring); + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_deleteAddressBookEntryJni(JNIEnv *, jobject, jint); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getAttributeJni(JNIEnv *, jobject, jstring); + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_setAttributeJni(JNIEnv *, jobject, jstring, jstring); + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_startMiningJni(JNIEnv *, jobject, jlong, jboolean, jboolean); + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_stopMiningJni(JNIEnv *, jobject); + +JNIEXPORT jboolean JNICALL Java_monero_wallet_MoneroWalletLight_isMultisigImportNeededJni(JNIEnv *, jobject); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_getMultisigInfoJni(JNIEnv *, jobject); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_prepareMultisigJni(JNIEnv *, jobject); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_makeMultisigJni(JNIEnv *, jobject, jobjectArray, jint, jstring); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_exchangeMultisigKeysJni(JNIEnv *, jobject, jobjectArray, jstring); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_exportMultisigHexJni(JNIEnv *, jobject); + +JNIEXPORT jint JNICALL Java_monero_wallet_MoneroWalletLight_importMultisigHexJni(JNIEnv *, jobject, jobjectArray); + +JNIEXPORT jstring JNICALL Java_monero_wallet_MoneroWalletLight_signMultisigTxHexJni(JNIEnv *, jobject, jstring); + +JNIEXPORT jobjectArray JNICALL Java_monero_wallet_MoneroWalletLight_submitMultisigTxHexJni(JNIEnv *, jobject, jstring); + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_changePasswordJniJni(JNIEnv *, jobject, jstring, jstring); + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_moveToJni(JNIEnv *, jobject, jstring, jstring); + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_saveJni(JNIEnv *, jobject); + +JNIEXPORT void JNICALL Java_monero_wallet_MoneroWalletLight_closeJni(JNIEnv *, jobject, jboolean); + #ifdef __cplusplus } #endif diff --git a/src/main/java/monero/wallet/MoneroWalletFull.java b/src/main/java/monero/wallet/MoneroWalletFull.java index 728052da..318d0d52 100644 --- a/src/main/java/monero/wallet/MoneroWalletFull.java +++ b/src/main/java/monero/wallet/MoneroWalletFull.java @@ -22,74 +22,26 @@ package monero.wallet; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.core.type.TypeReference; -import common.utils.GenUtils; import common.utils.JsonUtils; -import java.math.BigInteger; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.logging.Logger; import monero.common.MoneroError; import monero.common.MoneroRpcConnection; -import monero.common.MoneroUtils; -import monero.daemon.model.MoneroBlock; -import monero.daemon.model.MoneroKeyImage; import monero.daemon.model.MoneroNetworkType; -import monero.daemon.model.MoneroTx; -import monero.daemon.model.MoneroVersion; -import monero.wallet.model.MoneroAccount; -import monero.wallet.model.MoneroAccountTag; -import monero.wallet.model.MoneroAddressBookEntry; -import monero.wallet.model.MoneroCheckReserve; -import monero.wallet.model.MoneroCheckTx; -import monero.wallet.model.MoneroIncomingTransfer; -import monero.wallet.model.MoneroIntegratedAddress; -import monero.wallet.model.MoneroKeyImageImportResult; -import monero.wallet.model.MoneroMessageSignatureResult; -import monero.wallet.model.MoneroMessageSignatureType; -import monero.wallet.model.MoneroMultisigInfo; -import monero.wallet.model.MoneroMultisigInitResult; -import monero.wallet.model.MoneroMultisigSignResult; -import monero.wallet.model.MoneroOutputQuery; -import monero.wallet.model.MoneroOutputWallet; -import monero.wallet.model.MoneroSubaddress; -import monero.wallet.model.MoneroSyncResult; -import monero.wallet.model.MoneroTransfer; -import monero.wallet.model.MoneroTransferQuery; -import monero.wallet.model.MoneroTxConfig; -import monero.wallet.model.MoneroTxPriority; -import monero.wallet.model.MoneroTxQuery; -import monero.wallet.model.MoneroTxSet; -import monero.wallet.model.MoneroTxWallet; import monero.wallet.model.MoneroWalletConfig; -import monero.wallet.model.MoneroWalletListenerI; /** * Implements a Monero wallet using fully client-side JNI bindings to monero-project's wallet2 in C++. */ -public class MoneroWalletFull extends MoneroWalletDefault { +public class MoneroWalletFull extends MoneroWalletJni { // ----------------------------- PRIVATE SETUP ------------------------------ - - // try to load jni bindings - static { - MoneroUtils.tryLoadNativeLibrary(); - } // class variables - private static final Logger LOGGER = Logger.getLogger(MoneroWalletFull.class.getName()); - private static final long DEFAULT_SYNC_PERIOD_IN_MS = 10000; // default period betweeen syncs in ms - - // instance variables - private long jniWalletHandle; // memory address of the wallet in c++; this variable is read directly by name in c++ - private long jniListenerHandle; // memory address of the wallet listener in c++; this variable is read directly by name in c++ - private WalletJniListener jniListener; // receives notifications from jni c++ + protected static final Logger LOGGER = Logger.getLogger(MoneroWalletFull.class.getName()); + private String password; /** @@ -98,9 +50,8 @@ public class MoneroWalletFull extends MoneroWalletDefault { * @param jniWalletHandle memory address of the wallet in c++ * @param password password of the wallet instance */ - private MoneroWalletFull(long jniWalletHandle, String password) { - this.jniWalletHandle = jniWalletHandle; - this.jniListener = new WalletJniListener(); + protected MoneroWalletFull(long jniWalletHandle, String password) { + super(jniWalletHandle); this.password = password; } @@ -328,7 +279,7 @@ private static MoneroWalletFull createWalletRandom(MoneroWalletConfig config) { return new MoneroWalletFull(jniWalletHandle, config.getPassword()); } - private static String serializeWalletConfig(MoneroWalletConfig config) { + protected static String serializeWalletConfig(MoneroWalletConfig config) { Map configMap = JsonUtils.toMap(config); configMap.put("networkType", config.getNetworkType().ordinal()); return JsonUtils.serialize(configMap); @@ -345,78 +296,6 @@ public static List getSeedLanguages() { // ------------ WALLET METHODS SPECIFIC TO JNI IMPLEMENTATION --------------- - /** - * Get the maximum height of the peers the wallet's daemon is connected to. - * - * @return the maximum height of the peers the wallet's daemon is connected to - */ - public long getDaemonMaxPeerHeight() { - assertNotClosed(); - try { - return getDaemonMaxPeerHeightJni(); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - - /** - * Indicates if the wallet's daemon is synced with the network. - * - * @return true if the daemon is synced with the network, false otherwise - */ - public boolean isDaemonSynced() { - assertNotClosed(); - try { - return isDaemonSyncedJni(); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - - /** - * Indicates if the wallet is synced with the daemon. - * - * @return true if the wallet is synced with the daemon, false otherwise - */ - public boolean isSynced() { - assertNotClosed(); - try { - return isSyncedJni(); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - - /** - * Get the wallet's network type (mainnet, testnet, or stagenet). - * - * @return the wallet's network type - */ - public MoneroNetworkType getNetworkType() { - assertNotClosed(); - return MoneroNetworkType.values()[getNetworkTypeJni()]; - } - - /** - * Get the height of the first block that the wallet scans. - * - * @return the height of the first block that the wallet scans - */ - public long getRestoreHeight() { - assertNotClosed(); - return getRestoreHeightJni(); - } - - /** - * Set the height of the first block that the wallet scans. - * - * @param syncHeight is the height of the first block that the wallet scans - */ - public void setRestoreHeight(long syncHeight) { - assertNotClosed(); - setRestoreHeightJni(syncHeight); - } - /** * Move the wallet from its current path to the given path. * @@ -428,1462 +307,349 @@ public void moveTo(String path) { } // -------------------------- COMMON WALLET METHODS ------------------------- - - @Override - public void addListener(MoneroWalletListenerI listener) { - assertNotClosed(); - super.addListener(listener); - refreshListening(); - } - - @Override - public void removeListener(MoneroWalletListenerI listener) { - assertNotClosed(); - super.removeListener(listener); - refreshListening(); - } - - @Override - public Set getListeners() { - assertNotClosed(); - return super.getListeners(); - } - - @Override - public boolean isViewOnly() { - assertNotClosed(); - return isViewOnlyJni(); - } - - @Override - public void setDaemonConnection(MoneroRpcConnection daemonConnection) { - assertNotClosed(); - if (daemonConnection == null) setDaemonConnectionJni("", "", "", ""); - else { - try { - setDaemonConnectionJni(daemonConnection.getUri() == null ? "" : daemonConnection.getUri().toString(), daemonConnection.getUsername(), daemonConnection.getPassword(), daemonConnection.getProxyUri()); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } + + /** + * Get the wallet's keys and cache data. + * + * @return the keys and cache data, respectively + */ + public synchronized byte[][] getData() { + return new byte[][] { getKeysFileBufferJni(password, isViewOnly()), getCacheFileBufferJni() }; } @Override - public MoneroRpcConnection getDaemonConnection() { - assertNotClosed(); + public void changePassword(String oldPassword, String newPassword) { try { - String[] vals = getDaemonConnectionJni(); - return vals == null ? null : new MoneroRpcConnection(vals[0], vals[1], vals[2]); + if (!password.equals(oldPassword)) throw new RuntimeException("Invalid original password."); + changePasswordJni(oldPassword, newPassword); + password = newPassword; } catch (Exception e) { throw new MoneroError(e.getMessage()); } } - + @Override - public boolean isConnectedToDaemon() { - assertNotClosed(); + public void close(boolean save) { + if (isClosed) return; // closing a closed wallet has no effect + super.close(save); + password = null; + refreshListening(); try { - return isConnectedToDaemonJni(); + closeJni(save); } catch (Exception e) { throw new MoneroError(e.getMessage()); } } + + // ------------------------------ NATIVE METHODS ---------------------------- + + protected native static boolean walletExistsJni(String path); + + protected native static long openWalletJni(String path, String password, int networkType); + + protected native static long openWalletDataJni(String password, int networkType, byte[] keysData, byte[] cacheData); + + protected native static long createWalletJni(String walletConfigJson); @Override - public MoneroVersion getVersion() { - assertNotClosed(); - try { - String versionJson = getVersionJni(); - return JsonUtils.deserialize(MoneroRpcConnection.MAPPER, versionJson, MoneroVersion.class); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - + protected native long getHeightJni(); + @Override - public String getPath() { - assertNotClosed(); - String path = getPathJni(); - return path.isEmpty() ? null : path; - } - + protected native long getRestoreHeightJni(); + @Override - public String getSeed() { - assertNotClosed(); - String seed = getSeedJni(); - if ("".equals(seed)) return null; - return seed; - } + protected native void setRestoreHeightJni(long height); + + @Override + protected native long getDaemonHeightJni(); @Override - public String getSeedLanguage() { - assertNotClosed(); - String seedLanguage = getSeedLanguageJni(); - if ("".equals(seedLanguage)) return null; - return seedLanguage; - } + protected native long getHeightByDateJni(int year, int month, int day); @Override - public String getPrivateViewKey() { - assertNotClosed(); - return getPrivateViewKeyJni(); - } - + protected native long getDaemonMaxPeerHeightJni(); + @Override - public String getPrivateSpendKey() { - assertNotClosed(); - String privateSpendKey = getPrivateSpendKeyJni(); - if ("".equals(privateSpendKey)) return null; - return privateSpendKey; - } - + protected native boolean isViewOnlyJni(); + @Override - public String getPublicViewKey() { - assertNotClosed(); - return getPublicViewKeyJni(); - } - + protected native void setDaemonConnectionJni(String uri, String username, String password, String proxyUri); + @Override - public String getPublicSpendKey() { - assertNotClosed(); - return getPublicSpendKeyJni(); - } - + protected native String[] getDaemonConnectionJni(); // returns [uri, username, password] + @Override - public MoneroIntegratedAddress getIntegratedAddress(String standardAddress, String paymentId) { - assertNotClosed(); - try { - String integratedAddressJson = getIntegratedAddressJni(standardAddress, paymentId); - return JsonUtils.deserialize(MoneroRpcConnection.MAPPER, integratedAddressJson, MoneroIntegratedAddress.class); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - + protected native boolean isConnectedToDaemonJni(); + @Override - public MoneroIntegratedAddress decodeIntegratedAddress(String integratedAddress) { - assertNotClosed(); - try { - String integratedAddressJson = decodeIntegratedAddressJni(integratedAddress); - return JsonUtils.deserialize(MoneroRpcConnection.MAPPER, integratedAddressJson, MoneroIntegratedAddress.class); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - + protected native boolean isDaemonSyncedJni(); + @Override - public long getHeight() { - assertNotClosed(); - return getHeightJni(); - } - + protected native boolean isSyncedJni(); + @Override - public long getDaemonHeight() { - assertNotClosed(); - try { - return getDaemonHeightJni(); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - + protected native int getNetworkTypeJni(); + @Override - public long getHeightByDate(int year, int month, int day) { - assertNotClosed(); - try { - return getHeightByDateJni(year, month ,day); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - + protected native String getVersionJni(); + @Override - public MoneroSyncResult sync(Long startHeight, MoneroWalletListenerI listener) { - assertNotClosed(); - if (startHeight == null) startHeight = Math.max(getHeight(), getRestoreHeight()); + protected native String getPathJni(); - // register listener if given - if (listener != null) addListener(listener); + @Override + protected native String getSeedJni(); - // sync wallet and handle exception - try { - Object[] results = syncJni(startHeight); - return new MoneroSyncResult((long) results[0], (boolean) results[1]); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } finally { - if (listener != null) removeListener(listener); // unregister listener - } - } - @Override - public void startSyncing(Long syncPeriodInMs) { - assertNotClosed(); - try { - startSyncingJni(syncPeriodInMs == null ? DEFAULT_SYNC_PERIOD_IN_MS : syncPeriodInMs); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } + protected native String getSeedLanguageJni(); + protected static native String[] getSeedLanguagesJni(); + @Override - public void stopSyncing() { - assertNotClosed(); - try { - stopSyncingJni(); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - + protected native String getPublicViewKeyJni(); + @Override - public void scanTxs(Collection txHashes) { - assertNotClosed(); - String[] txMetadatasArr = txHashes.toArray(new String[txHashes.size()]); // convert to array for jni - try { - scanTxsJni(txMetadatasArr); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - + protected native String getPrivateViewKeyJni(); + @Override - public void rescanSpent() { - assertNotClosed(); - try { - rescanSpentJni(); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - + protected native String getPublicSpendKeyJni(); + @Override - public void rescanBlockchain() { - assertNotClosed(); - try { - rescanBlockchainJni(); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - + protected native String getPrivateSpendKeyJni(); + @Override - public List getAccounts(boolean includeSubaddresses, String tag) { - assertNotClosed(); - String accountsJson = getAccountsJni(includeSubaddresses, tag); - List accounts = JsonUtils.deserialize(MoneroRpcConnection.MAPPER, accountsJson, AccountsContainer.class).accounts; - for (MoneroAccount account : accounts) sanitizeAccount(account); - return accounts; - } - + protected native String getAddressJni(int accountIdx, int subaddressIdx); + @Override - public MoneroAccount getAccount(int accountIdx, boolean includeSubaddresses) { - assertNotClosed(); - String accountJson = getAccountJni(accountIdx, includeSubaddresses); - MoneroAccount account = JsonUtils.deserialize(MoneroRpcConnection.MAPPER, accountJson, MoneroAccount.class); - sanitizeAccount(account); - return account; - } - + protected native String getAddressIndexJni(String address); + @Override - public MoneroAccount createAccount(String label) { - assertNotClosed(); - String accountJson = createAccountJni(label); - MoneroAccount account = JsonUtils.deserialize(MoneroRpcConnection.MAPPER, accountJson, MoneroAccount.class); - sanitizeAccount(account); - return account; - } - + protected native String getIntegratedAddressJni(String standardAddress, String paymentId); + @Override - public List getSubaddresses(int accountIdx, List subaddressIndices) { - assertNotClosed(); - String subaddresses_json = getSubaddressesJni(accountIdx, GenUtils.listToIntArray(subaddressIndices)); - List subaddresses = JsonUtils.deserialize(MoneroRpcConnection.MAPPER, subaddresses_json, SubaddressesContainer.class).subaddresses; - for (MoneroSubaddress subaddress : subaddresses) sanitizeSubaddress(subaddress); - return subaddresses; - } - + protected native String decodeIntegratedAddressJni(String integratedAddress); + @Override - public MoneroSubaddress createSubaddress(int accountIdx, String label) { - assertNotClosed(); - String subaddressJson = createSubaddressJni(accountIdx, label); - MoneroSubaddress subaddress = JsonUtils.deserialize(MoneroRpcConnection.MAPPER, subaddressJson, MoneroSubaddress.class); - sanitizeSubaddress(subaddress); - return subaddress; - } - + protected native long setListenerJni(MoneroWalletJni.WalletJniListener listener); + @Override - public void setSubaddressLabel(int accountIdx, int subaddressIdx, String label) { - assertNotClosed(); - if (label == null) label = ""; - setSubaddressLabelJni(accountIdx, subaddressIdx, label); - } - + protected native Object[] syncJni(long startHeight); + @Override - public String getAddress(int accountIdx, int subaddressIdx) { - assertNotClosed(); - return getAddressJni(accountIdx, subaddressIdx); - } - + protected native void startSyncingJni(long syncPeriodInMs); + @Override - public MoneroSubaddress getAddressIndex(String address) { - assertNotClosed(); - try { - String subaddressJson = getAddressIndexJni(address); - MoneroSubaddress subaddress = JsonUtils.deserialize(MoneroRpcConnection.MAPPER, subaddressJson, MoneroSubaddress.class); - return sanitizeSubaddress(subaddress); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - + protected native void stopSyncingJni(); + @Override - public BigInteger getBalance(Integer accountIdx, Integer subaddressIdx) { - assertNotClosed(); - try { - if (accountIdx == null) { - if (subaddressIdx != null) throw new MoneroError("Must provide account index with subaddress index"); - return new BigInteger(getBalanceWalletJni()); - } else { - if (subaddressIdx == null) return new BigInteger(getBalanceAccountJni(accountIdx)); - else return new BigInteger(getBalanceSubaddressJni(accountIdx, subaddressIdx)); - } - } catch (MoneroError e) { - throw new MoneroError(e.getMessage()); - } - } - + protected native void scanTxsJni(String[] txHashes); + @Override - public BigInteger getUnlockedBalance(Integer accountIdx, Integer subaddressIdx) { - assertNotClosed(); - try { - if (accountIdx == null) { - if (subaddressIdx != null) throw new MoneroError("Must provide account index with subaddress index"); - return new BigInteger(getUnlockedBalanceWalletJni()); - } else { - if (subaddressIdx == null) return new BigInteger(getUnlockedBalanceAccountJni(accountIdx)); - else return new BigInteger(getUnlockedBalanceSubaddressJni(accountIdx, subaddressIdx)); - } - } catch (MoneroError e) { - throw new MoneroError(e.getMessage()); - } - } - + protected native void rescanSpentJni(); + @Override - public List getTxs(MoneroTxQuery query) { - assertNotClosed(); + protected native void rescanBlockchainJni(); - // copy and normalize tx query up to block - query = query == null ? new MoneroTxQuery() : query.copy(); - if (query.getBlock() == null) query.setBlock(new MoneroBlock().setTxs(query)); + @Override + protected native String getBalanceWalletJni(); - // serialize query from block and fetch txs from jni - String blocksJson; - try { - blocksJson = getTxsJni(JsonUtils.serialize(query.getBlock())); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } + @Override + protected native String getBalanceAccountJni(int accountIdx); - // deserialize and return txs - return deserializeTxs(query, blocksJson); - } - @Override - public List getTransfers(MoneroTransferQuery query) { - assertNotClosed(); + protected native String getBalanceSubaddressJni(int accountIdx, int subaddressIdx); - // copy and normalize query up to block - query = normalizeTransferQuery(query); + @Override + protected native String getUnlockedBalanceWalletJni(); - // serialize query from block and fetch transfers from jni - String blocksJson; - try { - blocksJson = getTransfersJni(JsonUtils.serialize(query.getTxQuery().getBlock())); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } + @Override + protected native String getUnlockedBalanceAccountJni(int accountIdx); - // deserialize and return transfers - return deserializeTransfers(query, blocksJson); - } - @Override - public List getOutputs(MoneroOutputQuery query) { - assertNotClosed(); + protected native String getUnlockedBalanceSubaddressJni(int accountIdx, int subaddressIdx); - // copy and normalize query up to block - if (query == null) query = new MoneroOutputQuery(); - else { - if (query.getTxQuery() == null) query = query.copy(); - else { - MoneroTxQuery txQuery = query.getTxQuery().copy(); - if (query.getTxQuery().getOutputQuery() == query) query = txQuery.getOutputQuery(); - else { - GenUtils.assertNull("Output query's tx query must be circular reference or null", query.getTxQuery().getOutputQuery()); - query = query.copy(); - query.setTxQuery(txQuery); - } - } - } - if (query.getTxQuery() == null) query.setTxQuery(new MoneroTxQuery()); - query.getTxQuery().setOutputQuery(query); - if (query.getTxQuery().getBlock() == null) query.getTxQuery().setBlock(new MoneroBlock().setTxs(query.getTxQuery())); + @Override + protected native String getAccountsJni(boolean includeSubaddresses, String tag); - // serialize query from block and fetch outputs from jni - String blocksJson = getOutputsJni(JsonUtils.serialize(query.getTxQuery().getBlock())); + @Override + protected native String getAccountJni(int accountIdx, boolean includeSubaddresses); - // deserialize and return outputs - return deserializeOutputs(query, blocksJson); - } - @Override - public String exportOutputs(boolean all) { - assertNotClosed(); - String outputsHex = exportOutputsJni(all); - return outputsHex.isEmpty() ? null : outputsHex; - } - + protected native String createAccountJni(String label); + @Override - public int importOutputs(String outputsHex) { - assertNotClosed(); - return importOutputsJni(outputsHex); - } - + protected native String getSubaddressesJni(int accountIdx, int[] subaddressIndices); + @Override - public List exportKeyImages(boolean all) { - assertNotClosed(); - String keyImagesJson = exportKeyImagesJni(all); - List keyImages = JsonUtils.deserialize(MoneroRpcConnection.MAPPER, keyImagesJson, KeyImagesContainer.class).keyImages; - return keyImages; - } + protected native String createSubaddressJni(int accountIdx, String label); + protected native void setSubaddressLabelJni(int accountIdx, int subaddressIdx, String label); + @Override - public MoneroKeyImageImportResult importKeyImages(List keyImages) { - assertNotClosed(); + protected native String getTxsJni(String txQueryJson); - // wrap and serialize key images in container for jni - KeyImagesContainer keyImageContainer = new KeyImagesContainer(keyImages); - String importResultJson = importKeyImagesJni(JsonUtils.serialize(keyImageContainer)); + @Override + protected native String getTransfersJni(String transferQueryJson); - // deserialize response - return JsonUtils.deserialize(MoneroRpcConnection.MAPPER, importResultJson, MoneroKeyImageImportResult.class); - } - @Override - public List getNewKeyImagesFromLastImport() { - assertNotClosed(); - throw new RuntimeException("MoneroWalletFull.getNewKeyImagesFromLastImport() not implemented"); - } - + protected native String getOutputsJni(String outputQueryJson); + @Override - public void freezeOutput(String keyImage) { - assertNotClosed(); - try { - freezeOutputJni(keyImage); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - + protected native String exportOutputsJni(boolean all); + @Override - public void thawOutput(String keyImage) { - assertNotClosed(); - try { - thawOutputJni(keyImage); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } + protected native int importOutputsJni(String outputsHex); @Override - public boolean isOutputFrozen(String keyImage) { - assertNotClosed(); - try { - return isOutputFrozenJni(keyImage); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - + protected native String exportKeyImagesJni(boolean all); + @Override - public MoneroTxPriority getDefaultFeePriority() { - assertNotClosed(); - try { - int moneroTxPriorityOrdinal = getDefaultFeePriorityJni(); - return MoneroTxPriority.values()[moneroTxPriorityOrdinal]; - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - + protected native String importKeyImagesJni(String keyImagesJson); + @Override - public List createTxs(MoneroTxConfig config) { - assertNotClosed(); - LOGGER.fine("java createTxs(request)"); - LOGGER.fine("Tx config: " + JsonUtils.serialize(config)); + protected native String[] relayTxsJni(String[] txMetadatas); - // validate request - if (config == null) throw new MoneroError("Tx config cannot be null"); + @Override + protected native void freezeOutputJni(String KeyImage); - // submit tx config to JNI and get response as json rooted at tx set - String txSetJson; - try { - txSetJson = createTxsJni(JsonUtils.serialize(config)); - LOGGER.fine("Received createTxs() response from JNI: " + txSetJson.substring(0, Math.min(5000, txSetJson.length())) + "..."); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } + @Override + protected native void thawOutputJni(String keyImage); - // deserialize and return txs - MoneroTxSet txSet = JsonUtils.deserialize(txSetJson, MoneroTxSet.class); - return txSet.getTxs(); - } - @Override - public MoneroTxWallet sweepOutput(MoneroTxConfig config) { - assertNotClosed(); - try { - String txSetJson = sweepOutputJni(JsonUtils.serialize(config)); - MoneroTxSet txSet = JsonUtils.deserialize(txSetJson, MoneroTxSet.class); - return txSet.getTxs().get(0); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } + protected native boolean isOutputFrozenJni(String keyImage); @Override - public List sweepUnlocked(MoneroTxConfig config) { - assertNotClosed(); + protected native int getDefaultFeePriorityJni(); + + @Override + protected native String createTxsJni(String txConfigJson); - // validate request - if (config == null) throw new MoneroError("Send request cannot be null"); + @Override + protected native String sweepUnlockedJni(String txConfigJson); - // submit send request to JNI and get response as json rooted at tx set - String txSetsJson; - try { - txSetsJson = sweepUnlockedJni(JsonUtils.serialize(config)); - LOGGER.fine("Received sweepUnlocked() response from JNI: " + txSetsJson.substring(0, Math.min(5000, txSetsJson.length())) + "..."); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } + @Override + protected native String sweepOutputJni(String txConfigJson); - // deserialize tx sets - List txSets = JsonUtils.deserialize(MoneroRpcConnection.MAPPER, txSetsJson, TxSetsContainer.class).txSets; + @Override + protected native String sweepDustJni(boolean doNotRelay); - // return txs - List txs = new ArrayList(); - for (MoneroTxSet txSet : txSets) txs.addAll(txSet.getTxs()); - return txs; - } - @Override - public List sweepDust(boolean relay) { - assertNotClosed(); - try { - String txSetJson = sweepDustJni(relay); - MoneroTxSet txSet = JsonUtils.deserialize(txSetJson, MoneroTxSet.class); - if (txSet.getTxs() == null) txSet.setTxs(new ArrayList()); - return txSet.getTxs(); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - + protected native String describeTxSetJni(String txSetJson); + @Override - public List relayTxs(Collection txMetadatas) { - assertNotClosed(); - String[] txMetadatasArr = txMetadatas.toArray(new String[txMetadatas.size()]); // convert to array for jni - try { - return Arrays.asList(relayTxsJni(txMetadatasArr)); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - + protected native String signTxsJni(String unsignedTxHex); + @Override - public MoneroTxSet describeTxSet(MoneroTxSet txSet) { - assertNotClosed(); - txSet = new MoneroTxSet() - .setUnsignedTxHex(txSet.getUnsignedTxHex()) - .setSignedTxHex(txSet.getSignedTxHex()) - .setMultisigTxHex(txSet.getMultisigTxHex()); - String describedTxSetJson; - try { - describedTxSetJson = describeTxSetJni(JsonUtils.serialize(txSet)); - return JsonUtils.deserialize(describedTxSetJson, MoneroTxSet.class); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - + protected native String[] submitTxsJni(String signedTxHex); + @Override - public MoneroTxSet signTxs(String unsignedTxHex) { - assertNotClosed(); - try { - String signedTxSetJson = signTxsJni(unsignedTxHex); - return JsonUtils.deserialize(signedTxSetJson, MoneroTxSet.class); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - + protected native String[] getTxNotesJni(String[] txHashes); + @Override - public List submitTxs(String signedTxHex) { - assertNotClosed(); - try { - return Arrays.asList(submitTxsJni(signedTxHex)); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - + protected native void setTxNotesJni(String[] txHashes, String[] notes); + @Override - public MoneroCheckTx checkTxKey(String txHash, String txKey, String address) { - assertNotClosed(); - try { - String checkStr = checkTxKeyJni(txHash, txKey, address); - return JsonUtils.deserialize(MoneroRpcConnection.MAPPER, checkStr, MoneroCheckTx.class); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - + protected native String signMessageJni(String msg, int signatureType, int accountIdx, int subaddressIdx); + @Override - public String getTxProof(String txHash, String address, String message) { - assertNotClosed(); - try { - return getTxProofJni(txHash, address, message); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - + protected native String verifyMessageJni(String msg, String address, String signature); + @Override - public MoneroCheckTx checkTxProof(String txHash, String address, String message, String signature) { - assertNotClosed(); - try { - String checkStr = checkTxProofJni(txHash, address, message, signature); - return JsonUtils.deserialize(MoneroRpcConnection.MAPPER, checkStr, MoneroCheckTx.class); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - + protected native String getTxKeyJni(String txHash); + @Override - public String getSpendProof(String txHash, String message) { - assertNotClosed(); - try { - return getSpendProofJni(txHash, message); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - + protected native String checkTxKeyJni(String txHash, String txKey, String address); + @Override - public boolean checkSpendProof(String txHash, String message, String signature) { - assertNotClosed(); - try { - return checkSpendProofJni(txHash, message, signature); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - + protected native String getTxProofJni(String txHash, String address, String message); + @Override - public String getReserveProofWallet(String message) { - try { - return getReserveProofWalletJni(message); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - + protected native String checkTxProofJni(String txHash, String address, String message, String signature); + @Override - public String getReserveProofAccount(int accountIdx, BigInteger amount, String message) { - assertNotClosed(); - try { - return getReserveProofAccountJni(accountIdx, amount.toString(), message); - } catch (Exception e) { - throw new MoneroError(e.getMessage(), -1); - } - } - + protected native String getSpendProofJni(String txHash, String message); + @Override - public MoneroCheckReserve checkReserveProof(String address, String message, String signature) { - assertNotClosed(); - try { - String checkStr = checkReserveProofJni(address, message, signature); - return JsonUtils.deserialize(MoneroRpcConnection.MAPPER, checkStr, MoneroCheckReserve.class); - } catch (Exception e) { - throw new MoneroError(e.getMessage(), -1); - } - } - + protected native boolean checkSpendProofJni(String txHash, String message, String signature); + @Override - public String signMessage(String msg, MoneroMessageSignatureType signatureType, int accountIdx, int subaddressIdx) { - assertNotClosed(); - return signMessageJni(msg, signatureType.ordinal(), accountIdx, subaddressIdx); - } - + protected native String getReserveProofWalletJni(String message); + @Override - public MoneroMessageSignatureResult verifyMessage(String msg, String address, String signature) { - assertNotClosed(); - try { - String resultJson = verifyMessageJni(msg, address, signature); - Map result = JsonUtils.deserialize(resultJson, new TypeReference>(){}); - boolean isGood = (boolean) result.get("isGood"); - return new MoneroMessageSignatureResult( - isGood, - !isGood ? null : (Boolean) result.get("isOld"), - !isGood ? null : "spend".equals(result.get("signatureType")) ? MoneroMessageSignatureType.SIGN_WITH_SPEND_KEY : MoneroMessageSignatureType.SIGN_WITH_VIEW_KEY, - !isGood ? null : (Integer) result.get("version")); - } catch (Exception e) { - return new MoneroMessageSignatureResult(false, null, null, null); // jni can differentiate incorrect from invalid address, but rpc returns -2 for both, so returning bad result for consistency - } - } - + protected native String getReserveProofAccountJni(int accountIdx, String amount, String message); + @Override - public String getTxKey(String txHash) { - assertNotClosed(); - try { - return getTxKeyJni(txHash); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - + protected native String checkReserveProofJni(String address, String message, String signature); + @Override - public List getTxNotes(List txHashes) { - assertNotClosed(); - return Arrays.asList(getTxNotesJni(txHashes.toArray(new String[txHashes.size()]))); // convert to array for jni - } - + protected native String getAddressBookEntriesJni(int[] indices); + @Override - public void setTxNotes(List txHashes, List notes) { - assertNotClosed(); - setTxNotesJni(txHashes.toArray(new String[txHashes.size()]), notes.toArray(new String[notes.size()])); - } - + protected native int addAddressBookEntryJni(String address, String description); + @Override - public List getAddressBookEntries(List entryIndices) { - assertNotClosed(); - if (entryIndices == null) entryIndices = new ArrayList(); - String entriesJson = getAddressBookEntriesJni(GenUtils.listToIntArray(entryIndices)); - List entries = JsonUtils.deserialize(MoneroRpcConnection.MAPPER, entriesJson, AddressBookEntriesContainer.class).entries; - if (entries == null) entries = new ArrayList(); - return entries; - } - + protected native void editAddressBookEntryJni(int index, boolean setAddress, String address, boolean setDescription, String description); + @Override - public int addAddressBookEntry(String address, String description) { - assertNotClosed(); - return addAddressBookEntryJni(address, description); - } - + protected native void deleteAddressBookEntryJni(int entryIdx); + @Override - public void editAddressBookEntry(int index, boolean setAddress, String address, boolean setDescription, String description) { - assertNotClosed(); - editAddressBookEntryJni(index, setAddress, address, setDescription, description); - } - + protected native String getPaymentUriJni(String sendRequestJson); + @Override - public void deleteAddressBookEntry(int entryIdx) { - assertNotClosed(); - deleteAddressBookEntryJni(entryIdx); - } - + protected native String parsePaymentUriJni(String uri); + @Override - public void tagAccounts(String tag, Collection accountIndices) { - assertNotClosed(); - throw new RuntimeException("MoneroWalletFull.tagAccounts() not implemented"); - } - - @Override - public void untagAccounts(Collection accountIndices) { - assertNotClosed(); - throw new RuntimeException("MoneroWalletFull.untagAccounts() not implemented"); - } - - @Override - public List getAccountTags() { - assertNotClosed(); - throw new RuntimeException("MoneroWalletFull.getAccountTags() not implemented"); - } - - @Override - public void setAccountTagLabel(String tag, String label) { - assertNotClosed(); - throw new RuntimeException("MoneroWalletFull.setAccountTagLabel() not implemented"); - } - - @Override - public String getPaymentUri(MoneroTxConfig request) { - assertNotClosed(); - try { - return getPaymentUriJni(JsonUtils.serialize(request)); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - - @Override - public MoneroTxConfig parsePaymentUri(String uri) { - assertNotClosed(); - try { - String sendRequestJson = parsePaymentUriJni(uri); - return JsonUtils.deserialize(MoneroRpcConnection.MAPPER, sendRequestJson, MoneroTxConfig.class); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - - @Override - public String getAttribute(String key) { - assertNotClosed(); - return getAttributeJni(key); - } - + protected native String getAttributeJni(String key); + @Override - public void setAttribute(String key, String val) { - assertNotClosed(); - setAttributeJni(key, val); - } + protected native void setAttributeJni(String key, String val); @Override - public void startMining(Long numThreads, Boolean backgroundMining, Boolean ignoreBattery) { - assertNotClosed(); - try { - startMiningJni(numThreads == null ? 0l : (long) numThreads, Boolean.TRUE.equals(backgroundMining), Boolean.TRUE.equals(ignoreBattery)); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - - @Override - public void stopMining() { - assertNotClosed(); - try { - stopMiningJni(); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - - - @Override - public boolean isMultisigImportNeeded() { - assertNotClosed(); - return isMultisigImportNeededJni(); - } - - @Override - public MoneroMultisigInfo getMultisigInfo() { - assertNotClosed(); - try { - String multisigInfoJson = getMultisigInfoJni(); - return JsonUtils.deserialize(multisigInfoJson, MoneroMultisigInfo.class); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - + protected native void startMiningJni(long numThreads, boolean backgroundMining, boolean ignoreBattery); + @Override - public String prepareMultisig() { - assertNotClosed(); - return prepareMultisigJni(); - } - + protected native void stopMiningJni(); + @Override - public String makeMultisig(List multisigHexes, int threshold, String password) { - assertNotClosed(); - try { - return makeMultisigJni(multisigHexes.toArray(new String[multisigHexes.size()]), threshold, password); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - + protected native boolean isMultisigImportNeededJni(); + @Override - public MoneroMultisigInitResult exchangeMultisigKeys(List multisigHexes, String password) { - assertNotClosed(); - try { - String initMultisigResultJson = exchangeMultisigKeysJni(multisigHexes.toArray(new String[multisigHexes.size()]), password); - return JsonUtils.deserialize(initMultisigResultJson, MoneroMultisigInitResult.class); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - + protected native String getMultisigInfoJni(); + @Override - public String exportMultisigHex() { - assertNotClosed(); - try { - return exportMultisigHexJni(); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - + protected native String prepareMultisigJni(); + @Override - public int importMultisigHex(List multisigHexes) { - assertNotClosed(); - try { - return importMultisigHexJni(multisigHexes.toArray(new String[multisigHexes.size()])); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - + protected native String makeMultisigJni(String[] multisigHexes, int threshold, String password); + @Override - public MoneroMultisigSignResult signMultisigTxHex(String multisigTxHex) { - assertNotClosed(); - try { - String signMultisigResultJson = signMultisigTxHexJni(multisigTxHex); - return JsonUtils.deserialize(signMultisigResultJson, MoneroMultisigSignResult.class); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - + protected native String exchangeMultisigKeysJni(String[] multisigHexes, String password); + @Override - public List submitMultisigTxHex(String signedMultisigTxHex) { - assertNotClosed(); - try { - return Arrays.asList(submitMultisigTxHexJni(signedMultisigTxHex)); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - - /** - * Get the wallet's keys and cache data. - * - * @return the keys and cache data, respectively - */ - public synchronized byte[][] getData() { - return new byte[][] { getKeysFileBufferJni(password, isViewOnly()), getCacheFileBufferJni() }; - } - + protected native String exportMultisigHexJni(); + @Override - public void changePassword(String oldPassword, String newPassword) { - try { - if (!password.equals(oldPassword)) throw new RuntimeException("Invalid original password."); - changePasswordJni(oldPassword, newPassword); - password = newPassword; - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - + protected native int importMultisigHexJni(String[] multisigHexes); + @Override - public void save() { - assertNotClosed(); - saveJni(); - } - + protected native String signMultisigTxHexJni(String multisigTxHex); + @Override - public void close(boolean save) { - if (isClosed) return; // closing a closed wallet has no effect - super.close(save); - password = null; - refreshListening(); - try { - closeJni(save); - } catch (Exception e) { - throw new MoneroError(e.getMessage()); - } - } - - // ------------------------------ NATIVE METHODS ---------------------------- - - private native static boolean walletExistsJni(String path); - - private native static long openWalletJni(String path, String password, int networkType); - - private native static long openWalletDataJni(String password, int networkType, byte[] keysData, byte[] cacheData); - - private native static long createWalletJni(String walletConfigJson); - - private native long getHeightJni(); - - private native long getRestoreHeightJni(); - - private native void setRestoreHeightJni(long height); - - private native long getDaemonHeightJni(); - - private native long getDaemonMaxPeerHeightJni(); - - private native long getHeightByDateJni(int year, int month, int day); - - private native boolean isViewOnlyJni(); - - private native void setDaemonConnectionJni(String uri, String username, String password, String proxyUri); - - private native String[] getDaemonConnectionJni(); // returns [uri, username, password] - - private native boolean isConnectedToDaemonJni(); - - private native boolean isDaemonSyncedJni(); - - private native boolean isSyncedJni(); - - private native int getNetworkTypeJni(); - - private native String getVersionJni(); - - private native String getPathJni(); - - private native String getSeedJni(); - - private native String getSeedLanguageJni(); - - private static native String[] getSeedLanguagesJni(); - - private native String getPublicViewKeyJni(); - - private native String getPrivateViewKeyJni(); - - private native String getPublicSpendKeyJni(); - - private native String getPrivateSpendKeyJni(); - - private native String getAddressJni(int accountIdx, int subaddressIdx); - - private native String getAddressIndexJni(String address); - - private native String getIntegratedAddressJni(String standardAddress, String paymentId); - - private native String decodeIntegratedAddressJni(String integratedAddress); - - private native long setListenerJni(WalletJniListener listener); - - private native Object[] syncJni(long startHeight); - - private native void startSyncingJni(long syncPeriodInMs); - - private native void stopSyncingJni(); - - private native void scanTxsJni(String[] txHashes); - - private native void rescanSpentJni(); - - private native void rescanBlockchainJni(); - - private native String getBalanceWalletJni(); - - private native String getBalanceAccountJni(int accountIdx); - - private native String getBalanceSubaddressJni(int accountIdx, int subaddressIdx); - - private native String getUnlockedBalanceWalletJni(); - - private native String getUnlockedBalanceAccountJni(int accountIdx); - - private native String getUnlockedBalanceSubaddressJni(int accountIdx, int subaddressIdx); - - private native String getAccountsJni(boolean includeSubaddresses, String tag); - - private native String getAccountJni(int accountIdx, boolean includeSubaddresses); - - private native String createAccountJni(String label); - - private native String getSubaddressesJni(int accountIdx, int[] subaddressIndices); - - private native String createSubaddressJni(int accountIdx, String label); - - private native void setSubaddressLabelJni(int accountIdx, int subaddressIdx, String label); - - private native String getTxsJni(String txQueryJson); - - private native String getTransfersJni(String transferQueryJson); - - private native String getOutputsJni(String outputQueryJson); - - private native String exportOutputsJni(boolean all); - - private native int importOutputsJni(String outputsHex); - - private native String exportKeyImagesJni(boolean all); - - private native String importKeyImagesJni(String keyImagesJson); - - private native String[] relayTxsJni(String[] txMetadatas); - - private native void freezeOutputJni(String KeyImage); - - private native void thawOutputJni(String keyImage); - - private native boolean isOutputFrozenJni(String keyImage); - - private native int getDefaultFeePriorityJni(); - - private native String createTxsJni(String txConfigJson); - - private native String sweepUnlockedJni(String txConfigJson); - - private native String sweepOutputJni(String txConfigJson); - - private native String sweepDustJni(boolean doNotRelay); - - private native String describeTxSetJni(String txSetJson); - - private native String signTxsJni(String unsignedTxHex); - - private native String[] submitTxsJni(String signedTxHex); - - private native String[] getTxNotesJni(String[] txHashes); - - private native void setTxNotesJni(String[] txHashes, String[] notes); - - private native String signMessageJni(String msg, int signatureType, int accountIdx, int subaddressIdx); - - private native String verifyMessageJni(String msg, String address, String signature); - - private native String getTxKeyJni(String txHash); - - private native String checkTxKeyJni(String txHash, String txKey, String address); - - private native String getTxProofJni(String txHash, String address, String message); - - private native String checkTxProofJni(String txHash, String address, String message, String signature); - - private native String getSpendProofJni(String txHash, String message); - - private native boolean checkSpendProofJni(String txHash, String message, String signature); - - private native String getReserveProofWalletJni(String message); - - private native String getReserveProofAccountJni(int accountIdx, String amount, String message); - - private native String checkReserveProofJni(String address, String message, String signature); - - private native String getAddressBookEntriesJni(int[] indices); - - private native int addAddressBookEntryJni(String address, String description); - - private native void editAddressBookEntryJni(int index, boolean setAddress, String address, boolean setDescription, String description); - - private native void deleteAddressBookEntryJni(int entryIdx); - - private native String getPaymentUriJni(String sendRequestJson); - - private native String parsePaymentUriJni(String uri); - - private native String getAttributeJni(String key); - - private native void setAttributeJni(String key, String val); - - private native void startMiningJni(long numThreads, boolean backgroundMining, boolean ignoreBattery); - - private native void stopMiningJni(); - - private native boolean isMultisigImportNeededJni(); - - private native String getMultisigInfoJni(); - - private native String prepareMultisigJni(); - - private native String makeMultisigJni(String[] multisigHexes, int threshold, String password); - - private native String exchangeMultisigKeysJni(String[] multisigHexes, String password); - - private native String exportMultisigHexJni(); - - private native int importMultisigHexJni(String[] multisigHexes); - - private native String signMultisigTxHexJni(String multisigTxHex); - - private native String[] submitMultisigTxHexJni(String signedMultisigTxHex); + protected native String[] submitMultisigTxHexJni(String signedMultisigTxHex); private native byte[] getKeysFileBufferJni(String password, boolean viewOnly); private native byte[] getCacheFileBufferJni(); - - private native void changePasswordJni(String oldPassword, String newPassword); - - private native void moveToJni(String path, String password); - - private native void saveJni(); - - private native void closeJni(boolean save); - - // -------------------------------- LISTENER -------------------------------- - - /** - * Receives notifications directly from jni c++. - */ - @SuppressWarnings("unused") // called directly from jni c++ - private class WalletJniListener { - - public void onSyncProgress(long height, long startHeight, long endHeight, double percentDone, String message) { - announceSyncProgress(height, startHeight, endHeight, percentDone, message); - } - - public void onNewBlock(long height) { - announceNewBlock(height); - } - - public void onBalancesChanged(String newBalanceStr, String newUnlockedBalanceStr) { - announceBalancesChanged(new BigInteger(newBalanceStr), new BigInteger(newUnlockedBalanceStr)); - } - public void onOutputReceived(long height, String txHash, String amountStr, int accountIdx, int subaddressIdx, int version, String unlockTimeStr, boolean isLocked) { - - // build output to announce - MoneroOutputWallet output = new MoneroOutputWallet(); - output.setAmount(new BigInteger(amountStr)); - output.setAccountIndex(accountIdx); - output.setSubaddressIndex(subaddressIdx); - MoneroTxWallet tx = new MoneroTxWallet(); - tx.setHash(txHash); - tx.setVersion(version); - tx.setUnlockTime(new BigInteger(unlockTimeStr)); - output.setTx(tx); - tx.setOutputs(Arrays.asList(output)); - tx.setIsIncoming(true); - tx.setIsLocked(isLocked); - if (height > 0) { - MoneroBlock block = new MoneroBlock().setHeight(height); - block.setTxs(Arrays.asList(tx)); - tx.setBlock(block); - tx.setIsConfirmed(true); - tx.setInTxPool(false); - tx.setIsFailed(false); - } else { - tx.setIsConfirmed(false); - tx.setInTxPool(true); - } - - // announce output - announceOutputReceived((MoneroOutputWallet) tx.getOutputs().get(0)); - } - - public void onOutputSpent(long height, String txHash, String amountStr, String accountIdxStr, String subaddressIdxStr, int version, String unlockTimeStr, boolean isLocked) { - - // build spent output - MoneroOutputWallet output = new MoneroOutputWallet(); - output.setAmount(new BigInteger(amountStr)); - if (accountIdxStr.length() > 0) output.setAccountIndex(Integer.parseInt(accountIdxStr)); - if (subaddressIdxStr.length() > 0) output.setSubaddressIndex(Integer.parseInt(subaddressIdxStr)); - MoneroTxWallet tx = new MoneroTxWallet(); - tx.setHash(txHash); - tx.setVersion(version); - tx.setUnlockTime(new BigInteger(unlockTimeStr)); - tx.setIsLocked(isLocked); - output.setTx(tx); - tx.setInputs(Arrays.asList(output)); - tx.setIsIncoming(false); - if (height > 0) { - MoneroBlock block = new MoneroBlock().setHeight(height); - block.setTxs(Arrays.asList(tx)); - tx.setBlock(block); - tx.setIsConfirmed(true); - tx.setInTxPool(false); - tx.setIsFailed(false); - } else { - tx.setIsConfirmed(false); - tx.setInTxPool(true); - } - - // announce output - announceOutputSpent((MoneroOutputWallet) tx.getInputs().get(0)); - } - } - - // ------------------------ RESPONSE DESERIALIZATION ------------------------ - - /** - * Override MoneroBlock with wallet types for polymorphic deserialization. - */ - private static class MoneroBlockWallet extends MoneroBlock { - - // default constructor necessary for serialization - @SuppressWarnings("unused") - public MoneroBlockWallet() { - super(); - } - - @JsonProperty("txs") - public MoneroBlockWallet setTxWallets(List txs) { - super.setTxs(new ArrayList(txs)); - return this; - } + @Override + protected native void changePasswordJni(String oldPassword, String newPassword); - /** - * Initializes a new MoneroBlock with direct references to this block. - * - * TODO: more efficient way to deserialize directly into MoneroBlock? - * - * @return MoneroBlock is the newly initialized block with direct references to this block - */ - public MoneroBlock toBlock() { - MoneroBlock block = new MoneroBlock(); - block.setHash(getHash()); - block.setHeight(getHeight()); - block.setTimestamp(getTimestamp()); - block.setSize(getSize()); - block.setWeight(getWeight()); - block.setLongTermWeight(getLongTermWeight()); - block.setDepth(getDepth()); - block.setDifficulty(getDifficulty()); - block.setCumulativeDifficulty(getCumulativeDifficulty()); - block.setMajorVersion(getMajorVersion()); - block.setMinorVersion(getMinorVersion()); - block.setNonce(getNonce()); - block.setMinerTxHash(getMinerTxHash()); - block.setNumTxs(getNumTxs()); - block.setOrphanStatus(getOrphanStatus()); - block.setPrevHash(getPrevHash()); - block.setReward(getReward()); - block.setPowHash(getPowHash()); - block.setHex(getHex()); - block.setMinerTx(getMinerTx()); - block.setTxs(getTxs()); - block.setTxHashes(getTxHashes()); - for (MoneroTx tx : getTxs()) tx.setBlock(block); // re-assign tx block references - return block; - } - } - - private static class AccountsContainer { - public List accounts; - }; - - private static class SubaddressesContainer { - public List subaddresses; - }; - - private static class BlocksWalletContainer { - public List blocks; - } - - private static class DeserializedBlocksContainer { - public List blocks; - } - - private static class TxSetsContainer { - public List txSets; - } - - private static class KeyImagesContainer { - public List keyImages; - @SuppressWarnings("unused") public KeyImagesContainer() { } // necessary for serialization - public KeyImagesContainer(List keyImages) { this.keyImages = keyImages; }; - } - - private static DeserializedBlocksContainer deserializeBlocks(String blocksJson) { - DeserializedBlocksContainer deserializedBlocksContainer = new DeserializedBlocksContainer(); - deserializedBlocksContainer.blocks = new ArrayList(); - BlocksWalletContainer blocksWalletContainer = JsonUtils.deserialize(MoneroRpcConnection.MAPPER, blocksJson, BlocksWalletContainer.class); - if (blocksWalletContainer.blocks != null) for (MoneroBlockWallet blockWallet : blocksWalletContainer.blocks) deserializedBlocksContainer.blocks.add(blockWallet.toBlock()); - return deserializedBlocksContainer; - } + @Override + protected native void moveToJni(String path, String password); - private static List deserializeTxs(MoneroTxQuery query, String blocksJson) { - - // deserialize blocks - DeserializedBlocksContainer deserializedBlocks = deserializeBlocks(blocksJson); - List blocks = deserializedBlocks.blocks; - - // collect txs - List txs = new ArrayList(); - for (MoneroBlock block : blocks) { - sanitizeBlock(block); - for (MoneroTx tx : block.getTxs()) { - if (block.getHeight() == null) tx.setBlock(null); // dereference placeholder block for unconfirmed txs - txs.add((MoneroTxWallet) tx); - } - } + @Override + protected native void saveJni(); - // re-sort txs which is lost over jni serialization - if (query.getHashes() != null) { - Map txMap = new HashMap(); - for (MoneroTxWallet tx : txs) txMap.put(tx.getHash(), tx); - List txsSorted = new ArrayList(); - for (String txHash : query.getHashes()) if (txMap.containsKey(txHash)) txsSorted.add(txMap.get(txHash)); - txs = txsSorted; - } - return txs; - } - - private static List deserializeTransfers(MoneroTransferQuery query, String blocksJson) { - - // deserialize blocks - DeserializedBlocksContainer deserializedBlocks = deserializeBlocks(blocksJson); - List blocks = deserializedBlocks.blocks; - - // collect transfers - List transfers = new ArrayList(); - for (MoneroBlock block : blocks) { - sanitizeBlock(block); - for (MoneroTx tx : block.getTxs()) { - if (block.getHeight() == null) tx.setBlock(null); // dereference placeholder block for unconfirmed txs - MoneroTxWallet txWallet = (MoneroTxWallet) tx; - if (txWallet.getOutgoingTransfer() != null) transfers.add(txWallet.getOutgoingTransfer()); - if (txWallet.getIncomingTransfers() != null) { - for (MoneroIncomingTransfer transfer : txWallet.getIncomingTransfers()) transfers.add(transfer); - } - } - } - return transfers; - } - - private static List deserializeOutputs(MoneroOutputQuery query, String blocksJson) { - - // deserialize blocks - DeserializedBlocksContainer deserializedBlocks = deserializeBlocks(blocksJson); - List blocks = deserializedBlocks.blocks; - - // collect outputs - List outputs = new ArrayList(); - for (MoneroBlock block : blocks) { - sanitizeBlock(block); - for (MoneroTx tx : block.getTxs()) { - outputs.addAll(((MoneroTxWallet) tx).getOutputsWallet()); - } - } - return outputs; - } - - private static class AddressBookEntriesContainer { - public List entries; - } - - // ---------------------------- PRIVATE HELPERS ----------------------------- - - /** - * Enables or disables listening in the c++ wallet. - */ - private void refreshListening() { - boolean isEnabled = listeners.size() > 0; - if (jniListenerHandle == 0 && !isEnabled || jniListenerHandle > 0 && isEnabled) return; // no difference - jniListenerHandle = setListenerJni(isEnabled ? jniListener : null); - } - - private void assertNotClosed() { - if (isClosed) throw new MoneroError("Wallet is closed"); - } - - private static MoneroAccount sanitizeAccount(MoneroAccount account) { - if (account.getSubaddresses() != null) { - for (MoneroSubaddress subaddress : account.getSubaddresses()) sanitizeSubaddress(subaddress); - } - return account; - } - - private static MoneroSubaddress sanitizeSubaddress(MoneroSubaddress subaddress) { - if ("".equals(subaddress.getLabel())) subaddress.setLabel(null); - return subaddress; - } - - private static MoneroBlock sanitizeBlock(MoneroBlock block) { - for (MoneroTx tx : block.getTxs()) sanitizeTxWallet((MoneroTxWallet) tx); - return block; - } - - private static MoneroTxWallet sanitizeTxWallet(MoneroTxWallet tx) { - return tx; - } + @Override + protected native void closeJni(boolean save); } diff --git a/src/main/java/monero/wallet/MoneroWalletJni.java b/src/main/java/monero/wallet/MoneroWalletJni.java new file mode 100644 index 00000000..c616ab84 --- /dev/null +++ b/src/main/java/monero/wallet/MoneroWalletJni.java @@ -0,0 +1,1567 @@ +package monero.wallet; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Logger; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.type.TypeReference; + +import common.utils.GenUtils; +import common.utils.JsonUtils; +import monero.common.MoneroError; +import monero.common.MoneroRpcConnection; +import monero.common.MoneroUtils; +import monero.daemon.model.MoneroBlock; +import monero.daemon.model.MoneroKeyImage; +import monero.daemon.model.MoneroNetworkType; +import monero.daemon.model.MoneroTx; +import monero.daemon.model.MoneroVersion; +import monero.wallet.model.MoneroAccount; +import monero.wallet.model.MoneroAccountTag; +import monero.wallet.model.MoneroAddressBookEntry; +import monero.wallet.model.MoneroCheckReserve; +import monero.wallet.model.MoneroCheckTx; +import monero.wallet.model.MoneroIncomingTransfer; +import monero.wallet.model.MoneroIntegratedAddress; +import monero.wallet.model.MoneroKeyImageImportResult; +import monero.wallet.model.MoneroMessageSignatureResult; +import monero.wallet.model.MoneroMessageSignatureType; +import monero.wallet.model.MoneroMultisigInfo; +import monero.wallet.model.MoneroMultisigInitResult; +import monero.wallet.model.MoneroMultisigSignResult; +import monero.wallet.model.MoneroOutputQuery; +import monero.wallet.model.MoneroOutputWallet; +import monero.wallet.model.MoneroSubaddress; +import monero.wallet.model.MoneroSyncResult; +import monero.wallet.model.MoneroTransfer; +import monero.wallet.model.MoneroTransferQuery; +import monero.wallet.model.MoneroTxConfig; +import monero.wallet.model.MoneroTxPriority; +import monero.wallet.model.MoneroTxQuery; +import monero.wallet.model.MoneroTxSet; +import monero.wallet.model.MoneroTxWallet; +import monero.wallet.model.MoneroWalletListenerI; + +public abstract class MoneroWalletJni extends MoneroWalletDefault { + // ----------------------------- PRIVATE SETUP ------------------------------ + + // try to load jni bindings + static { + MoneroUtils.tryLoadNativeLibrary(); + } + + // class variables + protected static final Logger LOGGER = Logger.getLogger(MoneroWalletJni.class.getName()); + protected static final long DEFAULT_SYNC_PERIOD_IN_MS = 10000; // default period betweeen syncs in ms + + // instance variables + protected long jniWalletHandle; // memory address of the wallet in c++; this variable is read directly by name in c++ + protected long jniListenerHandle; // memory address of the wallet listener in c++; this variable is read directly by name in c++ + protected WalletJniListener jniListener; // receives notifications from jni c++ + + /** + * Private constructor with a handle to the memory address of the wallet in c++. + * + * @param jniWalletHandle memory address of the wallet in c++ + * @param password password of the wallet instance + */ + protected MoneroWalletJni(long jniWalletHandle) { + this.jniWalletHandle = jniWalletHandle; + this.jniListener = new WalletJniListener(); + } + + // ------------ WALLET METHODS SPECIFIC TO JNI IMPLEMENTATION --------------- + + /** + * Get the maximum height of the peers the wallet's daemon is connected to. + * + * @return the maximum height of the peers the wallet's daemon is connected to + */ + public long getDaemonMaxPeerHeight() { + assertNotClosed(); + try { + return getDaemonMaxPeerHeightJni(); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + /** + * Indicates if the wallet's daemon is synced with the network. + * + * @return true if the daemon is synced with the network, false otherwise + */ + public boolean isDaemonSynced() { + assertNotClosed(); + try { + return isDaemonSyncedJni(); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + /** + * Indicates if the wallet is synced with the daemon. + * + * @return true if the wallet is synced with the daemon, false otherwise + */ + public boolean isSynced() { + assertNotClosed(); + try { + return isSyncedJni(); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + /** + * Get the wallet's network type (mainnet, testnet, or stagenet). + * + * @return the wallet's network type + */ + public MoneroNetworkType getNetworkType() { + assertNotClosed(); + return MoneroNetworkType.values()[getNetworkTypeJni()]; + } + + /** + * Get the height of the first block that the wallet scans. + * + * @return the height of the first block that the wallet scans + */ + public long getRestoreHeight() { + assertNotClosed(); + return getRestoreHeightJni(); + } + + /** + * Set the height of the first block that the wallet scans. + * + * @param syncHeight is the height of the first block that the wallet scans + */ + public void setRestoreHeight(long syncHeight) { + assertNotClosed(); + setRestoreHeightJni(syncHeight); + } + + + // -------------------------- COMMON WALLET METHODS ------------------------- + + @Override + public void addListener(MoneroWalletListenerI listener) { + assertNotClosed(); + super.addListener(listener); + refreshListening(); + } + + @Override + public void removeListener(MoneroWalletListenerI listener) { + assertNotClosed(); + super.removeListener(listener); + refreshListening(); + } + + @Override + public Set getListeners() { + assertNotClosed(); + return super.getListeners(); + } + + @Override + public boolean isViewOnly() { + assertNotClosed(); + return isViewOnlyJni(); + } + + @Override + public void setDaemonConnection(MoneroRpcConnection daemonConnection) { + assertNotClosed(); + if (daemonConnection == null) setDaemonConnectionJni("", "", "", ""); + else { + try { + setDaemonConnectionJni(daemonConnection.getUri() == null ? "" : daemonConnection.getUri().toString(), daemonConnection.getUsername(), daemonConnection.getPassword(), daemonConnection.getProxyUri()); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + } + + @Override + public MoneroRpcConnection getDaemonConnection() { + assertNotClosed(); + try { + String[] vals = getDaemonConnectionJni(); + return vals == null ? null : new MoneroRpcConnection(vals[0], vals[1], vals[2]); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public boolean isConnectedToDaemon() { + assertNotClosed(); + try { + return isConnectedToDaemonJni(); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public MoneroVersion getVersion() { + assertNotClosed(); + try { + String versionJson = getVersionJni(); + return JsonUtils.deserialize(MoneroRpcConnection.MAPPER, versionJson, MoneroVersion.class); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public String getPath() { + assertNotClosed(); + String path = getPathJni(); + return path.isEmpty() ? null : path; + } + + @Override + public String getSeed() { + assertNotClosed(); + String seed = getSeedJni(); + if ("".equals(seed)) return null; + return seed; + } + + @Override + public String getSeedLanguage() { + assertNotClosed(); + String seedLanguage = getSeedLanguageJni(); + if ("".equals(seedLanguage)) return null; + return seedLanguage; + } + + @Override + public String getPrivateViewKey() { + assertNotClosed(); + return getPrivateViewKeyJni(); + } + + @Override + public String getPrivateSpendKey() { + assertNotClosed(); + String privateSpendKey = getPrivateSpendKeyJni(); + if ("".equals(privateSpendKey)) return null; + return privateSpendKey; + } + + @Override + public String getPublicViewKey() { + assertNotClosed(); + return getPublicViewKeyJni(); + } + + @Override + public String getPublicSpendKey() { + assertNotClosed(); + return getPublicSpendKeyJni(); + } + + @Override + public MoneroIntegratedAddress getIntegratedAddress(String standardAddress, String paymentId) { + assertNotClosed(); + try { + String integratedAddressJson = getIntegratedAddressJni(standardAddress, paymentId); + return JsonUtils.deserialize(MoneroRpcConnection.MAPPER, integratedAddressJson, MoneroIntegratedAddress.class); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public MoneroIntegratedAddress decodeIntegratedAddress(String integratedAddress) { + assertNotClosed(); + try { + String integratedAddressJson = decodeIntegratedAddressJni(integratedAddress); + return JsonUtils.deserialize(MoneroRpcConnection.MAPPER, integratedAddressJson, MoneroIntegratedAddress.class); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public long getHeight() { + assertNotClosed(); + return getHeightJni(); + } + + @Override + public long getDaemonHeight() { + assertNotClosed(); + try { + return getDaemonHeightJni(); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public long getHeightByDate(int year, int month, int day) { + assertNotClosed(); + try { + return getHeightByDateJni(year, month ,day); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public MoneroSyncResult sync(Long startHeight, MoneroWalletListenerI listener) { + assertNotClosed(); + if (startHeight == null) startHeight = Math.max(getHeight(), getRestoreHeight()); + + // register listener if given + if (listener != null) addListener(listener); + + // sync wallet and handle exception + try { + Object[] results = syncJni(startHeight); + return new MoneroSyncResult((long) results[0], (boolean) results[1]); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } finally { + if (listener != null) removeListener(listener); // unregister listener + } + } + + @Override + public void startSyncing(Long syncPeriodInMs) { + assertNotClosed(); + try { + startSyncingJni(syncPeriodInMs == null ? DEFAULT_SYNC_PERIOD_IN_MS : syncPeriodInMs); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public void stopSyncing() { + assertNotClosed(); + try { + stopSyncingJni(); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public void scanTxs(Collection txHashes) { + assertNotClosed(); + String[] txMetadatasArr = txHashes.toArray(new String[txHashes.size()]); // convert to array for jni + try { + scanTxsJni(txMetadatasArr); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public void rescanSpent() { + assertNotClosed(); + try { + rescanSpentJni(); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public void rescanBlockchain() { + assertNotClosed(); + try { + rescanBlockchainJni(); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public List getAccounts(boolean includeSubaddresses, String tag) { + assertNotClosed(); + String accountsJson = getAccountsJni(includeSubaddresses, tag); + List accounts = JsonUtils.deserialize(MoneroRpcConnection.MAPPER, accountsJson, AccountsContainer.class).accounts; + for (MoneroAccount account : accounts) sanitizeAccount(account); + return accounts; + } + + @Override + public MoneroAccount getAccount(int accountIdx, boolean includeSubaddresses) { + assertNotClosed(); + String accountJson = getAccountJni(accountIdx, includeSubaddresses); + MoneroAccount account = JsonUtils.deserialize(MoneroRpcConnection.MAPPER, accountJson, MoneroAccount.class); + sanitizeAccount(account); + return account; + } + + @Override + public MoneroAccount createAccount(String label) { + assertNotClosed(); + String accountJson = createAccountJni(label); + MoneroAccount account = JsonUtils.deserialize(MoneroRpcConnection.MAPPER, accountJson, MoneroAccount.class); + sanitizeAccount(account); + return account; + } + + @Override + public List getSubaddresses(int accountIdx, List subaddressIndices) { + assertNotClosed(); + String subaddresses_json = getSubaddressesJni(accountIdx, GenUtils.listToIntArray(subaddressIndices)); + List subaddresses = JsonUtils.deserialize(MoneroRpcConnection.MAPPER, subaddresses_json, SubaddressesContainer.class).subaddresses; + for (MoneroSubaddress subaddress : subaddresses) sanitizeSubaddress(subaddress); + return subaddresses; + } + + @Override + public MoneroSubaddress createSubaddress(int accountIdx, String label) { + assertNotClosed(); + String subaddressJson = createSubaddressJni(accountIdx, label); + MoneroSubaddress subaddress = JsonUtils.deserialize(MoneroRpcConnection.MAPPER, subaddressJson, MoneroSubaddress.class); + sanitizeSubaddress(subaddress); + return subaddress; + } + + @Override + public void setSubaddressLabel(int accountIdx, int subaddressIdx, String label) { + assertNotClosed(); + if (label == null) label = ""; + setSubaddressLabelJni(accountIdx, subaddressIdx, label); + } + + @Override + public String getAddress(int accountIdx, int subaddressIdx) { + assertNotClosed(); + return getAddressJni(accountIdx, subaddressIdx); + } + + @Override + public MoneroSubaddress getAddressIndex(String address) { + assertNotClosed(); + try { + String subaddressJson = getAddressIndexJni(address); + MoneroSubaddress subaddress = JsonUtils.deserialize(MoneroRpcConnection.MAPPER, subaddressJson, MoneroSubaddress.class); + return sanitizeSubaddress(subaddress); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public BigInteger getBalance(Integer accountIdx, Integer subaddressIdx) { + assertNotClosed(); + try { + if (accountIdx == null) { + if (subaddressIdx != null) throw new MoneroError("Must provide account index with subaddress index"); + return new BigInteger(getBalanceWalletJni()); + } else { + if (subaddressIdx == null) return new BigInteger(getBalanceAccountJni(accountIdx)); + else return new BigInteger(getBalanceSubaddressJni(accountIdx, subaddressIdx)); + } + } catch (MoneroError e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public BigInteger getUnlockedBalance(Integer accountIdx, Integer subaddressIdx) { + assertNotClosed(); + try { + if (accountIdx == null) { + if (subaddressIdx != null) throw new MoneroError("Must provide account index with subaddress index"); + return new BigInteger(getUnlockedBalanceWalletJni()); + } else { + if (subaddressIdx == null) return new BigInteger(getUnlockedBalanceAccountJni(accountIdx)); + else return new BigInteger(getUnlockedBalanceSubaddressJni(accountIdx, subaddressIdx)); + } + } catch (MoneroError e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public List getTxs(MoneroTxQuery query) { + assertNotClosed(); + + // copy and normalize tx query up to block + query = query == null ? new MoneroTxQuery() : query.copy(); + if (query.getBlock() == null) query.setBlock(new MoneroBlock().setTxs(query)); + + // serialize query from block and fetch txs from jni + String blocksJson; + try { + blocksJson = getTxsJni(JsonUtils.serialize(query.getBlock())); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + + // deserialize and return txs + return deserializeTxs(query, blocksJson); + } + + @Override + public List getTransfers(MoneroTransferQuery query) { + assertNotClosed(); + + // copy and normalize query up to block + query = normalizeTransferQuery(query); + + // serialize query from block and fetch transfers from jni + String blocksJson; + try { + blocksJson = getTransfersJni(JsonUtils.serialize(query.getTxQuery().getBlock())); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + + // deserialize and return transfers + return deserializeTransfers(query, blocksJson); + } + + @Override + public List getOutputs(MoneroOutputQuery query) { + assertNotClosed(); + + // copy and normalize query up to block + if (query == null) query = new MoneroOutputQuery(); + else { + if (query.getTxQuery() == null) query = query.copy(); + else { + MoneroTxQuery txQuery = query.getTxQuery().copy(); + if (query.getTxQuery().getOutputQuery() == query) query = txQuery.getOutputQuery(); + else { + GenUtils.assertNull("Output query's tx query must be circular reference or null", query.getTxQuery().getOutputQuery()); + query = query.copy(); + query.setTxQuery(txQuery); + } + } + } + if (query.getTxQuery() == null) query.setTxQuery(new MoneroTxQuery()); + query.getTxQuery().setOutputQuery(query); + if (query.getTxQuery().getBlock() == null) query.getTxQuery().setBlock(new MoneroBlock().setTxs(query.getTxQuery())); + + // serialize query from block and fetch outputs from jni + String blocksJson = getOutputsJni(JsonUtils.serialize(query.getTxQuery().getBlock())); + + // deserialize and return outputs + return deserializeOutputs(query, blocksJson); + } + + @Override + public String exportOutputs(boolean all) { + assertNotClosed(); + String outputsHex = exportOutputsJni(all); + return outputsHex.isEmpty() ? null : outputsHex; + } + + @Override + public int importOutputs(String outputsHex) { + assertNotClosed(); + return importOutputsJni(outputsHex); + } + + @Override + public List exportKeyImages(boolean all) { + assertNotClosed(); + String keyImagesJson = exportKeyImagesJni(all); + List keyImages = JsonUtils.deserialize(MoneroRpcConnection.MAPPER, keyImagesJson, KeyImagesContainer.class).keyImages; + return keyImages; + } + + @Override + public MoneroKeyImageImportResult importKeyImages(List keyImages) { + assertNotClosed(); + + // wrap and serialize key images in container for jni + KeyImagesContainer keyImageContainer = new KeyImagesContainer(keyImages); + String importResultJson = importKeyImagesJni(JsonUtils.serialize(keyImageContainer)); + + // deserialize response + return JsonUtils.deserialize(MoneroRpcConnection.MAPPER, importResultJson, MoneroKeyImageImportResult.class); + } + + @Override + public List getNewKeyImagesFromLastImport() { + assertNotClosed(); + throw new RuntimeException("MoneroWalletFull.getNewKeyImagesFromLastImport() not implemented"); + } + + @Override + public void freezeOutput(String keyImage) { + assertNotClosed(); + try { + freezeOutputJni(keyImage); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public void thawOutput(String keyImage) { + assertNotClosed(); + try { + thawOutputJni(keyImage); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public boolean isOutputFrozen(String keyImage) { + assertNotClosed(); + try { + return isOutputFrozenJni(keyImage); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public MoneroTxPriority getDefaultFeePriority() { + assertNotClosed(); + try { + int moneroTxPriorityOrdinal = getDefaultFeePriorityJni(); + return MoneroTxPriority.values()[moneroTxPriorityOrdinal]; + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public List createTxs(MoneroTxConfig config) { + assertNotClosed(); + LOGGER.fine("java createTxs(request)"); + LOGGER.fine("Tx config: " + JsonUtils.serialize(config)); + + // validate request + if (config == null) throw new MoneroError("Tx config cannot be null"); + + // submit tx config to JNI and get response as json rooted at tx set + String txSetJson; + try { + txSetJson = createTxsJni(JsonUtils.serialize(config)); + LOGGER.fine("Received createTxs() response from JNI: " + txSetJson.substring(0, Math.min(5000, txSetJson.length())) + "..."); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + + // deserialize and return txs + MoneroTxSet txSet = JsonUtils.deserialize(txSetJson, MoneroTxSet.class); + return txSet.getTxs(); + } + + @Override + public MoneroTxWallet sweepOutput(MoneroTxConfig config) { + assertNotClosed(); + try { + String txSetJson = sweepOutputJni(JsonUtils.serialize(config)); + MoneroTxSet txSet = JsonUtils.deserialize(txSetJson, MoneroTxSet.class); + return txSet.getTxs().get(0); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public List sweepUnlocked(MoneroTxConfig config) { + assertNotClosed(); + + // validate request + if (config == null) throw new MoneroError("Send request cannot be null"); + + // submit send request to JNI and get response as json rooted at tx set + String txSetsJson; + try { + txSetsJson = sweepUnlockedJni(JsonUtils.serialize(config)); + LOGGER.fine("Received sweepUnlocked() response from JNI: " + txSetsJson.substring(0, Math.min(5000, txSetsJson.length())) + "..."); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + + // deserialize tx sets + List txSets = JsonUtils.deserialize(MoneroRpcConnection.MAPPER, txSetsJson, TxSetsContainer.class).txSets; + + // return txs + List txs = new ArrayList(); + for (MoneroTxSet txSet : txSets) txs.addAll(txSet.getTxs()); + return txs; + } + + @Override + public List sweepDust(boolean relay) { + assertNotClosed(); + try { + String txSetJson = sweepDustJni(relay); + MoneroTxSet txSet = JsonUtils.deserialize(txSetJson, MoneroTxSet.class); + if (txSet.getTxs() == null) txSet.setTxs(new ArrayList()); + return txSet.getTxs(); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public List relayTxs(Collection txMetadatas) { + assertNotClosed(); + String[] txMetadatasArr = txMetadatas.toArray(new String[txMetadatas.size()]); // convert to array for jni + try { + return Arrays.asList(relayTxsJni(txMetadatasArr)); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public MoneroTxSet describeTxSet(MoneroTxSet txSet) { + assertNotClosed(); + txSet = new MoneroTxSet() + .setUnsignedTxHex(txSet.getUnsignedTxHex()) + .setSignedTxHex(txSet.getSignedTxHex()) + .setMultisigTxHex(txSet.getMultisigTxHex()); + String describedTxSetJson; + try { + describedTxSetJson = describeTxSetJni(JsonUtils.serialize(txSet)); + return JsonUtils.deserialize(describedTxSetJson, MoneroTxSet.class); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public MoneroTxSet signTxs(String unsignedTxHex) { + assertNotClosed(); + try { + String signedTxSetJson = signTxsJni(unsignedTxHex); + return JsonUtils.deserialize(signedTxSetJson, MoneroTxSet.class); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public List submitTxs(String signedTxHex) { + assertNotClosed(); + try { + return Arrays.asList(submitTxsJni(signedTxHex)); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public MoneroCheckTx checkTxKey(String txHash, String txKey, String address) { + assertNotClosed(); + try { + String checkStr = checkTxKeyJni(txHash, txKey, address); + return JsonUtils.deserialize(MoneroRpcConnection.MAPPER, checkStr, MoneroCheckTx.class); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public String getTxProof(String txHash, String address, String message) { + assertNotClosed(); + try { + return getTxProofJni(txHash, address, message); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public MoneroCheckTx checkTxProof(String txHash, String address, String message, String signature) { + assertNotClosed(); + try { + String checkStr = checkTxProofJni(txHash, address, message, signature); + return JsonUtils.deserialize(MoneroRpcConnection.MAPPER, checkStr, MoneroCheckTx.class); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public String getSpendProof(String txHash, String message) { + assertNotClosed(); + try { + return getSpendProofJni(txHash, message); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public boolean checkSpendProof(String txHash, String message, String signature) { + assertNotClosed(); + try { + return checkSpendProofJni(txHash, message, signature); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public String getReserveProofWallet(String message) { + try { + return getReserveProofWalletJni(message); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public String getReserveProofAccount(int accountIdx, BigInteger amount, String message) { + assertNotClosed(); + try { + return getReserveProofAccountJni(accountIdx, amount.toString(), message); + } catch (Exception e) { + throw new MoneroError(e.getMessage(), -1); + } + } + + @Override + public MoneroCheckReserve checkReserveProof(String address, String message, String signature) { + assertNotClosed(); + try { + String checkStr = checkReserveProofJni(address, message, signature); + return JsonUtils.deserialize(MoneroRpcConnection.MAPPER, checkStr, MoneroCheckReserve.class); + } catch (Exception e) { + throw new MoneroError(e.getMessage(), -1); + } + } + + @Override + public String signMessage(String msg, MoneroMessageSignatureType signatureType, int accountIdx, int subaddressIdx) { + assertNotClosed(); + return signMessageJni(msg, signatureType.ordinal(), accountIdx, subaddressIdx); + } + + @Override + public MoneroMessageSignatureResult verifyMessage(String msg, String address, String signature) { + assertNotClosed(); + try { + String resultJson = verifyMessageJni(msg, address, signature); + Map result = JsonUtils.deserialize(resultJson, new TypeReference>(){}); + boolean isGood = (boolean) result.get("isGood"); + return new MoneroMessageSignatureResult( + isGood, + !isGood ? null : (Boolean) result.get("isOld"), + !isGood ? null : "spend".equals(result.get("signatureType")) ? MoneroMessageSignatureType.SIGN_WITH_SPEND_KEY : MoneroMessageSignatureType.SIGN_WITH_VIEW_KEY, + !isGood ? null : (Integer) result.get("version")); + } catch (Exception e) { + return new MoneroMessageSignatureResult(false, null, null, null); // jni can differentiate incorrect from invalid address, but rpc returns -2 for both, so returning bad result for consistency + } + } + + @Override + public String getTxKey(String txHash) { + assertNotClosed(); + try { + return getTxKeyJni(txHash); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public List getTxNotes(List txHashes) { + assertNotClosed(); + return Arrays.asList(getTxNotesJni(txHashes.toArray(new String[txHashes.size()]))); // convert to array for jni + } + + @Override + public void setTxNotes(List txHashes, List notes) { + assertNotClosed(); + setTxNotesJni(txHashes.toArray(new String[txHashes.size()]), notes.toArray(new String[notes.size()])); + } + + @Override + public List getAddressBookEntries(List entryIndices) { + assertNotClosed(); + if (entryIndices == null) entryIndices = new ArrayList(); + String entriesJson = getAddressBookEntriesJni(GenUtils.listToIntArray(entryIndices)); + List entries = JsonUtils.deserialize(MoneroRpcConnection.MAPPER, entriesJson, AddressBookEntriesContainer.class).entries; + if (entries == null) entries = new ArrayList(); + return entries; + } + + @Override + public int addAddressBookEntry(String address, String description) { + assertNotClosed(); + return addAddressBookEntryJni(address, description); + } + + @Override + public void editAddressBookEntry(int index, boolean setAddress, String address, boolean setDescription, String description) { + assertNotClosed(); + editAddressBookEntryJni(index, setAddress, address, setDescription, description); + } + + @Override + public void deleteAddressBookEntry(int entryIdx) { + assertNotClosed(); + deleteAddressBookEntryJni(entryIdx); + } + + @Override + public void tagAccounts(String tag, Collection accountIndices) { + assertNotClosed(); + throw new RuntimeException("MoneroWalletFull.tagAccounts() not implemented"); + } + + @Override + public void untagAccounts(Collection accountIndices) { + assertNotClosed(); + throw new RuntimeException("MoneroWalletFull.untagAccounts() not implemented"); + } + + @Override + public List getAccountTags() { + assertNotClosed(); + throw new RuntimeException("MoneroWalletFull.getAccountTags() not implemented"); + } + + @Override + public void setAccountTagLabel(String tag, String label) { + assertNotClosed(); + throw new RuntimeException("MoneroWalletFull.setAccountTagLabel() not implemented"); + } + + @Override + public String getPaymentUri(MoneroTxConfig request) { + assertNotClosed(); + try { + return getPaymentUriJni(JsonUtils.serialize(request)); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public MoneroTxConfig parsePaymentUri(String uri) { + assertNotClosed(); + try { + String sendRequestJson = parsePaymentUriJni(uri); + return JsonUtils.deserialize(MoneroRpcConnection.MAPPER, sendRequestJson, MoneroTxConfig.class); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public String getAttribute(String key) { + assertNotClosed(); + return getAttributeJni(key); + } + + @Override + public void setAttribute(String key, String val) { + assertNotClosed(); + setAttributeJni(key, val); + } + + @Override + public void startMining(Long numThreads, Boolean backgroundMining, Boolean ignoreBattery) { + assertNotClosed(); + try { + startMiningJni(numThreads == null ? 0l : (long) numThreads, Boolean.TRUE.equals(backgroundMining), Boolean.TRUE.equals(ignoreBattery)); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public void stopMining() { + assertNotClosed(); + try { + stopMiningJni(); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public boolean isMultisigImportNeeded() { + assertNotClosed(); + return isMultisigImportNeededJni(); + } + + @Override + public MoneroMultisigInfo getMultisigInfo() { + assertNotClosed(); + try { + String multisigInfoJson = getMultisigInfoJni(); + return JsonUtils.deserialize(multisigInfoJson, MoneroMultisigInfo.class); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public String prepareMultisig() { + assertNotClosed(); + return prepareMultisigJni(); + } + + @Override + public String makeMultisig(List multisigHexes, int threshold, String password) { + assertNotClosed(); + try { + return makeMultisigJni(multisigHexes.toArray(new String[multisigHexes.size()]), threshold, password); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public MoneroMultisigInitResult exchangeMultisigKeys(List multisigHexes, String password) { + assertNotClosed(); + try { + String initMultisigResultJson = exchangeMultisigKeysJni(multisigHexes.toArray(new String[multisigHexes.size()]), password); + return JsonUtils.deserialize(initMultisigResultJson, MoneroMultisigInitResult.class); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public String exportMultisigHex() { + assertNotClosed(); + try { + return exportMultisigHexJni(); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public int importMultisigHex(List multisigHexes) { + assertNotClosed(); + try { + return importMultisigHexJni(multisigHexes.toArray(new String[multisigHexes.size()])); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public MoneroMultisigSignResult signMultisigTxHex(String multisigTxHex) { + assertNotClosed(); + try { + String signMultisigResultJson = signMultisigTxHexJni(multisigTxHex); + return JsonUtils.deserialize(signMultisigResultJson, MoneroMultisigSignResult.class); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public List submitMultisigTxHex(String signedMultisigTxHex) { + assertNotClosed(); + try { + return Arrays.asList(submitMultisigTxHexJni(signedMultisigTxHex)); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + @Override + public void save() { + assertNotClosed(); + saveJni(); + } + + // -------------------------------- LISTENER -------------------------------- + + /** + * Receives notifications directly from jni c++. + */ + @SuppressWarnings("unused") // called directly from jni c++ + public class WalletJniListener { + + public void onSyncProgress(long height, long startHeight, long endHeight, double percentDone, String message) { + announceSyncProgress(height, startHeight, endHeight, percentDone, message); + } + + public void onNewBlock(long height) { + announceNewBlock(height); + } + + public void onBalancesChanged(String newBalanceStr, String newUnlockedBalanceStr) { + announceBalancesChanged(new BigInteger(newBalanceStr), new BigInteger(newUnlockedBalanceStr)); + } + + public void onOutputReceived(long height, String txHash, String amountStr, int accountIdx, int subaddressIdx, int version, String unlockTimeStr, boolean isLocked) { + + // build output to announce + MoneroOutputWallet output = new MoneroOutputWallet(); + output.setAmount(new BigInteger(amountStr)); + output.setAccountIndex(accountIdx); + output.setSubaddressIndex(subaddressIdx); + MoneroTxWallet tx = new MoneroTxWallet(); + tx.setHash(txHash); + tx.setVersion(version); + tx.setUnlockTime(new BigInteger(unlockTimeStr)); + output.setTx(tx); + tx.setOutputs(Arrays.asList(output)); + tx.setIsIncoming(true); + tx.setIsLocked(isLocked); + if (height > 0) { + MoneroBlock block = new MoneroBlock().setHeight(height); + block.setTxs(Arrays.asList(tx)); + tx.setBlock(block); + tx.setIsConfirmed(true); + tx.setInTxPool(false); + tx.setIsFailed(false); + } else { + tx.setIsConfirmed(false); + tx.setInTxPool(true); + } + + // announce output + announceOutputReceived((MoneroOutputWallet) tx.getOutputs().get(0)); + } + + public void onOutputSpent(long height, String txHash, String amountStr, String accountIdxStr, String subaddressIdxStr, int version, String unlockTimeStr, boolean isLocked) { + + // build spent output + MoneroOutputWallet output = new MoneroOutputWallet(); + output.setAmount(new BigInteger(amountStr)); + if (accountIdxStr.length() > 0) output.setAccountIndex(Integer.parseInt(accountIdxStr)); + if (subaddressIdxStr.length() > 0) output.setSubaddressIndex(Integer.parseInt(subaddressIdxStr)); + MoneroTxWallet tx = new MoneroTxWallet(); + tx.setHash(txHash); + tx.setVersion(version); + tx.setUnlockTime(new BigInteger(unlockTimeStr)); + tx.setIsLocked(isLocked); + output.setTx(tx); + tx.setInputs(Arrays.asList(output)); + tx.setIsIncoming(false); + if (height > 0) { + MoneroBlock block = new MoneroBlock().setHeight(height); + block.setTxs(Arrays.asList(tx)); + tx.setBlock(block); + tx.setIsConfirmed(true); + tx.setInTxPool(false); + tx.setIsFailed(false); + } else { + tx.setIsConfirmed(false); + tx.setInTxPool(true); + } + + // announce output + announceOutputSpent((MoneroOutputWallet) tx.getInputs().get(0)); + } + } + + // ------------------------ RESPONSE DESERIALIZATION ------------------------ + + /** + * Override MoneroBlock with wallet types for polymorphic deserialization. + */ + protected static class MoneroBlockWallet extends MoneroBlock { + + // default constructor necessary for serialization + @SuppressWarnings("unused") + public MoneroBlockWallet() { + super(); + } + + @JsonProperty("txs") + public MoneroBlockWallet setTxWallets(List txs) { + super.setTxs(new ArrayList(txs)); + return this; + } + + /** + * Initializes a new MoneroBlock with direct references to this block. + * + * TODO: more efficient way to deserialize directly into MoneroBlock? + * + * @return MoneroBlock is the newly initialized block with direct references to this block + */ + public MoneroBlock toBlock() { + MoneroBlock block = new MoneroBlock(); + block.setHash(getHash()); + block.setHeight(getHeight()); + block.setTimestamp(getTimestamp()); + block.setSize(getSize()); + block.setWeight(getWeight()); + block.setLongTermWeight(getLongTermWeight()); + block.setDepth(getDepth()); + block.setDifficulty(getDifficulty()); + block.setCumulativeDifficulty(getCumulativeDifficulty()); + block.setMajorVersion(getMajorVersion()); + block.setMinorVersion(getMinorVersion()); + block.setNonce(getNonce()); + block.setMinerTxHash(getMinerTxHash()); + block.setNumTxs(getNumTxs()); + block.setOrphanStatus(getOrphanStatus()); + block.setPrevHash(getPrevHash()); + block.setReward(getReward()); + block.setPowHash(getPowHash()); + block.setHex(getHex()); + block.setMinerTx(getMinerTx()); + block.setTxs(getTxs()); + block.setTxHashes(getTxHashes()); + for (MoneroTx tx : getTxs()) tx.setBlock(block); // re-assign tx block references + return block; + } + } + + protected static class AccountsContainer { + public List accounts; + }; + + protected static class SubaddressesContainer { + public List subaddresses; + }; + + protected static class BlocksWalletContainer { + public List blocks; + } + + protected static class DeserializedBlocksContainer { + public List blocks; + } + + protected static class TxSetsContainer { + public List txSets; + } + + protected static class KeyImagesContainer { + public List keyImages; + @SuppressWarnings("unused") public KeyImagesContainer() { } // necessary for serialization + public KeyImagesContainer(List keyImages) { this.keyImages = keyImages; }; + } + + protected static DeserializedBlocksContainer deserializeBlocks(String blocksJson) { + DeserializedBlocksContainer deserializedBlocksContainer = new DeserializedBlocksContainer(); + deserializedBlocksContainer.blocks = new ArrayList(); + BlocksWalletContainer blocksWalletContainer = JsonUtils.deserialize(MoneroRpcConnection.MAPPER, blocksJson, BlocksWalletContainer.class); + if (blocksWalletContainer.blocks != null) for (MoneroBlockWallet blockWallet : blocksWalletContainer.blocks) deserializedBlocksContainer.blocks.add(blockWallet.toBlock()); + return deserializedBlocksContainer; + } + + protected static List deserializeTxs(MoneroTxQuery query, String blocksJson) { + + // deserialize blocks + DeserializedBlocksContainer deserializedBlocks = deserializeBlocks(blocksJson); + List blocks = deserializedBlocks.blocks; + + // collect txs + List txs = new ArrayList(); + for (MoneroBlock block : blocks) { + sanitizeBlock(block); + for (MoneroTx tx : block.getTxs()) { + if (block.getHeight() == null) tx.setBlock(null); // dereference placeholder block for unconfirmed txs + txs.add((MoneroTxWallet) tx); + } + } + + // re-sort txs which is lost over jni serialization + if (query.getHashes() != null) { + Map txMap = new HashMap(); + for (MoneroTxWallet tx : txs) txMap.put(tx.getHash(), tx); + List txsSorted = new ArrayList(); + for (String txHash : query.getHashes()) if (txMap.containsKey(txHash)) txsSorted.add(txMap.get(txHash)); + txs = txsSorted; + } + return txs; + } + + protected static List deserializeTransfers(MoneroTransferQuery query, String blocksJson) { + + // deserialize blocks + DeserializedBlocksContainer deserializedBlocks = deserializeBlocks(blocksJson); + List blocks = deserializedBlocks.blocks; + + // collect transfers + List transfers = new ArrayList(); + for (MoneroBlock block : blocks) { + sanitizeBlock(block); + for (MoneroTx tx : block.getTxs()) { + if (block.getHeight() == null) tx.setBlock(null); // dereference placeholder block for unconfirmed txs + MoneroTxWallet txWallet = (MoneroTxWallet) tx; + if (txWallet.getOutgoingTransfer() != null) transfers.add(txWallet.getOutgoingTransfer()); + if (txWallet.getIncomingTransfers() != null) { + for (MoneroIncomingTransfer transfer : txWallet.getIncomingTransfers()) transfers.add(transfer); + } + } + } + return transfers; + } + + protected static List deserializeOutputs(MoneroOutputQuery query, String blocksJson) { + + // deserialize blocks + DeserializedBlocksContainer deserializedBlocks = deserializeBlocks(blocksJson); + List blocks = deserializedBlocks.blocks; + + // collect outputs + List outputs = new ArrayList(); + for (MoneroBlock block : blocks) { + sanitizeBlock(block); + for (MoneroTx tx : block.getTxs()) { + outputs.addAll(((MoneroTxWallet) tx).getOutputsWallet()); + } + } + return outputs; + } + + protected static class AddressBookEntriesContainer { + public List entries; + } + + // ---------------------------- PRIVATE HELPERS ----------------------------- + + /** + * Enables or disables listening in the c++ wallet. + */ + protected void refreshListening() { + boolean isEnabled = listeners.size() > 0; + if (jniListenerHandle == 0 && !isEnabled || jniListenerHandle > 0 && isEnabled) return; // no difference + jniListenerHandle = setListenerJni(isEnabled ? jniListener : null); + } + + protected void assertNotClosed() { + if (isClosed) throw new MoneroError("Wallet is closed"); + } + + protected static MoneroAccount sanitizeAccount(MoneroAccount account) { + if (account.getSubaddresses() != null) { + for (MoneroSubaddress subaddress : account.getSubaddresses()) sanitizeSubaddress(subaddress); + } + return account; + } + + protected static MoneroSubaddress sanitizeSubaddress(MoneroSubaddress subaddress) { + if ("".equals(subaddress.getLabel())) subaddress.setLabel(null); + return subaddress; + } + + protected static MoneroBlock sanitizeBlock(MoneroBlock block) { + for (MoneroTx tx : block.getTxs()) sanitizeTxWallet((MoneroTxWallet) tx); + return block; + } + + protected static MoneroTxWallet sanitizeTxWallet(MoneroTxWallet tx) { + return tx; + } + + // ------------------------------ NATIVE METHODS ---------------------------- + + + protected abstract long getHeightJni(); + + protected abstract long getRestoreHeightJni(); + + protected abstract void setRestoreHeightJni(long height); + + protected abstract long getDaemonHeightJni(); + + protected abstract long getDaemonMaxPeerHeightJni(); + + protected abstract long getHeightByDateJni(int year, int month, int day); + + protected abstract boolean isViewOnlyJni(); + + protected abstract void setDaemonConnectionJni(String uri, String username, String password, String proxyUri); + + protected abstract String[] getDaemonConnectionJni(); // returns [uri, username, password] + + protected abstract boolean isConnectedToDaemonJni(); + + protected abstract boolean isDaemonSyncedJni(); + + protected abstract boolean isSyncedJni(); + + protected abstract int getNetworkTypeJni(); + + protected abstract String getVersionJni(); + + protected abstract String getPathJni(); + + protected abstract String getSeedJni(); + + protected abstract String getSeedLanguageJni(); + + protected abstract String getPublicViewKeyJni(); + + protected abstract String getPrivateViewKeyJni(); + + protected abstract String getPublicSpendKeyJni(); + + protected abstract String getPrivateSpendKeyJni(); + + protected abstract String getAddressJni(int accountIdx, int subaddressIdx); + + protected abstract String getAddressIndexJni(String address); + + protected abstract String getIntegratedAddressJni(String standardAddress, String paymentId); + + protected abstract String decodeIntegratedAddressJni(String integratedAddress); + + protected abstract long setListenerJni(WalletJniListener listener); + + protected abstract Object[] syncJni(long startHeight); + + protected abstract void startSyncingJni(long syncPeriodInMs); + + protected abstract void stopSyncingJni(); + + protected abstract void scanTxsJni(String[] txHashes); + + protected abstract void rescanSpentJni(); + + protected abstract void rescanBlockchainJni(); + + protected abstract String getBalanceWalletJni(); + + protected abstract String getBalanceAccountJni(int accountIdx); + + protected abstract String getBalanceSubaddressJni(int accountIdx, int subaddressIdx); + + protected abstract String getUnlockedBalanceWalletJni(); + + protected abstract String getUnlockedBalanceAccountJni(int accountIdx); + + protected abstract String getUnlockedBalanceSubaddressJni(int accountIdx, int subaddressIdx); + + protected abstract String getAccountsJni(boolean includeSubaddresses, String tag); + + protected abstract String getAccountJni(int accountIdx, boolean includeSubaddresses); + + protected abstract String createAccountJni(String label); + + protected abstract String getSubaddressesJni(int accountIdx, int[] subaddressIndices); + + protected abstract String createSubaddressJni(int accountIdx, String label); + + protected abstract void setSubaddressLabelJni(int accountIdx, int subaddressIdx, String label); + + protected abstract String getTxsJni(String txQueryJson); + + protected abstract String getTransfersJni(String transferQueryJson); + + protected abstract String getOutputsJni(String outputQueryJson); + + protected abstract String exportOutputsJni(boolean all); + + protected abstract int importOutputsJni(String outputsHex); + + protected abstract String exportKeyImagesJni(boolean all); + + protected abstract String importKeyImagesJni(String keyImagesJson); + + protected abstract String[] relayTxsJni(String[] txMetadatas); + + protected abstract void freezeOutputJni(String KeyImage); + + protected abstract void thawOutputJni(String keyImage); + + protected abstract boolean isOutputFrozenJni(String keyImage); + + protected abstract int getDefaultFeePriorityJni(); + + protected abstract String createTxsJni(String txConfigJson); + + protected abstract String sweepUnlockedJni(String txConfigJson); + + protected abstract String sweepOutputJni(String txConfigJson); + + protected abstract String sweepDustJni(boolean doNotRelay); + + protected abstract String describeTxSetJni(String txSetJson); + + protected abstract String signTxsJni(String unsignedTxHex); + + protected abstract String[] submitTxsJni(String signedTxHex); + + protected abstract String[] getTxNotesJni(String[] txHashes); + + protected abstract void setTxNotesJni(String[] txHashes, String[] notes); + + protected abstract String signMessageJni(String msg, int signatureType, int accountIdx, int subaddressIdx); + + protected abstract String verifyMessageJni(String msg, String address, String signature); + + protected abstract String getTxKeyJni(String txHash); + + protected abstract String checkTxKeyJni(String txHash, String txKey, String address); + + protected abstract String getTxProofJni(String txHash, String address, String message); + + protected abstract String checkTxProofJni(String txHash, String address, String message, String signature); + + protected abstract String getSpendProofJni(String txHash, String message); + + protected abstract boolean checkSpendProofJni(String txHash, String message, String signature); + + protected abstract String getReserveProofWalletJni(String message); + + protected abstract String getReserveProofAccountJni(int accountIdx, String amount, String message); + + protected abstract String checkReserveProofJni(String address, String message, String signature); + + protected abstract String getAddressBookEntriesJni(int[] indices); + + protected abstract int addAddressBookEntryJni(String address, String description); + + protected abstract void editAddressBookEntryJni(int index, boolean setAddress, String address, boolean setDescription, String description); + + protected abstract void deleteAddressBookEntryJni(int entryIdx); + + protected abstract String getPaymentUriJni(String sendRequestJson); + + protected abstract String parsePaymentUriJni(String uri); + + protected abstract String getAttributeJni(String key); + + protected abstract void setAttributeJni(String key, String val); + + protected abstract void startMiningJni(long numThreads, boolean backgroundMining, boolean ignoreBattery); + + protected abstract void stopMiningJni(); + + protected abstract boolean isMultisigImportNeededJni(); + + protected abstract String getMultisigInfoJni(); + + protected abstract String prepareMultisigJni(); + + protected abstract String makeMultisigJni(String[] multisigHexes, int threshold, String password); + + protected abstract String exchangeMultisigKeysJni(String[] multisigHexes, String password); + + protected abstract String exportMultisigHexJni(); + + protected abstract int importMultisigHexJni(String[] multisigHexes); + + protected abstract String signMultisigTxHexJni(String multisigTxHex); + + protected abstract String[] submitMultisigTxHexJni(String signedMultisigTxHex); + + protected abstract void changePasswordJni(String oldPassword, String newPassword); + + protected abstract void moveToJni(String path, String password); + + protected abstract void saveJni(); + + protected abstract void closeJni(boolean save); +} diff --git a/src/main/java/monero/wallet/MoneroWalletLight.java b/src/main/java/monero/wallet/MoneroWalletLight.java new file mode 100644 index 00000000..d625f44a --- /dev/null +++ b/src/main/java/monero/wallet/MoneroWalletLight.java @@ -0,0 +1,506 @@ +package monero.wallet; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import common.utils.GenUtils; +import common.utils.JsonUtils; +import monero.common.MoneroError; +import monero.common.MoneroRpcConnection; +import monero.daemon.model.MoneroBlock; +import monero.wallet.model.MoneroOutputQuery; +import monero.wallet.model.MoneroOutputWallet; +import monero.wallet.model.MoneroTxQuery; +import monero.wallet.model.MoneroWalletConfig; + +public class MoneroWalletLight extends MoneroWalletJni { + /** + * Get a list of available languages for the wallet's seed. + * + * @return the available languages for the wallet's seed. + */ + public static List getSeedLanguages() { + return Arrays.asList(getSeedLanguagesJni()); + } + + public static boolean walletExists(MoneroWalletConfig config) { + if (config.getSeed() != null) { + MoneroWalletLight wallet = createWalletFromSeed(new MoneroWalletConfig().setNetworkType(config.getNetworkType()).setSeed(config.getSeed())); + + return walletExistsJni(wallet.getPrimaryAddress(), wallet.getPrivateViewKey(), config.getServerUri()); + } + if (config.getPrimaryAddress() == null || config.getPrimaryAddress().isEmpty()) return false; + if (config.getPrivateViewKey() == null || config.getPrimaryAddress().isEmpty()) return false; + if (config.getServer() == null || config.getServerUri().isEmpty()) return false; + + return walletExistsJni(config.getPrimaryAddress(), config.getPrivateViewKey(), config.getServerUri()); + } + + public static MoneroWalletLight openWallet(MoneroWalletConfig config, MoneroRpcConnection daemonConnection) { + //if (!walletExistsJni(config.getPrimaryAddress(), config.getPrivateViewKey(), daemonConnection.getUri())) throw new MoneroError("Wallet does not exist at server: " + daemonConnection.getUri()); + if (config.getNetworkType() == null) throw new MoneroError("Must provide a network type"); + config.setServer(daemonConnection); + long jniWalletHandle = openWalletJni(serializeWalletConfig(config)); + MoneroWalletLight wallet = new MoneroWalletLight(jniWalletHandle); + if (daemonConnection != null) wallet.setDaemonConnection(daemonConnection); + return wallet; + } + public static MoneroWalletLight openWallet(MoneroWalletConfig config, String daemonUri) { return openWallet(config, daemonUri == null ? null : new MoneroRpcConnection(daemonUri)); } + + public static MoneroWalletLight openWallet(MoneroWalletConfig config) { + + // validate config + if (config == null) throw new MoneroError("Must specify config to open wallet"); + if (config.getSeed() == null && config.getPrimaryAddress() == null && config.getPrivateSpendKey() == null) throw new MoneroError("Must specify spend key or view key when opening a light wallet"); + if (config.getNetworkType() == null) throw new MoneroError("Must specify a network type: 'mainnet', 'testnet' or 'stagenet'"); + if (config.getRestoreHeight() != null) throw new MoneroError("Cannot specify restore height when opening wallet"); + if (config.getLanguage() != null) throw new MoneroError("Cannot specify language when opening wallet"); + if (Boolean.TRUE.equals(config.getSaveCurrent())) throw new MoneroError("Cannot save current wallet when opening light wallet"); + + // set server from connection manager if provided + if (config.getConnectionManager() != null) { + if (config.getServer() != null) throw new MoneroError("Wallet can be opened with a server or connection manager but not both"); + config.setServer(config.getConnectionManager().getConnection()); + } + + // read wallet data from disk unless provided + MoneroWalletLight wallet; + if (config.getKeysData() == null) { + wallet = openWallet(config, config.getServer()); + } else { + throw new MoneroError("Cannot open light wallet by keys data"); + } + + // set connection manager + wallet.setConnectionManager(config.getConnectionManager()); + return wallet; + } + + public static MoneroWalletLight createWallet(MoneroWalletConfig config) { + + // validate config + if (config == null) throw new MoneroError("Must specify config to open wallet"); + if (config.getNetworkType() == null) throw new MoneroError("Must specify a network type: 'mainnet', 'testnet' or 'stagenet'"); + if ((config.getServer() != null || config.getServerUri() != null) && MoneroWalletLight.walletExists(config)) throw new MoneroError("Wallet already exists"); + if (config.getSeed() != null && (config.getPrimaryAddress() != null || config.getPrivateViewKey() != null || config.getPrivateSpendKey() != null)) { + throw new MoneroError("Wallet may be initialized with a seed or keys but not both"); + } + if (Boolean.TRUE.equals(config.getSaveCurrent() != null)) throw new MoneroError("Cannot save current wallet when creating full wallet"); + + // set server from connection manager if provided + if (config.getConnectionManager() != null) { + if (config.getServer() != null) throw new MoneroError("Wallet can be created with a server or connection manager but not both"); + config.setServer(config.getConnectionManager().getConnection()); + } + + // create wallet + MoneroWalletLight wallet; + if (config.getSeed() != null) { + if (config.getLanguage() != null) throw new MoneroError("Cannot specify language when creating wallet from seed"); + wallet = createWalletFromSeed(config); + } else if (config.getPrimaryAddress() != null || config.getPrivateSpendKey() != null) { + if (config.getSeedOffset() != null) throw new MoneroError("Cannot specify seed offset when creating wallet from keys"); + wallet = createWalletFromKeys(config); + } else { + if (config.getSeedOffset() != null) throw new MoneroError("Cannot specify seed offset when creating random wallet"); + if (config.getRestoreHeight() != null) throw new MoneroError("Cannot specify restore height when creating random wallet"); + wallet = createWalletRandom(config); + } + + // set connection manager + wallet.setConnectionManager(config.getConnectionManager()); + return wallet; + } + + private static MoneroWalletLight createWalletFromSeed(MoneroWalletConfig config) { + //if (config.getRestoreHeight() == null) config.setRestoreHeight(0l); + long jniWalletHandle = createWalletJni(serializeWalletConfig(config)); + MoneroWalletLight wallet = new MoneroWalletLight(jniWalletHandle); + return wallet; + } + + private static MoneroWalletLight createWalletFromKeys(MoneroWalletConfig config) { + //if (config.getRestoreHeight() == null) config.setRestoreHeight(0l); + if (config.getLanguage() == null) config.setLanguage(DEFAULT_LANGUAGE); + try { + long jniWalletHandle = createWalletJni(serializeWalletConfig(config)); + MoneroWalletLight wallet = new MoneroWalletLight(jniWalletHandle); + return wallet; + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + private static MoneroWalletLight createWalletRandom(MoneroWalletConfig config) { + if (config.getLanguage() == null) config.setLanguage(DEFAULT_LANGUAGE); + long jniWalletHandle = createWalletJni(serializeWalletConfig(config)); + return new MoneroWalletLight(jniWalletHandle); + } + + private static String serializeWalletConfig(MoneroWalletConfig config) { + Map configMap = JsonUtils.toMap(config); + configMap.put("networkType", config.getNetworkType().ordinal()); + return JsonUtils.serialize(configMap); + } + + private MoneroWalletLight(long jniWalletHandle) { + super(jniWalletHandle); + } + + @Override + public List getOutputs(MoneroOutputQuery query) { + assertNotClosed(); + + // copy and normalize query up to block + if (query == null) query = new MoneroOutputQuery(); + else { + if (query.getTxQuery() == null) query = query.copy(); + else { + MoneroTxQuery txQuery = query.getTxQuery().copy(); + if (query.getTxQuery().getOutputQuery() == query) query = txQuery.getOutputQuery(); + else { + GenUtils.assertNull("Output query's tx query must be circular reference or null", query.getTxQuery().getOutputQuery()); + query = query.copy(); + query.setTxQuery(txQuery); + } + } + } + if (query.getTxQuery() == null) query.setTxQuery(new MoneroTxQuery()); + query.getTxQuery().setOutputQuery(query); + if (query.getTxQuery().getBlock() == null) query.getTxQuery().setBlock(new MoneroBlock().setTxs(query.getTxQuery())); + + // serialize query from block and fetch outputs from jni + String blocksJson = getOutputsJni(JsonUtils.serialize(query.getTxQuery().getBlock())); + + // deserialize and return outputs + return deserializeOutputs(query, blocksJson); + } + + @Override + public void changePassword(String oldPassword, String newPassword) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("change password not supported"); + } + + @Override + public void close(boolean save) { + if (isClosed) return; // closing a closed wallet has no effect + super.close(save); + refreshListening(); + try { + closeJni(save); + } catch (Exception e) { + throw new MoneroError(e.getMessage()); + } + } + + // ------------------------------ NATIVE METHODS ---------------------------- + + protected native static boolean walletExistsJni(String primaryAddress, String privateViewKey, String serverUri); + + //protected native static boolean walletExistsJni(String walletConfigJson, String serverUri); + + protected native static long openWalletJni(String walletConfigJson); + + protected native static long createWalletJni(String walletConfigJson); + + @Override + protected native long getHeightJni(); + + @Override + protected native long getRestoreHeightJni(); + + @Override + protected native void setRestoreHeightJni(long height); + + @Override + protected native long getDaemonHeightJni(); + + @Override + protected native long getDaemonMaxPeerHeightJni(); + + @Override + protected native long getHeightByDateJni(int year, int month, int day); + + @Override + protected native boolean isViewOnlyJni(); + + @Override + protected native void setDaemonConnectionJni(String uri, String username, String password, String proxyUri); + + @Override + protected native String[] getDaemonConnectionJni(); // returns [uri, username, password] + + @Override + protected native boolean isConnectedToDaemonJni(); + + @Override + protected native boolean isDaemonSyncedJni(); + + @Override + protected native boolean isSyncedJni(); + + @Override + protected native int getNetworkTypeJni(); + + @Override + protected native String getVersionJni(); + + @Override + protected native String getPathJni(); + + @Override + protected native String getSeedJni(); + + @Override + protected native String getSeedLanguageJni(); + + protected static native String[] getSeedLanguagesJni(); + + @Override + protected native String getPublicViewKeyJni(); + + @Override + protected native String getPrivateViewKeyJni(); + + @Override + protected native String getPublicSpendKeyJni(); + + @Override + protected native String getPrivateSpendKeyJni(); + + @Override + protected native String getAddressJni(int accountIdx, int subaddressIdx); + + @Override + protected native String getAddressIndexJni(String address); + + @Override + protected native String getIntegratedAddressJni(String standardAddress, String paymentId); + + @Override + protected native String decodeIntegratedAddressJni(String integratedAddress); + + @Override + protected native Object[] syncJni(long startHeight); + + @Override + protected native void startSyncingJni(long syncPeriodInMs); + + @Override + protected native void stopSyncingJni(); + + @Override + protected native void scanTxsJni(String[] txHashes); + + @Override + protected native void rescanSpentJni(); + + @Override + protected native void rescanBlockchainJni(); + + @Override + protected native String getBalanceWalletJni(); + + @Override + protected native String getBalanceAccountJni(int accountIdx); + + @Override + protected native String getBalanceSubaddressJni(int accountIdx, int subaddressIdx); + + @Override + protected native String getUnlockedBalanceWalletJni(); + + @Override + protected native String getUnlockedBalanceAccountJni(int accountIdx); + + @Override + protected native String getUnlockedBalanceSubaddressJni(int accountIdx, int subaddressIdx); + + @Override + protected native String getAccountsJni(boolean includeSubaddresses, String tag); + + @Override + protected native String getAccountJni(int accountIdx, boolean includeSubaddresses); + + @Override + protected native String createAccountJni(String label); + + @Override + protected native String getSubaddressesJni(int accountIdx, int[] subaddressIndices); + + @Override + protected native String createSubaddressJni(int accountIdx, String label); + + @Override + protected native void setSubaddressLabelJni(int accountIdx, int subaddressIdx, String label); + + @Override + protected native String getTxsJni(String txQueryJson); + + @Override + protected native String getTransfersJni(String transferQueryJson); + + @Override + protected native String getOutputsJni(String outputQueryJson); + + @Override + protected native String exportOutputsJni(boolean all); + + @Override + protected native int importOutputsJni(String outputsHex); + + @Override + protected native String exportKeyImagesJni(boolean all); + + @Override + protected native String importKeyImagesJni(String keyImagesJson); + + @Override + protected native String[] relayTxsJni(String[] txMetadatas); + + @Override + protected native void freezeOutputJni(String KeyImage); + + @Override + protected native void thawOutputJni(String keyImage); + + @Override + protected native boolean isOutputFrozenJni(String keyImage); + + @Override + protected native int getDefaultFeePriorityJni(); + + @Override + protected native String createTxsJni(String txConfigJson); + + @Override + protected native String sweepUnlockedJni(String txConfigJson); + + @Override + protected native String sweepOutputJni(String txConfigJson); + + @Override + protected native String sweepDustJni(boolean doNotRelay); + + @Override + protected native String describeTxSetJni(String txSetJson); + + @Override + protected native String signTxsJni(String unsignedTxHex); + + @Override + protected native String[] submitTxsJni(String signedTxHex); + + @Override + protected native String[] getTxNotesJni(String[] txHashes); + + @Override + protected native void setTxNotesJni(String[] txHashes, String[] notes); + + @Override + protected native String signMessageJni(String msg, int signatureType, int accountIdx, int subaddressIdx); + + @Override + protected native String verifyMessageJni(String msg, String address, String signature); + + @Override + protected native String getTxKeyJni(String txHash); + + @Override + protected native String checkTxKeyJni(String txHash, String txKey, String address); + + @Override + protected native String getTxProofJni(String txHash, String address, String message); + + @Override + protected native String checkTxProofJni(String txHash, String address, String message, String signature); + + @Override + protected native String getSpendProofJni(String txHash, String message); + + @Override + protected native boolean checkSpendProofJni(String txHash, String message, String signature); + + @Override + protected native String getReserveProofWalletJni(String message); + + @Override + protected native String getReserveProofAccountJni(int accountIdx, String amount, String message); + + @Override + protected native String checkReserveProofJni(String address, String message, String signature); + + @Override + protected native String getAddressBookEntriesJni(int[] indices); + + @Override + protected native int addAddressBookEntryJni(String address, String description); + + @Override + protected native void editAddressBookEntryJni(int index, boolean setAddress, String address, boolean setDescription, String description); + + @Override + protected native void deleteAddressBookEntryJni(int entryIdx); + + @Override + protected native String getPaymentUriJni(String sendRequestJson); + + @Override + protected native String parsePaymentUriJni(String uri); + + @Override + protected native String getAttributeJni(String key); + + @Override + protected native void setAttributeJni(String key, String val); + + @Override + protected native void startMiningJni(long numThreads, boolean backgroundMining, boolean ignoreBattery); + + @Override + protected native void stopMiningJni(); + + @Override + protected native boolean isMultisigImportNeededJni(); + + @Override + protected native String getMultisigInfoJni(); + + @Override + protected native String prepareMultisigJni(); + + @Override + protected native String makeMultisigJni(String[] multisigHexes, int threshold, String password); + + @Override + protected native String exchangeMultisigKeysJni(String[] multisigHexes, String password); + + @Override + protected native String exportMultisigHexJni(); + + @Override + protected native int importMultisigHexJni(String[] multisigHexes); + + @Override + protected native String signMultisigTxHexJni(String multisigTxHex); + + @Override + protected native String[] submitMultisigTxHexJni(String signedMultisigTxHex); + + @Override + protected native void changePasswordJni(String oldPassword, String newPassword); + + @Override + protected native void moveToJni(String path, String password); + + @Override + protected native void saveJni(); + + @Override + protected native void closeJni(boolean save); + + @Override + protected native long setListenerJni(MoneroWalletJni.WalletJniListener listener); +} \ No newline at end of file diff --git a/src/test/java/TestMoneroWalletCommon.java b/src/test/java/TestMoneroWalletCommon.java index 1c518d10..420d0c92 100644 --- a/src/test/java/TestMoneroWalletCommon.java +++ b/src/test/java/TestMoneroWalletCommon.java @@ -40,6 +40,8 @@ import monero.daemon.model.MoneroTx; import monero.daemon.model.MoneroVersion; import monero.wallet.MoneroWallet; +import monero.wallet.MoneroWalletFull; +import monero.wallet.MoneroWalletLight; import monero.wallet.MoneroWalletRpc; import monero.wallet.model.MoneroAccount; import monero.wallet.model.MoneroAddressBookEntry; @@ -81,8 +83,10 @@ import utils.Pair; import utils.StartMining; +import utils.SyncProgressTester; import utils.TestUtils; import utils.WalletEqualityUtils; +import utils.WalletSyncTester; /** * Runs common tests that every Monero wallet implementation should support. @@ -171,6 +175,7 @@ protected MoneroDaemonRpc getTestDaemon() { */ protected MoneroWallet openWallet(String path, String password) { return openWallet(new MoneroWalletConfig().setPath(path).setPassword(password)); } protected abstract MoneroWallet openWallet(MoneroWalletConfig config); + protected abstract MoneroWallet openWallet(MoneroWalletConfig config, boolean startSyncing); /** * Create a test wallet with default configuration for each wallet type. @@ -179,7 +184,8 @@ protected MoneroDaemonRpc getTestDaemon() { * @return MoneroWallet is the created wallet */ protected abstract MoneroWallet createWallet(MoneroWalletConfig config); - + protected abstract MoneroWallet createWallet(MoneroWalletConfig config, boolean startSyncing); + /** * Close a test wallet with customization for each wallet type. * @@ -196,9 +202,348 @@ protected MoneroDaemonRpc getTestDaemon() { * @return List are the wallet's supported languages */ protected abstract List getSeedLanguages(); + + public static String getRandomWalletPath() { + return TestUtils.TEST_WALLETS_DIR + "/test_wallet_" + System.currentTimeMillis(); + } + + // possible configuration: on chain xor local wallet data ("strict"), txs ordered same way? TBD + protected static void testWalletEqualityOnChain(MoneroWalletFull wallet1, MoneroWalletFull wallet2) { + WalletEqualityUtils.testWalletEqualityOnChain(wallet1, wallet2); + assertEquals(wallet1.getNetworkType(), wallet2.getNetworkType()); + assertEquals(wallet1.getRestoreHeight(), wallet2.getRestoreHeight()); + assertEquals(wallet1.getDaemonConnection(), wallet2.getDaemonConnection()); + assertEquals(wallet1.getSeedLanguage(), wallet2.getSeedLanguage()); + // TODO: more jni-specific extensions + } + + protected static void testWalletEqualityOnChain(MoneroWalletFull wallet1, MoneroWalletLight wallet2) { + WalletEqualityUtils.testWalletEqualityOnChain(wallet1, wallet2); + assertEquals(wallet1.getNetworkType(), wallet2.getNetworkType()); + assertEquals(wallet1.getSeedLanguage(), wallet2.getSeedLanguage()); + // TODO: more jni-specific extensions + } + protected static void testWalletEqualityOnChain(MoneroWalletFull wallet1, MoneroWallet wallet2) + { + if (wallet2 instanceof MoneroWalletFull) { + testWalletEqualityOnChain(wallet1, (MoneroWalletFull)wallet2); + } + else if (wallet2 instanceof MoneroWalletLight) { + testWalletEqualityOnChain(wallet1, (MoneroWalletLight)wallet2); + } + else throw new RuntimeException("Invalid wallet2 instance type"); + } + // ------------------------------ BEGIN TESTS ------------------------------- + // Can sync a wallet with a randomly generated seed + @Test + public void testSyncRandom() { + assumeTrue(TEST_NON_RELAYS); + assertTrue(daemon.isConnected(), "Not connected to daemon"); + + // create test wallet + MoneroWallet wallet = createWallet(new MoneroWalletConfig(), false); + long restoreHeight = daemon.getHeight(); + + // test wallet's height before syncing + if (wallet instanceof MoneroWalletFull) assertEquals(TestUtils.getDaemonRpc().getRpcConnection(), wallet.getDaemonConnection()); + if (!(wallet instanceof MoneroWalletLight)) assertEquals(restoreHeight, wallet.getDaemonHeight()); + assertTrue(wallet.isConnectedToDaemon()); + if (wallet instanceof MoneroWalletFull) assertFalse(((MoneroWalletFull)wallet).isSynced()); + else if (wallet instanceof MoneroWalletLight) assertFalse(((MoneroWalletLight)wallet).isSynced()); + assertEquals(1, wallet.getHeight()); + if (wallet instanceof MoneroWalletFull) assertEquals(restoreHeight, ((MoneroWalletFull)wallet).getRestoreHeight()); + else if (wallet instanceof MoneroWalletLight) assertEquals(wallet.getDaemonHeight(), ((MoneroWalletLight)wallet).getRestoreHeight()); + if (wallet instanceof MoneroWalletFull) assertEquals(daemon.getHeight(), wallet.getDaemonHeight()); + + MoneroSyncResult result; + + // sync the wallet + if (wallet instanceof MoneroWalletFull) + { + SyncProgressTester progressTester = new SyncProgressTester(wallet, ((MoneroWalletFull)wallet).getRestoreHeight(), wallet.getDaemonHeight()); + result = wallet.sync(null, progressTester); + progressTester.onDone(wallet.getDaemonHeight()); + } + else result = syncWithDaemon(wallet); + + // test result after syncing + MoneroWalletFull walletGt = TestUtils.createWalletGroundTruth(TestUtils.NETWORK_TYPE, wallet.getSeed(), null, restoreHeight); + walletGt.sync(); + try { + assertTrue(wallet.isConnectedToDaemon()); + if (wallet instanceof MoneroWalletFull) assertTrue(((MoneroWalletFull)wallet).isSynced()); + else if (wallet instanceof MoneroWalletLight) assertTrue(((MoneroWalletLight)wallet).isSynced()); + if (wallet instanceof MoneroWalletFull) assertEquals(0, (long) result.getNumBlocksFetched()); + assertFalse(result.getReceivedMoney()); + assertEquals(daemon.getHeight(), wallet.getHeight()); + + // sync the wallet with default params + if (wallet instanceof MoneroWalletLight) syncWithDaemon(wallet); + else wallet.sync(); + if (wallet instanceof MoneroWalletFull) assertTrue(((MoneroWalletFull)wallet).isSynced()); + else if (wallet instanceof MoneroWalletLight) assertTrue(((MoneroWalletLight)wallet).isSynced()); + + assertEquals(daemon.getHeight(), wallet.getHeight()); + + // compare wallet to ground truth + testWalletEqualityOnChain(walletGt, wallet); + } finally { + if (walletGt != null) walletGt.close(!(wallet instanceof MoneroWalletLight)); + wallet.close(); + } + + // attempt to sync unconnected wallet + wallet = createWallet(new MoneroWalletConfig().setServerUri(TestUtils.OFFLINE_SERVER_URI)); + try { + wallet.sync(); + fail("Should have thrown exception"); + } catch (MoneroError e) { + assertEquals("Wallet is not connected to daemon", e.getMessage()); + } finally { + wallet.close(); + } + } + + // Can re-sync an existing wallet from scratch + @Test + @Disabled // TODO monero-project: cannot re-sync from lower block height after wallet saved + public void testResyncExisting() { + Boolean isLightWallet = wallet instanceof MoneroWalletLight; + if (isLightWallet) assertTrue(MoneroWalletLight.walletExists(TestUtils.getWalletLightConfig())); + else assertTrue(MoneroWalletFull.walletExists(TestUtils.WALLET_FULL_PATH)); + MoneroWallet wallet; + if (isLightWallet) wallet = openWallet(new MoneroWalletConfig().setSeed(TestUtils.SEED).setServerUri(TestUtils.OFFLINE_SERVER_URI), false); + else wallet = openWallet(new MoneroWalletConfig().setPath(TestUtils.WALLET_FULL_PATH).setServerUri(TestUtils.OFFLINE_SERVER_URI), false); + wallet.setDaemonConnection(isLightWallet ? new MoneroRpcConnection(TestUtils.WALLET_LWS_URI) : TestUtils.getDaemonRpc().getRpcConnection()); + //long startHeight = TestUtils.TEST_RESTORE_HEIGHT; + long startHeight = isLightWallet ? TestUtils.FIRST_RECEIVE_HEIGHT : 0; + SyncProgressTester progressTester = new SyncProgressTester(wallet, startHeight, wallet.getDaemonHeight()); + if (wallet instanceof MoneroWalletFull) ((MoneroWalletFull)wallet).setRestoreHeight(1); + MoneroSyncResult result = wallet.sync(isLightWallet ? startHeight : 1l, progressTester); + progressTester.onDone(wallet.getDaemonHeight()); + + // test result after syncing + assertTrue(wallet.isConnectedToDaemon()); + if (wallet instanceof MoneroWalletFull) assertTrue(((MoneroWalletFull)wallet).isSynced()); + else if (wallet instanceof MoneroWalletLight) assertTrue(((MoneroWalletLight)wallet).isSynced()); + assertEquals(wallet.getDaemonHeight() - startHeight, (long) result.getNumBlocksFetched()); + assertTrue(result.getReceivedMoney()); + assertEquals(daemon.getHeight(), wallet.getHeight()); + wallet.close(!isLightWallet); + } + + // Can sync a wallet created from keys + @Test + public void testSyncWalletFromKeys() { + assumeTrue(TEST_NON_RELAYS); + + // recreate test wallet from keys + String path = getRandomWalletPath(); + MoneroWalletConfig config = new MoneroWalletConfig().setPrimaryAddress(wallet.getPrimaryAddress()).setPrivateViewKey(wallet.getPrivateViewKey()).setPrivateSpendKey(wallet.getPrivateSpendKey()); + boolean isLightWallet = wallet instanceof MoneroWalletLight; + if (!isLightWallet) config.setPath(path).setRestoreHeight(TestUtils.FIRST_RECEIVE_HEIGHT); + else config.setServerUri(TestUtils.WALLET_LWS_URI); + MoneroWallet walletKeys = isLightWallet ? openWallet(config, false) : createWallet(config, false); + + // create ground truth wallet for comparison + MoneroWalletFull walletGt = TestUtils.createWalletGroundTruth(TestUtils.NETWORK_TYPE, TestUtils.SEED, null, TestUtils.FIRST_RECEIVE_HEIGHT); + + // test wallet and close as final step + try { + assertEquals(walletKeys.getSeed(), walletGt.getSeed()); + assertEquals(walletKeys.getPrimaryAddress(), walletGt.getPrimaryAddress()); + assertEquals(walletKeys.getPrivateViewKey(), walletGt.getPrivateViewKey()); + assertEquals(walletKeys.getPublicViewKey(), walletGt.getPublicViewKey()); + assertEquals(walletKeys.getPrivateSpendKey(), walletGt.getPrivateSpendKey()); + assertEquals(walletKeys.getPublicSpendKey(), walletGt.getPublicSpendKey()); + assertEquals(TestUtils.FIRST_RECEIVE_HEIGHT, walletGt.getRestoreHeight()); + assertTrue(walletKeys.isConnectedToDaemon()); + if (wallet instanceof MoneroWalletFull) assertFalse(((MoneroWalletFull)walletKeys).isSynced()); + else if (wallet instanceof MoneroWalletLight) assertFalse(((MoneroWalletLight)walletKeys).isSynced()); + + // sync the wallet + Long maxPeerHeight = 0l; + if (walletKeys instanceof MoneroWalletFull) maxPeerHeight = ((MoneroWalletFull)walletKeys).getDaemonMaxPeerHeight(); + else if (walletKeys instanceof MoneroWalletLight) maxPeerHeight = ((MoneroWalletLight)walletKeys).getDaemonMaxPeerHeight(); + SyncProgressTester progressTester = new SyncProgressTester(walletKeys, TestUtils.FIRST_RECEIVE_HEIGHT, maxPeerHeight); + MoneroSyncResult result = walletKeys.sync(progressTester); + progressTester.onDone(walletKeys.getDaemonHeight()); + + // test result after syncing + if (wallet instanceof MoneroWalletFull) assertTrue(((MoneroWalletFull)walletKeys).isSynced()); + if (wallet instanceof MoneroWalletLight) assertTrue(((MoneroWalletLight)walletKeys).isSynced()); + Long height = TestUtils.FIRST_RECEIVE_HEIGHT; + assertEquals(walletKeys.getDaemonHeight() - height, (long) result.getNumBlocksFetched()); + assertTrue(result.getReceivedMoney()); + assertEquals(daemon.getHeight(), walletKeys.getHeight()); + assertEquals(daemon.getHeight(), walletKeys.getDaemonHeight()); + assertEquals(TestUtils.FIRST_RECEIVE_HEIGHT, (long) walletKeys.getTxs().get(0).getHeight()); // wallet should be fully synced so first tx happens on true restore height + + // compare with ground truth + testWalletEqualityOnChain(walletGt, walletKeys); + } finally { + walletGt.close(true); + walletKeys.close(); + } + + // TODO monero-project: importing key images can cause erasure of incoming transfers per wallet2.cpp:11957 which causes this test to fail +// // sync the wallets until same height +// while (wallet.getHeight() != walletKeys.getHeight()) { +// wallet.sync(); +// walletKeys.sync(new WalletSyncPrinter()); +// } +// +// List keyImages = walletKeys.exportKeyImages(); +// walletKeys.importKeyImages(keyImages); + } + + // TODO: test start syncing, notification of syncs happening, stop syncing, no notifications, etc + // Can start and stop syncing + @Test + public void testStartStopSyncing() { + assumeTrue(TEST_NON_RELAYS); + + // test unconnected wallet + String path = getRandomWalletPath(); + MoneroWallet wallet = createWallet(new MoneroWalletConfig().setServerUri(TestUtils.OFFLINE_SERVER_URI)); + Boolean isLightWallet = wallet instanceof MoneroWalletLight; + try { + assertNotNull(wallet.getSeed()); + assertEquals(1, wallet.getHeight()); + assertEquals(BigInteger.valueOf(0), wallet.getBalance()); + wallet.startSyncing(); + } catch (MoneroError e) { + assertEquals("Wallet is not connected to daemon", e.getMessage()); + } finally { + wallet.close(); + } + + // test connecting wallet + path = getRandomWalletPath(); + MoneroWalletConfig config = new MoneroWalletConfig().setServerUri(TestUtils.OFFLINE_SERVER_URI); + wallet = isLightWallet ? createWallet(config) : createWallet(config.setPath(path)); + try { + assertNotNull(wallet.getSeed()); + MoneroRpcConnection connection = isLightWallet ? new MoneroRpcConnection(TestUtils.WALLET_LWS_URI) : daemon.getRpcConnection(); + wallet.setDaemonConnection(connection); + assertEquals(1, wallet.getHeight()); + if (wallet instanceof MoneroWalletFull) assertFalse(((MoneroWalletFull)wallet).isSynced()); + else if (wallet instanceof MoneroWalletLight) assertFalse(((MoneroWalletLight)wallet).isSynced()); + assertEquals(BigInteger.valueOf(0), wallet.getBalance()); + long chainHeight = wallet.getDaemonHeight(); + if (wallet instanceof MoneroWalletFull) ((MoneroWalletFull)wallet).setRestoreHeight(chainHeight - 3); + wallet.startSyncing(); + if (wallet instanceof MoneroWalletFull) assertEquals(chainHeight - 3, ((MoneroWalletFull)wallet).getRestoreHeight()); + assertEquals(connection, wallet.getDaemonConnection()); + wallet.stopSyncing(); + wallet.sync(); + wallet.stopSyncing(); + wallet.stopSyncing(); + } finally { + wallet.close(); + } + + // test that sync starts automatically + long restoreHeight = isLightWallet ? 0 : daemon.getHeight() - 100; + path = getRandomWalletPath(); + config = new MoneroWalletConfig(); + if (!isLightWallet) config.setPath(path).setSeed(TestUtils.SEED).setRestoreHeight(restoreHeight); + wallet = createWallet(config, false); + try { + + // start syncing + assertEquals(1, wallet.getHeight()); + if (wallet instanceof MoneroWalletFull) assertEquals(restoreHeight, ((MoneroWalletFull)wallet).getRestoreHeight()); + if (wallet instanceof MoneroWalletLight) assertEquals(restoreHeight, ((MoneroWalletLight)wallet).getRestoreHeight()); + + if (wallet instanceof MoneroWalletFull) assertFalse(((MoneroWalletFull)wallet).isSynced()); + if (wallet instanceof MoneroWalletLight) assertFalse(((MoneroWalletLight)wallet).isSynced()); + assertEquals(BigInteger.valueOf(0), wallet.getBalance()); + wallet.startSyncing(TestUtils.SYNC_PERIOD_IN_MS); + + // pause for sync to start + try { + System.out.println("Sleeping to test that sync starts automatically..."); + TimeUnit.MILLISECONDS.sleep(TestUtils.SYNC_PERIOD_IN_MS + 1000); + } catch (InterruptedException e) { + e.printStackTrace(); + throw new RuntimeException(e.getMessage()); + } + + // test that wallet has started syncing + assertTrue(wallet.getHeight() > 1); + + // stop syncing + wallet.stopSyncing(); + + // TODO monero-project: wallet.cpp m_synchronized only ever set to true, never false +// // wait for block to be added to chain +// daemon.getNextBlockHeader(); +// +// // wallet is no longer synced +// assertFalse(wallet.isSynced()); + } finally { + wallet.close(); + } + } + // Can get the daemon's max peer height + @Test + public void testGetDaemonMaxPeerHeight() { + assumeTrue(TEST_NON_RELAYS); + long height = 0; + if (wallet instanceof MoneroWalletFull) + { + height = ((MoneroWalletFull) wallet).getDaemonMaxPeerHeight(); + } + else if (wallet instanceof MoneroWalletLight) + { + height = ((MoneroWalletLight) wallet).getDaemonMaxPeerHeight(); + } + + assertTrue(height > 0); + } + + // Can get the daemon's height + @Test + public void testDaemon() { + assumeTrue(TEST_NON_RELAYS); + assertTrue(wallet.isConnectedToDaemon()); + long daemonHeight = wallet.getDaemonHeight(); + assertTrue(daemonHeight > 0); + } + + // Does not leak memory + @Test + public void testMemoryLeak() { + System.out.println("Infinite loop starting, monitor memory in OS process manager..."); + System.gc(); + int i = 0; + boolean closeWallet = false; + long time = System.currentTimeMillis(); + if (closeWallet) wallet.close(true); + while (true) { + if (closeWallet) { + if (wallet instanceof MoneroWalletFull) wallet = TestUtils.getWalletFull(); + else if (wallet instanceof MoneroWalletLight) wallet = TestUtils.getWalletLight(); + } + wallet.sync(); + wallet.getTxs(); + wallet.getTransfers(); + wallet.getOutputs(new MoneroOutputQuery().setIsSpent(false)); + if (i % 100 == 0) { + System.out.println("Garbage collecting on iteration " + i); + System.gc(); + System.out.println((System.currentTimeMillis() - time) + " ms since last GC"); + time = System.currentTimeMillis(); + } + if (closeWallet) wallet.close(true); + i++; + } + } + // Can create a random wallet @Test public void testCreateWalletRandom() { @@ -208,7 +553,9 @@ public void testCreateWalletRandom() { // create random wallet MoneroWallet wallet = createWallet(new MoneroWalletConfig()); - String path = wallet.getPath(); + String path = ""; + if (!(wallet instanceof MoneroWalletLight)) path = wallet.getPath(); + String seed = wallet.getSeed(); Exception e2 = null; try { MoneroUtils.validateAddress(wallet.getPrimaryAddress(), TestUtils.NETWORK_TYPE); @@ -222,12 +569,16 @@ public void testCreateWalletRandom() { closeWallet(wallet); if (e2 != null) throw e2; - // attempt to create wallet at same path + // attempt to create wallet at same path/with same seed try { - createWallet(new MoneroWalletConfig().setPath(path)); + MoneroWalletConfig config = new MoneroWalletConfig(); + if (wallet instanceof MoneroWalletLight) config.setSeed(seed); + else config.setPath(path); + createWallet(config); throw new Error("Should have thrown error"); } catch(Exception e) { - assertEquals("Wallet already exists: " + path, e.getMessage()); + if (path.isEmpty()) assertEquals("Wallet already exists" + path, e.getMessage()); + else assertEquals("Wallet already exists: " + path, e.getMessage()); } // attempt to create wallet with unknown language @@ -250,15 +601,16 @@ public void testCreateWalletFromSeed() { assumeTrue(TEST_NON_RELAYS); Exception e1 = null; // emulating Java "finally" but compatible with other languages try { - + Boolean isLightWallet = wallet instanceof MoneroWalletLight; // save for comparison String primaryAddress = wallet.getPrimaryAddress(); String privateViewKey = wallet.getPrivateViewKey(); String privateSpendKey = wallet.getPrivateSpendKey(); // recreate test wallet from seed - MoneroWallet wallet = createWallet(new MoneroWalletConfig().setSeed(TestUtils.SEED).setRestoreHeight(TestUtils.FIRST_RECEIVE_HEIGHT)); - String path = wallet.getPath(); + MoneroWalletConfig walletConfig = new MoneroWalletConfig().setSeed(TestUtils.SEED); + MoneroWallet wallet = (isLightWallet) ? openWallet(walletConfig) : createWallet(walletConfig.setRestoreHeight(TestUtils.FIRST_RECEIVE_HEIGHT)); + String path = (isLightWallet) ? "" : wallet.getPath(); Exception e2 = null; try { assertEquals(primaryAddress, wallet.getPrimaryAddress()); @@ -282,10 +634,12 @@ public void testCreateWalletFromSeed() { // attempt to create wallet at same path try { - createWallet(new MoneroWalletConfig().setPath(path)); + if (wallet instanceof MoneroWalletLight) createWallet(new MoneroWalletConfig().setSeed(TestUtils.SEED)); + else createWallet(new MoneroWalletConfig().setPath(path)); throw new RuntimeException("Should have thrown error"); } catch (Exception e) { - assertEquals("Wallet already exists: " + path, e.getMessage()); + if (wallet instanceof MoneroWalletLight) assertEquals("Wallet already exists", e.getMessage()); + else assertEquals("Wallet already exists: " + path, e.getMessage()); } } catch (Exception e) { e1 = e; @@ -297,29 +651,7 @@ public void testCreateWalletFromSeed() { // Can create a wallet from a seed with a seed offset @Test public void testCreateWalletFromSeedWithOffset() { - assumeTrue(TEST_NON_RELAYS); - Exception e1 = null; // emulating Java "finally" but compatible with other languages - try { - - // create test wallet with offset - MoneroWallet wallet = createWallet(new MoneroWalletConfig().setSeed(TestUtils.SEED).setRestoreHeight(TestUtils.FIRST_RECEIVE_HEIGHT).setSeedOffset("my secret offset!")); - Exception e2 = null; - try { - MoneroUtils.validateMnemonic(wallet.getSeed()); - assertNotEquals(TestUtils.SEED, wallet.getSeed()); - MoneroUtils.validateAddress(wallet.getPrimaryAddress(), TestUtils.NETWORK_TYPE); - assertNotEquals(TestUtils.ADDRESS, wallet.getPrimaryAddress()); - if (!(wallet instanceof MoneroWalletRpc)) assertEquals(MoneroWallet.DEFAULT_LANGUAGE, wallet.getSeedLanguage()); // TODO monero-wallet-rpc: support - } catch (Exception e) { - e2 = e; - } - closeWallet(wallet); - if (e2 != null) throw e2; - } catch (Exception e) { - e1 = e; - } - - if (e1 != null) throw new RuntimeException(e1); + testCreateWalletFromSeedWithOffset("my secret offset!"); } // Can create a wallet from keys @@ -333,10 +665,11 @@ public void testCreateWalletFromKeys() { String primaryAddress = wallet.getPrimaryAddress(); String privateViewKey = wallet.getPrivateViewKey(); String privateSpendKey = wallet.getPrivateSpendKey(); - + MoneroWalletConfig config = new MoneroWalletConfig().setPrimaryAddress(primaryAddress).setPrivateViewKey(privateViewKey).setPrivateSpendKey(privateSpendKey); + Boolean isLightWallet = wallet instanceof MoneroWalletLight; // recreate test wallet from keys - MoneroWallet wallet = createWallet(new MoneroWalletConfig().setPrimaryAddress(primaryAddress).setPrivateViewKey(privateViewKey).setPrivateSpendKey(privateSpendKey).setRestoreHeight(daemon.getHeight())); - String path = wallet.getPath(); + MoneroWallet wallet = isLightWallet ? openWallet(config) : createWallet(config.setRestoreHeight(daemon.getHeight())); + String path = isLightWallet ? "" : wallet.getPath(); Exception e2 = null; try { assertEquals(primaryAddress, wallet.getPrimaryAddress()); @@ -356,7 +689,8 @@ public void testCreateWalletFromKeys() { // recreate test wallet from spend key if (!(wallet instanceof MoneroWalletRpc)) { // TODO monero-wallet-rpc: cannot create wallet from spend key? - wallet = createWallet(new MoneroWalletConfig().setPrivateSpendKey(privateSpendKey).setRestoreHeight(daemon.getHeight())); + config = new MoneroWalletConfig().setPrivateSpendKey(privateSpendKey); + wallet = isLightWallet ? openWallet(config) : createWallet(config.setRestoreHeight(daemon.getHeight())); e2 = null; try { assertEquals(primaryAddress, wallet.getPrimaryAddress()); @@ -377,10 +711,14 @@ public void testCreateWalletFromKeys() { // attempt to create wallet at same path try { - createWallet(new MoneroWalletConfig().setPath(path)); + config = new MoneroWalletConfig(); + if (isLightWallet) config.setSeed(TestUtils.SEED); + else config.setPath(path); + createWallet(config); throw new Error("Should have thrown error"); } catch(Exception e) { - assertEquals("Wallet already exists: " + path, e.getMessage()); + if (isLightWallet) assertEquals("Wallet already exists", e.getMessage()); + else assertEquals("Wallet already exists: " + path, e.getMessage()); } } catch (Exception e) { e1 = e; @@ -406,9 +744,17 @@ public void testSubaddressLookahead() { .addDestination(receiver.getSubaddress(0, 85000).getAddress(), TestUtils.MAX_FEE) .setRelay(true)); - // observe unconfirmed funds - GenUtils.waitFor(1000); - receiver.sync(); + if (wallet instanceof MoneroWalletLight) + { + // light wallet cannot observe unconfirmed funds, must wait for next block + waitForNextBlockHeader(); + syncWithDaemon(receiver); + } + else { + // observe unconfirmed funds + GenUtils.waitFor(1000); + receiver.sync(); + } assert(receiver.getBalance().compareTo(new BigInteger("0")) > 0); } catch (Exception e) { e1 = e; @@ -455,10 +801,10 @@ public void testGetPath() { // Can set the daemon connection @Test public void testSetDaemonConnection() { - + String daemonUri = (wallet instanceof MoneroWalletLight) ? TestUtils.WALLET_LWS_URI : TestUtils.DAEMON_RPC_URI; // create random wallet with default daemon connection MoneroWallet wallet = createWallet(new MoneroWalletConfig()); - assertEquals(new MoneroRpcConnection(TestUtils.DAEMON_RPC_URI, TestUtils.DAEMON_RPC_USERNAME, TestUtils.DAEMON_RPC_PASSWORD), wallet.getDaemonConnection()); + assertEquals(new MoneroRpcConnection(daemonUri, TestUtils.DAEMON_RPC_USERNAME, TestUtils.DAEMON_RPC_PASSWORD), wallet.getDaemonConnection()); assertTrue(wallet.isConnectedToDaemon()); // uses default localhost connection // set empty server uri @@ -472,21 +818,21 @@ public void testSetDaemonConnection() { assertFalse(wallet.isConnectedToDaemon()); // set daemon with wrong credentials - wallet.setDaemonConnection(TestUtils.DAEMON_RPC_URI, "wronguser", "wrongpass"); - assertEquals(new MoneroRpcConnection(TestUtils.DAEMON_RPC_URI, "wronguser", "wrongpass"), wallet.getDaemonConnection()); + wallet.setDaemonConnection(daemonUri, "wronguser", "wrongpass"); + assertEquals(new MoneroRpcConnection(daemonUri, "wronguser", "wrongpass"), wallet.getDaemonConnection()); if ("".equals(TestUtils.DAEMON_RPC_USERNAME) || TestUtils.DAEMON_RPC_USERNAME == null) assertTrue(wallet.isConnectedToDaemon()); // TODO: monerod without authentication works with bad credentials? else assertFalse(wallet.isConnectedToDaemon()); // set daemon with authentication - wallet.setDaemonConnection(TestUtils.DAEMON_RPC_URI, TestUtils.DAEMON_RPC_USERNAME, TestUtils.DAEMON_RPC_PASSWORD); - assertEquals(new MoneroRpcConnection(TestUtils.DAEMON_RPC_URI, TestUtils.DAEMON_RPC_USERNAME, TestUtils.DAEMON_RPC_PASSWORD), wallet.getDaemonConnection()); + wallet.setDaemonConnection(daemonUri, TestUtils.DAEMON_RPC_USERNAME, TestUtils.DAEMON_RPC_PASSWORD); + assertEquals(new MoneroRpcConnection(daemonUri, TestUtils.DAEMON_RPC_USERNAME, TestUtils.DAEMON_RPC_PASSWORD), wallet.getDaemonConnection()); assertTrue(wallet.isConnectedToDaemon()); // nullify daemon connection wallet.setDaemonConnection((String) null); assertEquals(null, wallet.getDaemonConnection()); - wallet.setDaemonConnection(TestUtils.DAEMON_RPC_URI); - assertEquals(new MoneroRpcConnection(TestUtils.DAEMON_RPC_URI), wallet.getDaemonConnection()); + wallet.setDaemonConnection(daemonUri); + assertEquals(new MoneroRpcConnection(daemonUri), wallet.getDaemonConnection()); wallet.setDaemonConnection((MoneroRpcConnection) null); assertEquals(null, wallet.getDaemonConnection()); @@ -1320,7 +1666,9 @@ else if (tx.getIncomingTransfers() != null) { for (MoneroTxWallet tx : txs) { assertFalse(tx.isLocked()); } - + + if (wallet instanceof MoneroWalletLight) return; + // get confirmed transactions sent from/to same wallet with a transfer with destinations txs = wallet.getTxs(new MoneroTxQuery().setIsIncoming(true).setIsOutgoing(true).setIncludeOutputs(true).setIsConfirmed(true).setTransferQuery(new MoneroTransferQuery().setHasDestinations(true))); assertFalse(txs.isEmpty()); @@ -1934,7 +2282,6 @@ public void testImportOutputs() { @Test public void testAccounting() { assumeTrue(TEST_NON_RELAYS); - // pre-fetch wallet balances, accounts, subaddresses, and txs BigInteger walletBalance = wallet.getBalance(); BigInteger walletUnlockedBalance = wallet.getUnlockedBalance(); @@ -2501,7 +2848,7 @@ protected void testViewOnlyAndOfflineWallets(MoneroWallet viewOnlyWallet, Monero // wait for txs to confirm and for sufficient unlocked balance TestUtils.WALLET_TX_TRACKER.waitForTxsToClearPool(wallet, viewOnlyWallet); TestUtils.WALLET_TX_TRACKER.waitForUnlockedBalance(wallet, 0, null, TestUtils.MAX_FEE.multiply(new BigInteger("4"))); - + // test getting txs, transfers, and outputs from view-only wallet assertFalse(viewOnlyWallet.getTxs().isEmpty()); assertFalse(viewOnlyWallet.getTransfers().isEmpty()); @@ -2535,7 +2882,7 @@ protected void testViewOnlyAndOfflineWallets(MoneroWallet viewOnlyWallet, Monero assertNotEquals(errMsg, e.getMessage()); } assertTrue(viewOnlyWallet.isConnectedToDaemon()); // TODO: this fails with monero-wallet-rpc and monerod with authentication - viewOnlyWallet.sync(); + syncWithDaemon(viewOnlyWallet); assertTrue(viewOnlyWallet.getTxs().size() > 0); // export outputs from view-only wallet @@ -3137,17 +3484,19 @@ public void testSendToSelf() { BigInteger balance1 = wallet.getBalance(); BigInteger unlockedBalance1 = wallet.getUnlockedBalance(); - // test error sending funds to self with integrated subaddress - // TODO (monero-project): sending funds to self with integrated subaddress throws error: https://github.com/monero-project/monero/issues/8380 - try { - wallet.createTx(new MoneroTxConfig() - .setAccountIndex(0) - .setAddress(MoneroUtils.getIntegratedAddress(TestUtils.NETWORK_TYPE, wallet.getSubaddress(0, 1).getAddress(), null).getIntegratedAddress()) - .setAmount(amount) - .setRelay(true)); - throw new RuntimeException("Should have failed sending to self with integrated subaddress"); - } catch (MoneroError err) { - if (!err.getMessage().contains("Total received by")) throw err; + if (!(wallet instanceof MoneroWalletLight)) { + // test error sending funds to self with integrated subaddress + // TODO (monero-project): sending funds to self with integrated subaddress throws error: https://github.com/monero-project/monero/issues/8380 + try { + wallet.createTx(new MoneroTxConfig() + .setAccountIndex(0) + .setAddress(MoneroUtils.getIntegratedAddress(TestUtils.NETWORK_TYPE, wallet.getSubaddress(0, 1).getAddress(), null).getIntegratedAddress()) + .setAmount(amount) + .setRelay(true)); + throw new RuntimeException("Should have failed sending to self with integrated subaddress"); + } catch (MoneroError err) { + if (!err.getMessage().contains("Total received by")) throw err; + } } // send funds to self @@ -3157,6 +3506,8 @@ public void testSendToSelf() { .setAmount(amount) .setRelay(true)); + if (wallet instanceof MoneroWalletLight) syncWithDaemon(wallet); + // test balances after BigInteger balance2 = wallet.getBalance(); BigInteger unlockedBalance2 = wallet.getUnlockedBalance(); @@ -3180,6 +3531,8 @@ public void testSendToExternal() { // create recipient wallet recipient = createWallet(new MoneroWalletConfig()); + + if (recipient instanceof MoneroWalletLight) syncWithDaemon(recipient); // collect sender balances before BigInteger balance1 = wallet.getBalance(); @@ -3200,6 +3553,12 @@ public void testSendToExternal() { assertEquals(expectedBalance, balance2, "Balance after send was not balance before - net tx amount - fee (5 - 1 != 4 test)"); // test recipient balance after + if (recipient instanceof MoneroWalletLight) + { + waitForNextBlockHeader(); + syncWithDaemon(recipient); + } + recipient.sync(); assertFalse(wallet.getTxs(new MoneroTxQuery().setIsConfirmed(false)).isEmpty()); assertEquals(amount, recipient.getBalance()); @@ -4764,33 +5123,8 @@ private void testSweepWallet(Boolean sweepEachSubaddress) { // Can scan transactions by id @Test public void testScanTxs() { - - // get a few tx hashes - List txHashes = new ArrayList(); - List txs = wallet.getTxs(); - if (txs.size() < 3) fail("Not enough txs to scan"); - for (int i = 0; i < 3; i++) txHashes.add(txs.get(i).getHash()); - - // start wallet without scanning MoneroWallet scanWallet = createWallet(new MoneroWalletConfig().setSeed(wallet.getSeed()).setRestoreHeight(0l)); - scanWallet.stopSyncing(); // TODO: create wallet without daemon connection (offline does not reconnect, default connects to localhost, offline then online causes confirmed txs to disappear) - assertTrue(scanWallet.isConnectedToDaemon()); - - // scan txs - scanWallet.scanTxs(txHashes); - - // TODO: scanning txs causes merge problems reconciling 0 fee, isMinerTx with test txs - -// // txs are scanned -// assertEquals(txHashes.size(), scanWallet.getTxs().size()); -// for (int i = 0; i < txHashes.size(); i++) { -// assertEquals(wallet.getTx(txHashes.get(i)), scanWallet.getTx(txHashes.get(i))); -// } -// List scannedTxs = scanWallet.getTxs(txHashes); -// assertEquals(txHashes.size(), scannedTxs.size()); - - // close wallet - closeWallet(scanWallet, false); + testScanTxs(scanWallet); } // Can rescan the blockchain @@ -5186,12 +5520,14 @@ public void testCreateAndReceive() { try { StartMining.startMining(); } catch (Exception e) { } while (!(wallet.getTx(sentTx.getHash())).isConfirmed()) { if (wallet.getTx(sentTx.getHash()).isFailed()) throw new Error("Tx failed in mempool: " + sentTx.getHash()); - daemon.waitForNextBlockHeader(); + waitForNextBlockHeader(); } // receiver should have notified listeners of received outputs try { TimeUnit.MILLISECONDS.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } // zmq notifications received within 1 second - assertFalse(myListener.getOutputsReceived().isEmpty()); + // TODO remove this once listener is fully supported by light wallet + if (wallet instanceof MoneroWalletLight) assertTrue(myListener.getOutputsReceived().isEmpty()); + else assertFalse(myListener.getOutputsReceived().isEmpty()); } finally { closeWallet(receiver); try { daemon.stopMining(); } catch (Exception e) { } @@ -5226,12 +5562,15 @@ public void testFreezeOutputs() { assertTrue(outputFrozen.isFrozen()); assertEquals(output.getKeyImage().getHex(), outputFrozen.getKeyImage().getHex()); - // try to sweep frozen output - try { - wallet.sweepOutput(new MoneroTxConfig().setAddress(wallet.getPrimaryAddress()).setKeyImage(output.getKeyImage().getHex())); - fail("Should have thrown error"); - } catch (MoneroError e) { - assertEquals("No outputs found", e.getMessage()); + if (!(wallet instanceof MoneroWalletLight)) + { + // try to sweep frozen output, not supported by light wallet + try { + wallet.sweepOutput(new MoneroTxConfig().setAddress(wallet.getPrimaryAddress()).setKeyImage(output.getKeyImage().getHex())); + fail("Should have thrown error"); + } catch (MoneroError e) { + assertEquals("No outputs found", e.getMessage()); + } } // try to freeze null key image @@ -5392,6 +5731,41 @@ public void testGetDefaultFeePriority() { // --------------------------------- HELPERS -------------------------------- + protected void syncWithDaemon() + { + syncWithDaemon(null); + } + + protected MoneroSyncResult syncWithDaemon(MoneroWallet w) { + MoneroBlockHeader header = daemon.getLastBlockHeader(); + MoneroWallet _wallet = w == null ? wallet : w; + MoneroSyncResult result = new MoneroSyncResult(); + result.setNumBlocksFetched(0l); + result.setReceivedMoney(false); + + while(_wallet.getHeight() < header.getHeight()) + { + result = _wallet.sync(); + GenUtils.waitFor(TestUtils.SYNC_PERIOD_IN_MS); + header = daemon.getLastBlockHeader(); + } + + return result; + } + + protected MoneroBlockHeader waitForNextBlockHeader() + { + MoneroMiningStatus status = daemon.getMiningStatus(); + + if (!status.isActive()) StartMining.startMining(); + + MoneroBlockHeader header = daemon.waitForNextBlockHeader(); + + if (!status.isActive()) daemon.stopMining(); + + return header; + } + /** * Fetches and tests transactions according to the given query. * @@ -5471,6 +5845,34 @@ public TxContext(TxContext ctx) { } } + protected void testScanTxs(MoneroWallet scanWallet) { + // get a few tx hashes + List txHashes = new ArrayList(); + List txs = wallet.getTxs(); + if (txs.size() < 3) fail("Not enough txs to scan"); + for (int i = 0; i < 3; i++) txHashes.add(txs.get(i).getHash()); + + // start wallet without scanning + scanWallet.stopSyncing(); // TODO: create wallet without daemon connection (offline does not reconnect, default connects to localhost, offline then online causes confirmed txs to disappear) + assertTrue(scanWallet.isConnectedToDaemon()); + + // scan txs + scanWallet.scanTxs(txHashes); + + // TODO: scanning txs causes merge problems reconciling 0 fee, isMinerTx with test txs + +// // txs are scanned +// assertEquals(txHashes.size(), scanWallet.getTxs().size()); +// for (int i = 0; i < txHashes.size(); i++) { +// assertEquals(wallet.getTx(txHashes.get(i)), scanWallet.getTx(txHashes.get(i))); +// } +// List scannedTxs = scanWallet.getTxs(txHashes); +// assertEquals(txHashes.size(), scannedTxs.size()); + + // close wallet + closeWallet(scanWallet, false); + } + protected void testInvalidAddressError(MoneroError e) { assertEquals("Invalid address", e.getMessage()); } @@ -5524,7 +5926,7 @@ private static void testAccount(MoneroAccount account) { assertTrue(tag == null || tag.length() > 0); } - private static void testSubaddress(MoneroSubaddress subaddress) { + protected static void testSubaddress(MoneroSubaddress subaddress) { assertTrue(subaddress.getAccountIndex() >= 0); assertTrue(subaddress.getIndex() >= 0); assertNotNull(subaddress.getAddress()); @@ -5832,7 +6234,7 @@ protected void testTxWallet(MoneroTxWallet tx, TxContext ctx) { if (!Boolean.TRUE.equals(ctx.isCopy)) testTxWalletCopy(tx, ctx); } - private void testTxWalletCopy(MoneroTxWallet tx, TxContext ctx) { + protected void testTxWalletCopy(MoneroTxWallet tx, TxContext ctx) { // copy tx and assert deep equality MoneroTxWallet copy = tx.copy(); @@ -5881,7 +6283,7 @@ private void testTxWalletCopy(MoneroTxWallet tx, TxContext ctx) { assertEquals(merged.toString(), tx.toString()); } - private static void testTransfer(MoneroTransfer transfer, TxContext ctx) { + protected static void testTransfer(MoneroTransfer transfer, TxContext ctx) { if (ctx == null) ctx = new TxContext(); assertNotNull(transfer); TestUtils.testUnsignedBigInteger(transfer.getAmount()); @@ -5897,7 +6299,7 @@ private static void testTransfer(MoneroTransfer transfer, TxContext ctx) { } } - private static void testIncomingTransfer(MoneroIncomingTransfer transfer) { + protected static void testIncomingTransfer(MoneroIncomingTransfer transfer) { assertTrue(transfer.isIncoming()); assertFalse(transfer.isOutgoing()); assertNotNull(transfer.getAddress()); @@ -5905,7 +6307,7 @@ private static void testIncomingTransfer(MoneroIncomingTransfer transfer) { assertTrue(transfer.getNumSuggestedConfirmations() > 0); } - private static void testOutgoingTransfer(MoneroOutgoingTransfer transfer, TxContext ctx) { + protected static void testOutgoingTransfer(MoneroOutgoingTransfer transfer, TxContext ctx) { assertFalse(transfer.isIncoming()); assertTrue(transfer.isOutgoing()); if (!Boolean.TRUE.equals(ctx.isSendResponse)) assertNotNull(transfer.getSubaddressIndices()); @@ -5931,12 +6333,12 @@ private static void testOutgoingTransfer(MoneroOutgoingTransfer transfer, TxCont } } - private static void testDestination(MoneroDestination destination) { + protected static void testDestination(MoneroDestination destination) { MoneroUtils.validateAddress(destination.getAddress(), TestUtils.NETWORK_TYPE); TestUtils.testUnsignedBigInteger(destination.getAmount(), true); } - private static void testInputWallet(MoneroOutputWallet input) { + protected static void testInputWallet(MoneroOutputWallet input) { assertNotNull(input); assertNotNull(input.getKeyImage()); assertNotNull(input.getKeyImage().getHex()); @@ -5944,7 +6346,7 @@ private static void testInputWallet(MoneroOutputWallet input) { assertNull(input.getAmount()); // must get info separately } - private static void testOutputWallet(MoneroOutputWallet output) { + protected static void testOutputWallet(MoneroOutputWallet output) { assertNotNull(output); assertTrue(output.getAccountIndex() >= 0); assertTrue(output.getSubaddressIndex() >= 0); @@ -6281,4 +6683,232 @@ public void setListening(boolean listening) { this.listening = listening; } } + + protected void testSyncSeed(MoneroWallet wallet, Long startHeight, Long restoreHeight) { testSyncSeed(wallet, startHeight, restoreHeight, false, false); } + protected void testSyncSeed(MoneroWallet wallet, Long startHeight, Long restoreHeight, boolean skipGtComparison, boolean testPostSyncNotifications) { + assertTrue(daemon.isConnected(), "Not connected to daemon"); + if (startHeight != null && restoreHeight != null) assertTrue(startHeight <= TestUtils.FIRST_RECEIVE_HEIGHT || restoreHeight <= TestUtils.FIRST_RECEIVE_HEIGHT); + + // sanitize expected sync bounds + if (restoreHeight == null) restoreHeight = 0l; + long startHeightExpected = startHeight == null ? restoreHeight : startHeight; + if (startHeightExpected == 0) startHeightExpected = 1; + + long endHeightExpected = 0; + if (wallet instanceof MoneroWalletFull) endHeightExpected = ((MoneroWalletFull)wallet).getDaemonMaxPeerHeight(); + else if (wallet instanceof MoneroWalletLight) endHeightExpected = ((MoneroWalletLight)wallet).getDaemonMaxPeerHeight(); + // test wallet and close as final step + MoneroWalletFull walletGt = null; + try { + + // test wallet's height before syncing + assertTrue(wallet.isConnectedToDaemon()); + if (wallet instanceof MoneroWalletFull) assertFalse(((MoneroWalletFull)wallet).isSynced()); + else if (wallet instanceof MoneroWalletLight) assertFalse(((MoneroWalletLight)wallet).isSynced()); + + assertEquals(1, wallet.getHeight()); + if (wallet instanceof MoneroWalletFull) assertEquals((long) restoreHeight, ((MoneroWalletFull)wallet).getRestoreHeight()); + + // register a wallet listener which tests notifications throughout the sync + WalletSyncTester walletSyncTester = new WalletSyncTester(wallet, startHeightExpected, endHeightExpected); + wallet.addListener(walletSyncTester); + + // sync the wallet with a listener which tests sync notifications + SyncProgressTester progressTester = new SyncProgressTester(wallet, startHeightExpected, endHeightExpected); + MoneroSyncResult result = wallet.sync(startHeight, progressTester); + + // test completion of the wallet and sync listeners + progressTester.onDone(wallet.getDaemonHeight()); + walletSyncTester.onDone(wallet.getDaemonHeight()); + + // test result after syncing + if (wallet instanceof MoneroWalletFull) assertTrue(((MoneroWalletFull)wallet).isSynced()); + else if (wallet instanceof MoneroWalletLight) assertTrue(((MoneroWalletLight)wallet).isSynced()); + assertEquals(wallet.getDaemonHeight() - startHeightExpected, (long) result.getNumBlocksFetched()); + assertTrue(result.getReceivedMoney()); + if (wallet.getHeight() != daemon.getHeight()) System.out.println("WARNING: wallet height " + wallet.getHeight() + " is not synced with daemon height " + daemon.getHeight()); // TODO: height may not be same after long sync + assertEquals(daemon.getHeight(), wallet.getDaemonHeight(), "Daemon heights are not equal: " + wallet.getDaemonHeight() + " vs " + daemon.getHeight()); + if (startHeightExpected > TestUtils.FIRST_RECEIVE_HEIGHT) assertTrue(wallet.getTxs().get(0).getHeight() > TestUtils.FIRST_RECEIVE_HEIGHT); // wallet is partially synced so first tx happens after true restore height + else assertEquals(TestUtils.FIRST_RECEIVE_HEIGHT, (long) wallet.getTxs().get(0).getHeight()); // wallet should be fully synced so first tx happens on true restore height + + // sync the wallet with default params + result = syncWithDaemon(wallet); + if (wallet instanceof MoneroWalletFull) assertTrue(((MoneroWalletFull)wallet).isSynced()); + else if (wallet instanceof MoneroWalletLight) assertTrue(((MoneroWalletLight)wallet).isSynced()); + + assertEquals(daemon.getHeight(), wallet.getHeight()); + assertTrue(result.getNumBlocksFetched() == 0 || result.getNumBlocksFetched() == 1); // block might be added to chain + assertFalse(result.getReceivedMoney()); + + // compare with ground truth + if (!skipGtComparison) { + walletGt = TestUtils.createWalletGroundTruth(TestUtils.NETWORK_TYPE, wallet.getSeed(), startHeight, restoreHeight); + testWalletEqualityOnChain(walletGt, wallet); + } + + // if testing post-sync notifications, wait for a block to be added to the chain + // then test that sync arg listener was not invoked and registered wallet listener was invoked + if (testPostSyncNotifications) { + + // start automatic syncing + wallet.startSyncing(TestUtils.SYNC_PERIOD_IN_MS); + + // attempt to start mining to push the network along // TODO: TestUtils.tryStartMining() : reqId, TestUtils.tryStopMining(reqId) + boolean startedMining = false; + try { + StartMining.startMining(); + startedMining = true; + } catch (Exception e) { + // no problem + } + + try { + // prevent dead lock with light wallet tests + if (!startedMining) throw new RuntimeException("Mining did not start"); + // wait for block + System.out.println("Waiting for next block to test post sync notifications"); + waitForNextBlockHeader(); + + // ensure wallet has time to detect new block + try { + TimeUnit.MILLISECONDS.sleep(TestUtils.SYNC_PERIOD_IN_MS + 3000); // sleep for wallet interval + time to sync + } catch (InterruptedException e) { + e.printStackTrace(); + throw new RuntimeException(e.getMessage()); + } + + // test that wallet listener's onSyncProgress() and onNewBlock() were invoked after previous completion + assertTrue(walletSyncTester.getOnSyncProgressAfterDone()); + assertTrue(walletSyncTester.getOnNewBlockAfterDone()); + } finally { + if (startedMining) daemon.stopMining(); + } + } + } finally { + if (walletGt != null) walletGt.close(true); + wallet.close(); + } + } + + protected void testCreateWalletFromKeysJni(MoneroWallet walletKeys) { + if (!(walletKeys instanceof MoneroWalletFull) && !(walletKeys instanceof MoneroWalletLight)) { + throw new RuntimeException("Wallet type not supported"); + } + if (!(wallet instanceof MoneroWalletFull) && !(wallet instanceof MoneroWalletLight)) { + throw new RuntimeException("Wallet type not supported"); + } + + try { + assertEquals(wallet.getSeed(), walletKeys.getSeed()); + assertEquals(wallet.getPrimaryAddress(), walletKeys.getPrimaryAddress()); + assertEquals(wallet.getPrivateViewKey(), walletKeys.getPrivateViewKey()); + assertEquals(wallet.getPublicViewKey(), walletKeys.getPublicViewKey()); + assertEquals(wallet.getPrivateSpendKey(), walletKeys.getPrivateSpendKey()); + assertEquals(wallet.getPublicSpendKey(), walletKeys.getPublicSpendKey()); + assertTrue(walletKeys.isConnectedToDaemon()); + + // cannot test restore height of a non-synced light wallet + if (wallet instanceof MoneroWalletFull) { + assertEquals(((MoneroWalletFull)wallet).getRestoreHeight(), ((MoneroWalletFull)walletKeys).getRestoreHeight()); + } + + if ((walletKeys instanceof MoneroWalletFull)) assertFalse(((MoneroWalletFull)walletKeys).isSynced()); + else if ((walletKeys instanceof MoneroWalletLight)) assertFalse(((MoneroWalletLight)walletKeys).isSynced()); + + } finally { + walletKeys.close(); + } + } + + protected void testCreateWalletFromSeedWithOffset(String offset) { + assumeTrue(TEST_NON_RELAYS); + Exception e1 = null; // emulating Java "finally" but compatible with other languages + try { + + // create test wallet with offset + MoneroWalletConfig config = new MoneroWalletConfig().setSeed(TestUtils.SEED).setRestoreHeight(TestUtils.FIRST_RECEIVE_HEIGHT).setSeedOffset(offset); + MoneroWallet wallet = createWallet(config); + + Exception e2 = null; + try { + MoneroUtils.validateMnemonic(wallet.getSeed()); + assertNotEquals(TestUtils.SEED, wallet.getSeed()); + MoneroUtils.validateAddress(wallet.getPrimaryAddress(), TestUtils.NETWORK_TYPE); + assertNotEquals(TestUtils.ADDRESS, wallet.getPrimaryAddress()); + if (!(wallet instanceof MoneroWalletRpc)) assertEquals(MoneroWallet.DEFAULT_LANGUAGE, wallet.getSeedLanguage()); // TODO monero-wallet-rpc: support + } catch (Exception e) { + e2 = e; + } + closeWallet(wallet); if (e2 != null) throw e2; + } catch (Exception e) { + e1 = e; + } + + if (e1 != null) throw new RuntimeException(e1); + } + + protected static void testWalletsDoNotInterfere(MoneroWallet wallet1, MoneroWallet wallet2, long height, long restoreHeight) { + // track notifications of each wallet + SyncProgressTester tester1 = new SyncProgressTester(wallet1, restoreHeight, height); + SyncProgressTester tester2 = new SyncProgressTester(wallet1, restoreHeight, height); + wallet1.addListener(tester1); + wallet2.addListener(tester2); + + // sync first wallet and test that 2nd is not notified + wallet1.sync(); + assertTrue(tester1.isNotified()); + assertFalse(tester2.isNotified()); + + // sync 2nd wallet and test that 1st is not notified + SyncProgressTester tester3 = new SyncProgressTester(wallet1, restoreHeight, height); + wallet1.addListener(tester3); + wallet2.sync(); + assertTrue(tester2.isNotified()); + assertFalse(tester3.isNotified()); + } + + protected void testClose(MoneroWalletConfig config) + { + testClose(config, config.copy()); + } + + protected void testClose(MoneroWalletConfig conf1, MoneroWalletConfig conf2) + { + assumeTrue(TEST_NON_RELAYS); + + // create a test wallet + MoneroWallet wallet = createWallet(conf1); + wallet.sync(); + assertTrue(wallet.getHeight() > 1); + if (wallet instanceof MoneroWalletLight) assertTrue(((MoneroWalletLight)wallet).isSynced()); + else if (wallet instanceof MoneroWalletFull) assertTrue(((MoneroWalletFull)wallet).isSynced()); + + assertFalse(wallet.isClosed()); + + // close the wallet + wallet.close(); + assertTrue(wallet.isClosed()); + + // attempt to interact with the wallet + try { wallet.getHeight(); } + catch (MoneroError e) { assertEquals("Wallet is closed", e.getMessage()); } + try { wallet.getSeed(); } + catch (MoneroError e) { assertEquals("Wallet is closed", e.getMessage()); } + try { wallet.sync(); } + catch (MoneroError e) { assertEquals("Wallet is closed", e.getMessage()); } + try { wallet.startSyncing(); } + catch (MoneroError e) { assertEquals("Wallet is closed", e.getMessage()); } + try { wallet.stopSyncing(); } + catch (MoneroError e) { assertEquals("Wallet is closed", e.getMessage()); } + + // re-open the wallet + wallet = openWallet(conf2); + wallet.sync(); + assertEquals(wallet.getDaemonHeight(), wallet.getHeight()); + assertFalse(wallet.isClosed()); + + // close the wallet + wallet.close(); + assertTrue(wallet.isClosed()); + } } diff --git a/src/test/java/TestMoneroWalletFull.java b/src/test/java/TestMoneroWalletFull.java index dc96c9ec..6a3bbdda 100644 --- a/src/test/java/TestMoneroWalletFull.java +++ b/src/test/java/TestMoneroWalletFull.java @@ -3,7 +3,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assumptions.assumeTrue; @@ -15,7 +14,6 @@ import java.util.ArrayList; import java.util.List; import java.util.UUID; -import java.util.concurrent.TimeUnit; import monero.common.MoneroError; import monero.common.MoneroRpcConnection; import monero.common.MoneroUtils; @@ -26,12 +24,8 @@ import monero.wallet.MoneroWalletRpc; import monero.wallet.model.MoneroMultisigInfo; import monero.wallet.model.MoneroMultisigInitResult; -import monero.wallet.model.MoneroOutputQuery; -import monero.wallet.model.MoneroOutputWallet; -import monero.wallet.model.MoneroSyncResult; import monero.wallet.model.MoneroTransfer; import monero.wallet.model.MoneroTransferQuery; -import monero.wallet.model.MoneroTxQuery; import monero.wallet.model.MoneroTxWallet; import monero.wallet.model.MoneroWalletConfig; import monero.wallet.model.MoneroWalletListener; @@ -45,7 +39,6 @@ import utils.StartMining; import utils.TestUtils; import utils.WalletEqualityUtils; -import utils.WalletSyncPrinter; /** * Tests specific to the fully client-side wallet. @@ -206,23 +199,6 @@ public void testCreateWalletsWithoutClose() { // ------------------------------- BEGIN TESTS ------------------------------ - // Can get the daemon's height - @Test - public void testDaemon() { - assumeTrue(TEST_NON_RELAYS); - assertTrue(wallet.isConnectedToDaemon()); - long daemonHeight = wallet.getDaemonHeight(); - assertTrue(daemonHeight > 0); - } - - // Can get the daemon's max peer height - @Test - public void testGetDaemonMaxPeerHeight() { - assumeTrue(TEST_NON_RELAYS); - long height = ((MoneroWalletFull) wallet).getDaemonMaxPeerHeight(); - assertTrue(height > 0); - } - // @Test // public void getApproximateChainHeight() { // long height = wallet.getApproximateChainHeight(); @@ -366,19 +342,7 @@ public void testCreateWalletFromKeysJni() { // recreate test wallet from keys String path = getRandomWalletPath(); MoneroWalletFull walletKeys = createWallet(new MoneroWalletConfig().setPath(path).setPrimaryAddress(wallet.getPrimaryAddress()).setPrivateViewKey(wallet.getPrivateViewKey()).setPrivateSpendKey(wallet.getPrivateSpendKey()).setRestoreHeight(TestUtils.FIRST_RECEIVE_HEIGHT)); - try { - assertEquals(wallet.getSeed(), walletKeys.getSeed()); - assertEquals(wallet.getPrimaryAddress(), walletKeys.getPrimaryAddress()); - assertEquals(wallet.getPrivateViewKey(), walletKeys.getPrivateViewKey()); - assertEquals(wallet.getPublicViewKey(), walletKeys.getPublicViewKey()); - assertEquals(wallet.getPrivateSpendKey(), walletKeys.getPrivateSpendKey()); - assertEquals(wallet.getPublicSpendKey(), walletKeys.getPublicSpendKey()); - assertEquals(TestUtils.FIRST_RECEIVE_HEIGHT, walletKeys.getRestoreHeight()); - assertTrue(walletKeys.isConnectedToDaemon()); - assertFalse(walletKeys.isSynced()); - } finally { - walletKeys.close(); - } + testCreateWalletFromKeysJni(walletKeys); } // Is compatible with monero-wallet-rpc wallet files @@ -475,87 +439,6 @@ public void testMultisigCompatibility() throws InterruptedException, IOException } }; - // Can re-sync an existing wallet from scratch - @Test - @Disabled // TODO monero-project: cannot re-sync from lower block height after wallet saved - public void testResyncExisting() { - assertTrue(MoneroWalletFull.walletExists(TestUtils.WALLET_FULL_PATH)); - MoneroWalletFull wallet = openWallet(new MoneroWalletConfig().setPath(TestUtils.WALLET_FULL_PATH).setServerUri(TestUtils.OFFLINE_SERVER_URI), false); - wallet.setDaemonConnection(TestUtils.getDaemonRpc().getRpcConnection()); - //long startHeight = TestUtils.TEST_RESTORE_HEIGHT; - long startHeight = 0; - SyncProgressTester progressTester = new SyncProgressTester(wallet, startHeight, wallet.getDaemonHeight()); - wallet.setRestoreHeight(1); - MoneroSyncResult result = wallet.sync(1l, progressTester); - progressTester.onDone(wallet.getDaemonHeight()); - - // test result after syncing - assertTrue(wallet.isConnectedToDaemon()); - assertTrue(wallet.isSynced()); - assertEquals(wallet.getDaemonHeight() - startHeight, (long) result.getNumBlocksFetched()); - assertTrue(result.getReceivedMoney()); - assertEquals(daemon.getHeight(), wallet.getHeight()); - wallet.close(true); - } - - // Can sync a wallet with a randomly generated seed - @Test - public void testSyncRandom() { - assumeTrue(TEST_NON_RELAYS); - assertTrue(daemon.isConnected(), "Not connected to daemon"); - - // create test wallet - MoneroWalletFull wallet = createWallet(new MoneroWalletConfig(), false); - long restoreHeight = daemon.getHeight(); - - // test wallet's height before syncing - assertEquals(TestUtils.getDaemonRpc().getRpcConnection(), wallet.getDaemonConnection()); - assertEquals(restoreHeight, wallet.getDaemonHeight()); - assertTrue(wallet.isConnectedToDaemon()); - assertFalse(wallet.isSynced()); - assertEquals(1, wallet.getHeight()); - assertEquals(restoreHeight, wallet.getRestoreHeight()); - assertEquals(daemon.getHeight(), wallet.getDaemonHeight()); - - // sync the wallet - SyncProgressTester progressTester = new SyncProgressTester(wallet, wallet.getRestoreHeight(), wallet.getDaemonHeight()); - MoneroSyncResult result = wallet.sync(null, progressTester); - progressTester.onDone(wallet.getDaemonHeight()); - - // test result after syncing - MoneroWalletFull walletGt = TestUtils.createWalletGroundTruth(TestUtils.NETWORK_TYPE, wallet.getSeed(), null, restoreHeight); - walletGt.sync(); - try { - assertTrue(wallet.isConnectedToDaemon()); - assertTrue(wallet.isSynced()); - assertEquals(0, (long) result.getNumBlocksFetched()); - assertFalse(result.getReceivedMoney()); - assertEquals(daemon.getHeight(), wallet.getHeight()); - - // sync the wallet with default params - wallet.sync(); - assertTrue(wallet.isSynced()); - assertEquals(daemon.getHeight(), wallet.getHeight()); - - // compare wallet to ground truth - testWalletEqualityOnChain(walletGt, wallet); - } finally { - if (walletGt != null) walletGt.close(true); - wallet.close(); - } - - // attempt to sync unconnected wallet - wallet = createWallet(new MoneroWalletConfig().setServerUri(TestUtils.OFFLINE_SERVER_URI)); - try { - wallet.sync(); - fail("Should have thrown exception"); - } catch (MoneroError e) { - assertEquals("Wallet is not connected to daemon", e.getMessage()); - } finally { - wallet.close(); - } - } - // Can sync a wallet created from seed from the genesis @Test public void testSyncSeedFromGenesis() { @@ -601,234 +484,7 @@ private void testSyncSeed(Long startHeight, Long restoreHeight, boolean skipGtCo // create wallet from seed MoneroWalletFull wallet = createWallet(new MoneroWalletConfig().setSeed(TestUtils.SEED).setRestoreHeight(restoreHeight), false); - - // sanitize expected sync bounds - if (restoreHeight == null) restoreHeight = 0l; - long startHeightExpected = startHeight == null ? restoreHeight : startHeight; - if (startHeightExpected == 0) startHeightExpected = 1; - long endHeightExpected = wallet.getDaemonMaxPeerHeight(); - - // test wallet and close as final step - MoneroWalletFull walletGt = null; - try { - - // test wallet's height before syncing - assertTrue(wallet.isConnectedToDaemon()); - assertFalse(wallet.isSynced()); - assertEquals(1, wallet.getHeight()); - assertEquals((long) restoreHeight, wallet.getRestoreHeight()); - - // register a wallet listener which tests notifications throughout the sync - WalletSyncTester walletSyncTester = new WalletSyncTester(wallet, startHeightExpected, endHeightExpected); - wallet.addListener(walletSyncTester); - - // sync the wallet with a listener which tests sync notifications - SyncProgressTester progressTester = new SyncProgressTester(wallet, startHeightExpected, endHeightExpected); - MoneroSyncResult result = wallet.sync(startHeight, progressTester); - - // test completion of the wallet and sync listeners - progressTester.onDone(wallet.getDaemonHeight()); - walletSyncTester.onDone(wallet.getDaemonHeight()); - - // test result after syncing - assertTrue(wallet.isSynced()); - assertEquals(wallet.getDaemonHeight() - startHeightExpected, (long) result.getNumBlocksFetched()); - assertTrue(result.getReceivedMoney()); - if (wallet.getHeight() != daemon.getHeight()) System.out.println("WARNING: wallet height " + wallet.getHeight() + " is not synced with daemon height " + daemon.getHeight()); // TODO: height may not be same after long sync - assertEquals(daemon.getHeight(), wallet.getDaemonHeight(), "Daemon heights are not equal: " + wallet.getDaemonHeight() + " vs " + daemon.getHeight()); - if (startHeightExpected > TestUtils.FIRST_RECEIVE_HEIGHT) assertTrue(wallet.getTxs().get(0).getHeight() > TestUtils.FIRST_RECEIVE_HEIGHT); // wallet is partially synced so first tx happens after true restore height - else assertEquals(TestUtils.FIRST_RECEIVE_HEIGHT, (long) wallet.getTxs().get(0).getHeight()); // wallet should be fully synced so first tx happens on true restore height - - // sync the wallet with default params - result = wallet.sync(); - assertTrue(wallet.isSynced()); - assertEquals(daemon.getHeight(), wallet.getHeight()); - assertTrue(result.getNumBlocksFetched() == 0 || result.getNumBlocksFetched() == 1); // block might be added to chain - assertFalse(result.getReceivedMoney()); - - // compare with ground truth - if (!skipGtComparison) { - walletGt = TestUtils.createWalletGroundTruth(TestUtils.NETWORK_TYPE, wallet.getSeed(), startHeight, restoreHeight); - testWalletEqualityOnChain(walletGt, wallet); - } - - // if testing post-sync notifications, wait for a block to be added to the chain - // then test that sync arg listener was not invoked and registered wallet listener was invoked - if (testPostSyncNotifications) { - - // start automatic syncing - wallet.startSyncing(TestUtils.SYNC_PERIOD_IN_MS); - - // attempt to start mining to push the network along // TODO: TestUtils.tryStartMining() : reqId, TestUtils.tryStopMining(reqId) - boolean startedMining = false; - try { - StartMining.startMining(); - startedMining = true; - } catch (Exception e) { - // no problem - } - - try { - - // wait for block - System.out.println("Waiting for next block to test post sync notifications"); - daemon.waitForNextBlockHeader(); - - // ensure wallet has time to detect new block - try { - TimeUnit.MILLISECONDS.sleep(TestUtils.SYNC_PERIOD_IN_MS + 3000); // sleep for wallet interval + time to sync - } catch (InterruptedException e) { - e.printStackTrace(); - throw new RuntimeException(e.getMessage()); - } - - // test that wallet listener's onSyncProgress() and onNewBlock() were invoked after previous completion - assertTrue(walletSyncTester.getOnSyncProgressAfterDone()); - assertTrue(walletSyncTester.getOnNewBlockAfterDone()); - } finally { - if (startedMining) wallet.stopMining(); - } - } - } finally { - if (walletGt != null) walletGt.close(true); - wallet.close(); - } - } - - // Can sync a wallet created from keys - @Test - public void testSyncWalletFromKeys() { - assumeTrue(TEST_NON_RELAYS); - - // recreate test wallet from keys - String path = getRandomWalletPath(); - MoneroWalletFull walletKeys = createWallet(new MoneroWalletConfig().setPath(path).setPrimaryAddress(wallet.getPrimaryAddress()).setPrivateViewKey(wallet.getPrivateViewKey()).setPrivateSpendKey(wallet.getPrivateSpendKey()).setRestoreHeight(TestUtils.FIRST_RECEIVE_HEIGHT), false); - - // create ground truth wallet for comparison - MoneroWalletFull walletGt = TestUtils.createWalletGroundTruth(TestUtils.NETWORK_TYPE, TestUtils.SEED, null, TestUtils.FIRST_RECEIVE_HEIGHT); - - // test wallet and close as final step - try { - assertEquals(walletKeys.getSeed(), walletGt.getSeed()); - assertEquals(walletKeys.getPrimaryAddress(), walletGt.getPrimaryAddress()); - assertEquals(walletKeys.getPrivateViewKey(), walletGt.getPrivateViewKey()); - assertEquals(walletKeys.getPublicViewKey(), walletGt.getPublicViewKey()); - assertEquals(walletKeys.getPrivateSpendKey(), walletGt.getPrivateSpendKey()); - assertEquals(walletKeys.getPublicSpendKey(), walletGt.getPublicSpendKey()); - assertEquals(TestUtils.FIRST_RECEIVE_HEIGHT, walletGt.getRestoreHeight()); - assertTrue(walletKeys.isConnectedToDaemon()); - assertFalse(walletKeys.isSynced()); - - // sync the wallet - SyncProgressTester progressTester = new SyncProgressTester(walletKeys, TestUtils.FIRST_RECEIVE_HEIGHT, walletKeys.getDaemonMaxPeerHeight()); - MoneroSyncResult result = walletKeys.sync(progressTester); - progressTester.onDone(walletKeys.getDaemonHeight()); - - // test result after syncing - assertTrue(walletKeys.isSynced()); - assertEquals(walletKeys.getDaemonHeight() - TestUtils.FIRST_RECEIVE_HEIGHT, (long) result.getNumBlocksFetched()); - assertTrue(result.getReceivedMoney()); - assertEquals(daemon.getHeight(), walletKeys.getHeight()); - assertEquals(daemon.getHeight(), walletKeys.getDaemonHeight()); - assertEquals(TestUtils.FIRST_RECEIVE_HEIGHT, (long) walletKeys.getTxs().get(0).getHeight()); // wallet should be fully synced so first tx happens on true restore height - - // compare with ground truth - testWalletEqualityOnChain(walletGt, walletKeys); - } finally { - walletGt.close(true); - walletKeys.close(); - } - - // TODO monero-project: importing key images can cause erasure of incoming transfers per wallet2.cpp:11957 which causes this test to fail -// // sync the wallets until same height -// while (wallet.getHeight() != walletKeys.getHeight()) { -// wallet.sync(); -// walletKeys.sync(new WalletSyncPrinter()); -// } -// -// List keyImages = walletKeys.exportKeyImages(); -// walletKeys.importKeyImages(keyImages); - } - - // TODO: test start syncing, notification of syncs happening, stop syncing, no notifications, etc - // Can start and stop syncing - @Test - public void testStartStopSyncing() { - assumeTrue(TEST_NON_RELAYS); - - // test unconnected wallet - String path = getRandomWalletPath(); - MoneroWalletFull wallet = createWallet(new MoneroWalletConfig().setServerUri(TestUtils.OFFLINE_SERVER_URI)); - try { - assertNotNull(wallet.getSeed()); - assertEquals(1, wallet.getHeight()); - assertEquals(BigInteger.valueOf(0), wallet.getBalance()); - wallet.startSyncing(); - } catch (MoneroError e) { - assertEquals("Wallet is not connected to daemon", e.getMessage()); - } finally { - wallet.close(); - } - - // test connecting wallet - path = getRandomWalletPath(); - wallet = createWallet(new MoneroWalletConfig().setPath(path).setServerUri(TestUtils.OFFLINE_SERVER_URI)); - try { - assertNotNull(wallet.getSeed()); - wallet.setDaemonConnection(daemon.getRpcConnection()); - assertEquals(1, wallet.getHeight()); - assertFalse(wallet.isSynced()); - assertEquals(BigInteger.valueOf(0), wallet.getBalance()); - long chainHeight = wallet.getDaemonHeight(); - wallet.setRestoreHeight(chainHeight - 3); - wallet.startSyncing(); - assertEquals(chainHeight - 3, wallet.getRestoreHeight()); - assertEquals(daemon.getRpcConnection(), wallet.getDaemonConnection()); - wallet.stopSyncing(); - wallet.sync(); - wallet.stopSyncing(); - wallet.stopSyncing(); - } finally { - wallet.close(); - } - - // test that sync starts automatically - long restoreHeight = daemon.getHeight() - 100; - path = getRandomWalletPath(); - wallet = createWallet(new MoneroWalletConfig().setPath(path).setSeed(TestUtils.SEED).setRestoreHeight(restoreHeight), false); - try { - - // start syncing - assertEquals(1, wallet.getHeight()); - assertEquals(restoreHeight, wallet.getRestoreHeight()); - assertFalse(wallet.isSynced()); - assertEquals(BigInteger.valueOf(0), wallet.getBalance()); - wallet.startSyncing(TestUtils.SYNC_PERIOD_IN_MS); - - // pause for sync to start - try { - System.out.println("Sleeping to test that sync starts automatically..."); - TimeUnit.MILLISECONDS.sleep(TestUtils.SYNC_PERIOD_IN_MS + 1000); - } catch (InterruptedException e) { - e.printStackTrace(); - throw new RuntimeException(e.getMessage()); - } - - // test that wallet has started syncing - assertTrue(wallet.getHeight() > 1); - - // stop syncing - wallet.stopSyncing(); - - // TODO monero-project: wallet.cpp m_synchronized only ever set to true, never false -// // wait for block to be added to chain -// daemon.getNextBlockHeader(); -// -// // wallet is no longer synced -// assertFalse(wallet.isSynced()); - } finally { - wallet.close(); - } + testSyncSeed(wallet, startHeight, restoreHeight, skipGtComparison, testPostSyncNotifications); } // Does not interfere with other wallet notifications @@ -841,23 +497,7 @@ public void testWalletsDoNotInterfere() { MoneroWalletFull wallet1 = createWallet(new MoneroWalletConfig().setSeed(TestUtils.SEED).setRestoreHeight(restoreHeight), false); MoneroWalletFull wallet2 = createWallet(new MoneroWalletConfig().setSeed(TestUtils.SEED).setRestoreHeight(restoreHeight), false); - // track notifications of each wallet - SyncProgressTester tester1 = new SyncProgressTester(wallet1, restoreHeight, height); - SyncProgressTester tester2 = new SyncProgressTester(wallet1, restoreHeight, height); - wallet1.addListener(tester1); - wallet2.addListener(tester2); - - // sync first wallet and test that 2nd is not notified - wallet1.sync(); - assertTrue(tester1.isNotified()); - assertFalse(tester2.isNotified()); - - // sync 2nd wallet and test that 1st is not notified - SyncProgressTester tester3 = new SyncProgressTester(wallet1, restoreHeight, height); - wallet1.addListener(tester3); - wallet2.sync(); - assertTrue(tester2.isNotified()); - assertFalse(tester3.isNotified()); + testWalletsDoNotInterfere(wallet1, wallet2, height, restoreHeight); } // Is equal to the RPC wallet. @@ -1178,41 +818,9 @@ public void testMoveTo() { // Can be closed @Test public void testClose() { - assumeTrue(TEST_NON_RELAYS); - - // create a test wallet String path = getRandomWalletPath(); - MoneroWalletFull wallet = createWallet(new MoneroWalletConfig().setPath(path)); - wallet.sync(); - assertTrue(wallet.getHeight() > 1); - assertTrue(wallet.isSynced()); - assertFalse(wallet.isClosed()); - - // close the wallet - wallet.close(); - assertTrue(wallet.isClosed()); - - // attempt to interact with the wallet - try { wallet.getHeight(); } - catch (MoneroError e) { assertEquals("Wallet is closed", e.getMessage()); } - try { wallet.getSeed(); } - catch (MoneroError e) { assertEquals("Wallet is closed", e.getMessage()); } - try { wallet.sync(); } - catch (MoneroError e) { assertEquals("Wallet is closed", e.getMessage()); } - try { wallet.startSyncing(); } - catch (MoneroError e) { assertEquals("Wallet is closed", e.getMessage()); } - try { wallet.stopSyncing(); } - catch (MoneroError e) { assertEquals("Wallet is closed", e.getMessage()); } - - // re-open the wallet - wallet = openWallet(new MoneroWalletConfig().setPath(path)); - wallet.sync(); - assertEquals(wallet.getDaemonHeight(), wallet.getHeight()); - assertFalse(wallet.isClosed()); - - // close the wallet - wallet.close(); - assertTrue(wallet.isClosed()); + MoneroWalletConfig config = new MoneroWalletConfig().setPath(path); + testClose(config); } // Supports multisig sample code @@ -1281,233 +889,8 @@ private void testCreateMultisigWallet(int M, int N) { } } - // Does not leak memory - @Test - @Disabled - public void testMemoryLeak() { - System.out.println("Infinite loop starting, monitor memory in OS process manager..."); - System.gc(); - int i = 0; - boolean closeWallet = false; - long time = System.currentTimeMillis(); - if (closeWallet) wallet.close(true); - while (true) { - if (closeWallet) wallet = TestUtils.getWalletFull(); - wallet.sync(); - wallet.getTxs(); - wallet.getTransfers(); - wallet.getOutputs(new MoneroOutputQuery().setIsSpent(false)); - if (i % 100 == 0) { - System.out.println("Garbage collecting on iteration " + i); - System.gc(); - System.out.println((System.currentTimeMillis() - time) + " ms since last GC"); - time = System.currentTimeMillis(); - } - if (closeWallet) wallet.close(true); - i++; - } - } - // ---------------------------------- HELPERS ------------------------------- - public static String getRandomWalletPath() { - return TestUtils.TEST_WALLETS_DIR + "/test_wallet_" + System.currentTimeMillis(); - } - - /** - * Internal class to test progress updates. - */ - private class SyncProgressTester extends WalletSyncPrinter { - - protected MoneroWalletFull wallet; - private Long prevHeight; - private long startHeight; - private long prevEndHeight; - private Long prevCompleteHeight; - protected boolean isDone; - private Boolean onSyncProgressAfterDone; - - public SyncProgressTester(MoneroWalletFull wallet, long startHeight, long endHeight) { - this.wallet = wallet; - assertTrue(startHeight >= 0); - assertTrue(endHeight >= 0); - this.startHeight = startHeight; - this.prevEndHeight = endHeight; - this.isDone = false; - } - - @Override - public synchronized void onSyncProgress(long height, long startHeight, long endHeight, double percentDone, String message) { - super.onSyncProgress(height, startHeight, endHeight, percentDone, message); - - // registered wallet listeners will continue to get sync notifications after the wallet's initial sync - if (isDone) { - assertTrue(wallet.getListeners().contains(this), "Listener has completed and is not registered so should not be called again"); - onSyncProgressAfterDone = true; - } - - // update tester's start height if new sync session - if (prevCompleteHeight != null && startHeight == prevCompleteHeight) this.startHeight = startHeight; - - // if sync is complete, record completion height for subsequent start heights - if (Double.compare(percentDone, 1) == 0) prevCompleteHeight = endHeight; - - // otherwise start height is equal to previous completion height - else if (prevCompleteHeight != null) assertEquals((long) prevCompleteHeight, startHeight); - - assertTrue(endHeight > startHeight, "end height > start height"); - assertEquals(this.startHeight, startHeight); - assertTrue(endHeight >= prevEndHeight); // chain can grow while syncing - prevEndHeight = endHeight; - assertTrue(height >= startHeight); - assertTrue(height < endHeight); - double expectedPercentDone = (double) (height - startHeight + 1) / (double) (endHeight - startHeight); - assertTrue(Double.compare(expectedPercentDone, percentDone) == 0); - if (prevHeight == null) assertEquals(startHeight, height); - else assertEquals(height, prevHeight + 1); - prevHeight = height; - } - - public void onDone(long chainHeight) { - assertFalse(isDone); - this.isDone = true; - if (prevHeight == null) { - assertNull(prevCompleteHeight); - assertEquals(chainHeight, startHeight); - } else { - assertEquals(chainHeight - 1, (long) prevHeight); // otherwise last height is chain height - 1 - assertEquals(chainHeight, (long) prevCompleteHeight); - } - } - - public Boolean isNotified() { - return prevHeight != null; - } - - public Boolean getOnSyncProgressAfterDone() { - return onSyncProgressAfterDone; - } - } - - /** - * Internal class to test all wallet notifications on sync. - */ - private class WalletSyncTester extends SyncProgressTester { - - private Long walletTesterPrevHeight; // renamed from prevHeight to not interfere with super's prevHeight - private MoneroOutputWallet prevOutputReceived; - private MoneroOutputWallet prevOutputSpent; - private BigInteger incomingTotal; - private BigInteger outgoingTotal; - private Boolean onNewBlockAfterDone; - private BigInteger prevBalance; - private BigInteger prevUnlockedBalance; - - public WalletSyncTester(MoneroWalletFull wallet, long startHeight, long endHeight) { - super(wallet, startHeight, endHeight); - assertTrue(startHeight >= 0); - assertTrue(endHeight >= 0); - incomingTotal = BigInteger.valueOf(0); - outgoingTotal = BigInteger.valueOf(0); - } - - @Override - public synchronized void onNewBlock(long height) { - if (isDone) { - assertTrue(wallet.getListeners().contains(this), "Listener has completed and is not registered so should not be called again"); - onNewBlockAfterDone = true; - } - if (walletTesterPrevHeight != null) assertEquals(walletTesterPrevHeight + 1, height); - assertTrue(height >= super.startHeight); - walletTesterPrevHeight = height; - } - - @Override - public void onBalancesChanged(BigInteger newBalance, BigInteger newUnlockedBalance) { - if (this.prevBalance != null) assertTrue(!newBalance.equals(this.prevBalance) || !newUnlockedBalance.equals(this.prevUnlockedBalance)); - this.prevBalance = newBalance; - this.prevUnlockedBalance = newUnlockedBalance; - } - - @Override - public void onOutputReceived(MoneroOutputWallet output) { - assertNotNull(output); - prevOutputReceived = output; - - // test output - assertNotNull(output.getAmount()); - assertTrue(output.getAccountIndex() >= 0); - assertTrue(output.getSubaddressIndex() >= 0); - - // test output's tx - assertNotNull(output.getTx()); - assertNotNull(output.getTx().getHash()); - assertEquals(64, output.getTx().getHash().length()); - assertTrue(output.getTx().getVersion() >= 0); - assertTrue(output.getTx().getUnlockTime().compareTo(BigInteger.valueOf(0)) >= 0); - assertNull(output.getTx().getInputs()); - assertEquals(1, output.getTx().getOutputs().size()); - assertTrue(output.getTx().getOutputs().get(0) == output); - - // extra is not sent over the jni bridge - assertNull(output.getTx().getExtra()); - - // add incoming amount to running total - if (output.isLocked()) incomingTotal = incomingTotal.add(output.getAmount()); // TODO: only add if not unlocked, test unlocked received - } - - @Override - public void onOutputSpent(MoneroOutputWallet output) { - assertNotNull(output); - prevOutputSpent = output; - - // test output - assertNotNull(output.getAmount()); - assertTrue(output.getAccountIndex() >= 0); - if (output.getSubaddressIndex() != null) assertTrue(output.getSubaddressIndex() >= 0); // TODO (monero-project): can be undefined because inputs not provided so one created from outgoing transfer - - // test output's tx - assertNotNull(output.getTx()); - assertNotNull(output.getTx().getHash()); - assertEquals(64, output.getTx().getHash().length()); - assertTrue(output.getTx().getVersion() >= 0); - assertTrue(output.getTx().getUnlockTime().compareTo(BigInteger.valueOf(0)) >= 0); - assertEquals(1, output.getTx().getInputs().size()); - assertTrue(output.getTx().getInputs().get(0) == output); - assertNull(output.getTx().getOutputs()); - - // extra is not sent over the jni bridge - assertNull(output.getTx().getExtra()); - - // add outgoing amount to running total - if (output.isLocked()) outgoingTotal = outgoingTotal.add(output.getAmount()); - } - - @Override - public void onDone(long chainHeight) { - super.onDone(chainHeight); - assertNotNull(walletTesterPrevHeight); - assertNotNull(prevOutputReceived); - assertNotNull(prevOutputSpent); - BigInteger expectedBalance = incomingTotal.subtract(outgoingTotal); - - // output notifications do not include pool fees or outgoing amount - BigInteger poolSpendAmount = BigInteger.ZERO; - for (MoneroTxWallet poolTx : wallet.getTxs(new MoneroTxQuery().setInTxPool(true))) { - poolSpendAmount = poolSpendAmount.add(poolTx.getFee()).add(poolTx.getOutgoingAmount() == null ? BigInteger.ZERO : poolTx.getOutgoingAmount()); - } - expectedBalance = expectedBalance.subtract(poolSpendAmount); - - assertEquals(expectedBalance, wallet.getBalance()); - assertEquals(prevBalance, wallet.getBalance()); - assertEquals(prevUnlockedBalance, wallet.getUnlockedBalance()); - } - - public Boolean getOnNewBlockAfterDone() { - return onNewBlockAfterDone; - } - } - // jni-specific tx tests @Override protected void testTxWallet(MoneroTxWallet tx, TxContext ctx) { @@ -1517,17 +900,54 @@ protected void testTxWallet(MoneroTxWallet tx, TxContext ctx) { super.testTxWallet(tx, ctx); } - // possible configuration: on chain xor local wallet data ("strict"), txs ordered same way? TBD - private static void testWalletEqualityOnChain(MoneroWalletFull wallet1, MoneroWalletFull wallet2) { - WalletEqualityUtils.testWalletEqualityOnChain(wallet1, wallet2); - assertEquals(wallet1.getNetworkType(), wallet2.getNetworkType()); - assertEquals(wallet1.getRestoreHeight(), wallet2.getRestoreHeight()); - assertEquals(wallet1.getDaemonConnection(), wallet2.getDaemonConnection()); - assertEquals(wallet1.getSeedLanguage(), wallet2.getSeedLanguage()); - // TODO: more jni-specific extensions - } - // -------------------- OVERRIDES TO BE DIRECTLY RUNNABLE ------------------- + + @Override + @Test + public void testSyncRandom() { + super.testSyncRandom(); + } + + @Override + @Test + @Disabled // TODO monero-project: cannot re-sync from lower block height after wallet saved + public void testResyncExisting() { + super.testResyncExisting(); + } + + @Override + @Test + public void testSyncWalletFromKeys() { + super.testSyncWalletFromKeys(); + } + + @Override + @Test + public void testStartStopSyncing() { + super.testStartStopSyncing(); + } + + // Can get the daemon's max peer height + @Override + @Test + public void testGetDaemonMaxPeerHeight() { + super.testGetDaemonMaxPeerHeight(); + } + + @Override + @Test + public void testDaemon() + { + super.testDaemon(); + } + + // Does not leak memory + @Override + @Test + @Disabled + public void testMemoryLeak() { + super.testMemoryLeak(); + } @Override @Test diff --git a/src/test/java/TestMoneroWalletLight.java b/src/test/java/TestMoneroWalletLight.java new file mode 100644 index 00000000..bb858d74 --- /dev/null +++ b/src/test/java/TestMoneroWalletLight.java @@ -0,0 +1,1053 @@ +import monero.common.MoneroError; +import monero.common.MoneroRpcConnection; +import monero.wallet.MoneroWallet; +import monero.wallet.MoneroWalletFull; +import monero.wallet.MoneroWalletLight; +import monero.wallet.model.MoneroTxWallet; +import monero.wallet.model.MoneroWalletConfig; + +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; +import org.junit.jupiter.api.TestInstance.Lifecycle; + +import common.utils.GenUtils; + +import org.junit.jupiter.api.TestInstance; + +import utils.TestUtils; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +import java.io.IOException; +import java.util.List; +import java.util.UUID; + + +@TestInstance(Lifecycle.PER_CLASS) // so @BeforeAll and @AfterAll can be used on non-static functions +public class TestMoneroWalletLight extends TestMoneroWalletCommon { + + public TestMoneroWalletLight() { + super(); + } + + @Override + @BeforeAll + public void beforeAll() { + System.out.println("Starting Light Wallet Tests"); + super.beforeAll(); + wallet = getTestWallet(); + } + + @Override + @AfterAll + public void afterAll() { + System.out.println("End Light Wallet Tests"); + // try to stop mining + if (daemon != null) { + try { daemon.stopMining(); } + catch (MoneroError e) { } + } + + // close wallet + if (wallet != null) wallet.close(false); + } + + @Override + protected MoneroWalletLight getTestWallet() { + return TestUtils.getWalletLight(); + } + + @Override + @BeforeEach + public void beforeEach(TestInfo testInfo) { + System.out.println("Before test " + testInfo.getDisplayName()); + super.beforeEach(testInfo); + } + + private MoneroRpcConnection getRpcConnection() { + return new MoneroRpcConnection(TestUtils.WALLET_LWS_URI); + } + + @Override + protected MoneroWalletLight openWallet(MoneroWalletConfig config) { + return openWallet(config, true); + } + + @Override + protected MoneroWalletLight openWallet(MoneroWalletConfig config, boolean startSyncing) { + + // assign defaults + if (config == null) config = new MoneroWalletConfig(); + if (config.getPassword() == null) config.setPassword(TestUtils.WALLET_PASSWORD); + if (config.getNetworkType() == null) config.setNetworkType(TestUtils.NETWORK_TYPE); + if (config.getServer() == null && config.getConnectionManager() == null) config.setServer(getRpcConnection()); + + // open wallet + MoneroWalletLight wallet = MoneroWalletLight.openWallet(config); + if (startSyncing != false && wallet.isConnectedToDaemon()) { + wallet.startSyncing(TestUtils.SYNC_PERIOD_IN_MS); + syncWithDaemon(wallet); + } + return wallet; + } + + @Override + protected MoneroWalletLight createWallet(MoneroWalletConfig config) { + return createWallet(config, true); + } + + @Override + protected MoneroWalletLight createWallet(MoneroWalletConfig config, boolean startSyncing) { + + // assign defaults + if (config == null) config = new MoneroWalletConfig(); + //boolean random = config.getSeed() == null && config.getPrimaryAddress() == null; + if (config.getNetworkType() == null) config.setNetworkType(TestUtils.NETWORK_TYPE); + if (config.getServer() == null && config.getConnectionManager() == null) config.setServerUri(TestUtils.WALLET_LWS_URI); + + // create wallet + MoneroWalletLight wallet = MoneroWalletLight.createWallet(config); + if (startSyncing != false && wallet.isConnectedToDaemon()) { + wallet.startSyncing(TestUtils.SYNC_PERIOD_IN_MS); + syncWithDaemon(wallet); + } + return wallet; + } + + private MoneroWalletFull createWalletFull(MoneroWalletConfig config) { + return createWalletFull(config, true); + } + + private MoneroWalletFull createWalletFull(MoneroWalletConfig config, boolean startSyncing) { + + // assign defaults + if (config == null) config = new MoneroWalletConfig(); + boolean random = config.getSeed() == null && config.getPrimaryAddress() == null; + if (config.getPath() == null) config.setPath(TestUtils.TEST_WALLETS_DIR + "/" + UUID.randomUUID().toString()); + if (config.getPassword() == null) config.setPassword(TestUtils.WALLET_PASSWORD); + if (config.getNetworkType() == null) config.setNetworkType(TestUtils.NETWORK_TYPE); + if (config.getServer() == null && config.getConnectionManager() == null) config.setServerUri(TestUtils.DAEMON_RPC_URI); + + // create wallet + MoneroWalletFull wallet = MoneroWalletFull.createWallet(config); + if (!random) assertEquals(config.getRestoreHeight() == null ? 0l : config.getRestoreHeight(), wallet.getRestoreHeight()); + if (startSyncing != false && wallet.isConnectedToDaemon()) wallet.startSyncing(TestUtils.SYNC_PERIOD_IN_MS); + return wallet; + } + + @Override + public void closeWallet(MoneroWallet wallet, boolean save) { + wallet.close(save); + } + + /** + * Get the wallet's supported languages for the seed. This is an + * instance method for wallet rpc and a static utility for other wallets. + * + * @return List are the wallet's supported languages + */ + @Override + protected List getSeedLanguages() { + return MoneroWalletLight.getSeedLanguages(); + } + + // ------------------------------- BEGIN TESTS ------------------------------ + + // Can create a full wallet from keys + @Test + public void testCreateWalletFromKeysJni() { + assumeTrue(TEST_NON_RELAYS); + + // recreate test wallet from keys + MoneroWalletLight walletKeys = openWallet(new MoneroWalletConfig().setServerUri(TestUtils.WALLET_LWS_URI).setSeed(TestUtils.SEED).setNetworkType(TestUtils.NETWORK_TYPE), false); + testCreateWalletFromKeysJni(walletKeys); + } + + // Is compatible with monero-wallet-rpc outputs and offline transaction signing + @SuppressWarnings("unused") + @Test + public void testViewOnlyAndOfflineWalletCompatibility() throws InterruptedException, IOException { + assumeTrue(!LITE_MODE && (TEST_NON_RELAYS || TEST_RELAYS)); + + // create view-only wallet in wallet rpc process + MoneroWalletLight viewOnlyWallet = openWallet(new MoneroWalletConfig().setPrimaryAddress(wallet.getPrimaryAddress()).setPrivateViewKey(wallet.getPrivateViewKey()).setServerUri(TestUtils.WALLET_LWS_URI)); + syncWithDaemon(viewOnlyWallet); + + // create offline full wallet + MoneroWalletFull offlineWallet = createWalletFull(new MoneroWalletConfig().setPrimaryAddress(wallet.getPrimaryAddress()).setPrivateViewKey(wallet.getPrivateViewKey()).setPrivateSpendKey(wallet.getPrivateSpendKey()).setServerUri(TestUtils.OFFLINE_SERVER_URI).setRestoreHeight(0l)); + + // test tx signing with wallets + try { + testViewOnlyAndOfflineWallets(viewOnlyWallet, offlineWallet); + } finally { + closeWallet(offlineWallet); + } + }; + + // Does not interfere with other wallet notifications + @Test + public void testWalletsDoNotInterfere() { + MoneroWalletLight wallet1 = openWallet(new MoneroWalletConfig().setSeed(TestUtils.SEED), false); + MoneroWalletLight wallet2 = openWallet(new MoneroWalletConfig().setSeed(TestUtils.SEED), false); + + testWalletsDoNotInterfere(wallet1, wallet2, TestUtils.FIRST_RECEIVE_HEIGHT + 5, TestUtils.FIRST_RECEIVE_HEIGHT); + } + + // Can be closed + @Test + public void testClose() { + MoneroWalletConfig config = new MoneroWalletConfig().setPrimaryAddress(TestUtils.ADDRESS).setPrivateViewKey(TestUtils.PRIVATE_VIEW_KEY).setServerUri(TestUtils.WALLET_LWS_URI); + testClose(new MoneroWalletConfig(), config); + } + + // ---------------------------------- HELPERS ------------------------------- + + // jni-specific tx tests + @Override + protected void testTxWallet(MoneroTxWallet tx, TxContext ctx) { + if (ctx == null) ctx = new TxContext(); + + // run common tests + super.testTxWallet(tx, ctx); + } + + // -------------------- OVERRIDES TO BE DIRECTLY RUNNABLE ------------------- + + @Override + @Test + public void testSyncRandom() { + super.testSyncRandom(); + } + + @Override + @Test + public void testResyncExisting() { + super.testResyncExisting(); + } + + @Override + @Test + public void testSyncWalletFromKeys() { + super.testSyncWalletFromKeys(); + } + + @Override + @Test + public void testStartStopSyncing() { + super.testStartStopSyncing(); + } + + @Override + @Test + @Disabled + public void testMemoryLeak() { + super.testMemoryLeak(); + } + + @Override + @Test + public void testGetDaemonMaxPeerHeight() { + super.testGetDaemonMaxPeerHeight(); + } + + @Override + @Test + public void testDaemon() { + super.testDaemon(); + } + + @Override + @Test + public void testCreateWalletRandom() { + super.testCreateWalletRandom(); + } + + @Override + @Test + public void testCreateWalletFromSeed() { + super.testCreateWalletFromSeed(); + } + + @Override + @Test + @Disabled // bug in monero_wallet_keys? + public void testCreateWalletFromSeedWithOffset() { + testCreateWalletFromSeedWithOffset(GenUtils.getUUID()); + } + + @Override + @Test + public void testCreateWalletFromKeys() { + super.testCreateWalletFromKeys(); + } + + @Override + @Test + @Disabled // TODO too much high height for light wallet + public void testSubaddressLookahead() { + super.testSubaddressLookahead(); + } + + @Override + @Test + public void testGetVersion() { + super.testGetVersion(); + } + + @Override + @Disabled // not supported by light wallet + @Test + public void testGetPath() { + super.testGetPath(); + } + + @Override + @Test + public void testSetDaemonConnection() { + super.testSetDaemonConnection(); + } + + @Test + @Disabled + @Override + public void testConnectionManager() { + super.testConnectionManager(); + } + + @Override + @Test + public void testGetHeight() { + super.testGetHeight(); + } + + @Override + @Test + @Disabled // not supported by light wallet + public void testGetHeightByDate() { + super.testGetHeightByDate(); + } + + @Override + @Test + public void testGetSeed() { + super.testGetSeed(); + } + + @Override + @Test + public void testGetSeedLanguages() { + super.testGetSeedLanguages(); + } + + @Override + @Test + public void testGetPrivateViewKey() { + super.testGetPrivateViewKey(); + } + + @Override + @Test + public void testGetPrivateSpendKey() { + super.testGetPrivateSpendKey(); + } + + @Override + @Test + public void testGetPublicViewKey() { + super.testGetPublicViewKey(); + } + + @Override + @Test + public void testGetPublicSpendKey() { + super.testGetPublicSpendKey(); + } + + @Override + @Test + public void testGetPrimaryAddress() { + super.testGetPrimaryAddress(); + } + + @Override + @Test + public void testGetIntegratedAddress() { + super.testGetIntegratedAddress(); + } + + @Override + @Test + public void testDecodeIntegratedAddress() { + super.testDecodeIntegratedAddress(); + } + + @Override + @Test + public void testSyncWithoutProgress() { + super.testSyncWithoutProgress(); + } + + @Override + @Test + public void testWalletEqualityGroundTruth() { + super.testWalletEqualityGroundTruth(); + } + + @Override + @Test + public void testGetAccountsWithoutSubaddresses() { + super.testGetAccountsWithoutSubaddresses(); + } + + @Override + @Test + public void testGetAccountsWithSubaddresses() { + super.testGetAccountsWithSubaddresses(); + } + + @Override + @Test + public void testGetAccount() { + super.testGetAccount(); + } + + @Override + @Test + public void testCreateAccountWithoutLabel() { + super.testCreateAccountWithoutLabel(); + } + + @Override + @Test + public void testCreateAccountWithLabel() { + super.testCreateAccountWithLabel(); + } + + @Override + @Test + public void testSetAccountLabel() { + super.testSetAccountLabel(); + } + + @Override + @Test + public void testGetSubaddresses() { + super.testGetSubaddresses(); + } + + @Override + @Test + public void testGetSubaddressesByIndices() { + super.testGetSubaddressesByIndices(); + } + + @Override + @Test + public void testGetSubaddressByIndex() { + super.testGetSubaddressByIndex(); + } + + @Override + @Test + public void testCreateSubaddress() { + super.testCreateSubaddress(); + } + + @Override + @Test + public void testSetSubaddressLabel() { + super.testSetSubaddressLabel(); + } + + @Override + @Test + public void testGetSubaddressAddress() { + super.testGetSubaddressAddress(); + } + + @Override + @Test + public void testGetAddressIndices() { + super.testGetAddressIndices(); + } + + @Override + @Test + public void testGetAllBalances() { + super.testGetAllBalances(); + } + + @Override + @Test + public void testGetTxsWallet() { + super.testGetTxsWallet(); + } + + @Override + @Test + public void testGetTxsByHash() { + super.testGetTxsByHash(); + } + + @Override + @Test + public void testGetTxsWithQuery() { + super.testGetTxsWithQuery(); + } + + @Override + @Test + public void testGetTxsByHeight() { + super.testGetTxsByHeight(); + } + + @Override + @Test + @Disabled // TODO implement payment for light wallet + public void testGetTxsWithPaymentIds() { + /* LITE_MODE enabled */ + super.testGetTxsWithPaymentIds(); + } + + @Override + @Test + public void testGetTxsFieldsWithFiltering() { + super.testGetTxsFieldsWithFiltering(); + } + + @Override + @Test + public void testValidateInputsGetTxs() { + super.testValidateInputsGetTxs(); + } + + @Override + @Test + public void testGetTransfers() { + super.testGetTransfers(); + } + + @Override + @Test + public void testGetTransfersWithQuery() { + super.testGetTransfersWithQuery(); + } + + @Override + @Test + public void testValidateInputsGetTransfers() { + super.testValidateInputsGetTransfers(); + } + + @Override + @Test + public void testGetIncomingOutgoingTransfers() { + super.testGetIncomingOutgoingTransfers(); + } + + @Override + @Test + public void testGetOutputs() { + super.testGetOutputs(); + } + + @Override + @Test + public void testGetOutputsWithQuery() { + super.testGetOutputsWithQuery(); + } + + @Override + @Test + public void testValidateInputsGetOutputs() { + super.testValidateInputsGetOutputs(); + } + + @Override + @Test + public void testAccounting() { + super.testAccounting(); + } + + @Override + @Test + @Disabled // TODO implemenent for light wallet + public void testCheckTxKey() { + super.testCheckTxKey(); + } + + @Override + @Test + @Disabled // TODO implemenent for light wallet + public void testCheckTxProof() { + super.testCheckTxProof(); + } + + @Override + @Test + @Disabled // TODO implemenent for light wallet + public void testCheckSpendProof() { + super.testCheckSpendProof(); + } + + @Override + @Test + @Disabled // TODO still no way to get rpc_version from lws + public void testGetReserveProofWallet() { + super.testGetReserveProofWallet(); + } + + @Override + @Test + @Disabled // TODO still no way to get rpc_version from lws + public void testGetReserveProofAccount() { + super.testGetReserveProofAccount(); + } + + @Override + @Test + public void testSetTxNote() { + super.testSetTxNote(); + } + + @Override + @Test + public void testSetTxNotes() { + super.testSetTxNotes(); + } + + @Override + @Test + public void testExportOutputs() { + super.testExportOutputs(); + } + + @Override + @Disabled + @Test + public void testImportOutputs() { + super.testImportOutputs(); + } + + @Override + @Test + public void testExportKeyImages() { + super.testExportKeyImages(); + } + + @Override + @Test + @Disabled + public void testImportKeyImages() { + super.testImportKeyImages(); + } + + @Override + @Test + @Disabled + public void testGetNewKeyImagesFromLastImport() { + super.testGetNewKeyImagesFromLastImport(); + } + + @SuppressWarnings("unused") + @Test + @Override + public void testViewOnlyAndOfflineWallets() { + assumeTrue(!LITE_MODE && (TEST_NON_RELAYS || TEST_RELAYS)); + + // create view-only and offline wallets + MoneroWallet viewOnlyWallet = openWallet(new MoneroWalletConfig().setPrimaryAddress(wallet.getPrimaryAddress()).setPrivateViewKey(wallet.getPrivateViewKey()).setServer(wallet.getDaemonConnection())); + MoneroWallet offlineWallet = createWalletFull(new MoneroWalletConfig().setPrimaryAddress(wallet.getPrimaryAddress()).setPrivateViewKey(wallet.getPrivateViewKey()).setPrivateSpendKey(wallet.getPrivateSpendKey()).setServerUri(TestUtils.OFFLINE_SERVER_URI).setRestoreHeight(0l)); + assertFalse(offlineWallet.isConnectedToDaemon()); + syncWithDaemon(viewOnlyWallet); + + // test tx signing with wallets + try { + testViewOnlyAndOfflineWallets(viewOnlyWallet, offlineWallet); + } finally { + closeWallet(viewOnlyWallet); + closeWallet(offlineWallet); + } + } + + @Override + @Test + public void testSignAndVerifyMessages() { + super.testSignAndVerifyMessages(); + } + + @Override + @Test + public void testAddressBook() { + super.testAddressBook(); + } + + @Override + @Test + public void testSetAttributes() { + super.testSetAttributes(); + } + + @Override + @Test + public void testGetPaymentUri() { + super.testGetPaymentUri(); + } + + @Override + @Test + @Disabled // not supported by light wallet + public void testMining() { + super.testMining(); + } + + @Override + @Test + public void testValidateInputsSendingFunds() { + super.testValidateInputsSendingFunds(); + } + + @Override + @Test + @Disabled // unconfirmed txs not supported by lws + public void testSyncWithPoolSameAccounts() { + super.testSyncWithPoolSameAccounts(); + } + + @Override + @Test + @Disabled // unconfirmed txs not supported by lws + public void testSyncWithPoolSubmitAndRelay() { + super.testSyncWithPoolSubmitAndRelay(); + } + + @Override + @Test + @Disabled // unconfirmed txs not supported by lws + public void testSyncWithPoolRelay() { + super.testSyncWithPoolRelay(); + } + + @Override + @Test + @Disabled // unconfirmed txs not supported by lws + public void testSyncWithPoolSubmitAndFlush() { + super.testSyncWithPoolSubmitAndFlush(); + } + + @Override + @Test + public void testSendToSelf() { + super.testSendToSelf(); + } + + @Override + @Test + public void testSendToExternal() { + super.testSendToExternal(); + } + + @Override + @Test + public void testSendFromSubaddresses() { + super.testSendFromSubaddresses(); + } + + @Override + @Test + @Disabled // TODO implement tx splitting for light wallet + public void testSendFromSubaddressesSplit() { + super.testSendFromSubaddressesSplit(); + } + + @Override + @Test + public void testSend() { + super.testSend(); + } + + @Override + @Test + public void testSendWithPaymentId() { + super.testSendWithPaymentId(); + } + + @Override + @Test + @Disabled // TODO implement tx splitting for light wallet + public void testSendSplit() { + super.testSendSplit(); + } + + @Override + @Test + public void testCreateThenRelay() { + super.testCreateThenRelay(); + } + + @Override + @Test + @Disabled // TODO implement tx splitting for light wallet + public void testCreateThenRelaySplit() { + super.testCreateThenRelaySplit(); + } + + @Override + @Test + public void testSendToMultiple() { + super.testSendToMultiple(); + } + + @Override + @Test + @Disabled // TODO implement tx splitting for light wallet + public void testSendToMultipleSplit() { + super.testSendToMultipleSplit(); + } + + @Override + @Test + @Disabled // TODO implement tx splitting for light wallet + public void testSendDustToMultipleSplit() { + super.testSendDustToMultipleSplit(); + } + + @Override + @Test + @Disabled + public void testSubtractFeeFrom() { + super.testSubtractFeeFrom(); + } + + @Override + @Test + @Disabled // TODO implement tx splitting for light wallet + public void testSubtractFeeFromSplit() { + super.testSubtractFeeFromSplit(); + } + + @Override + @Test + @Disabled + public void testUpdateLockedSameAccount() { + super.testUpdateLockedSameAccount(); + } + + @Override + @Test + @Disabled + public void testUpdateLockedSameAccountSplit() { + super.testUpdateLockedSameAccountSplit(); + } + + @Override + @Test + @Disabled + public void testUpdateLockedDifferentAccounts() { + super.testUpdateLockedDifferentAccounts(); + } + + @Override + @Test + @Disabled + public void testUpdateLockedDifferentAccountsSplit() { + super.testUpdateLockedDifferentAccountsSplit(); + } + + @Override + @Disabled // TODO implement + @Test + public void testSweepOutputs() { + super.testSweepOutputs(); + } + + @Override + @Disabled // TODO implement + @Test + public void testSweepSubaddresses() { + super.testSweepSubaddresses(); + } + + @Override + @Disabled // TODO implement + @Test + public void testSweepAccounts() { + super.testSweepAccounts(); + } + + @Override + @Disabled // TODO implement + @Test + public void testSweepWalletByAccounts() { + super.testSweepWalletByAccounts(); + } + + @Override + @Disabled // TODO implement + @Test + public void testSweepWalletBySubaddresses() { + super.testSweepWalletBySubaddresses(); + } + + @Override + @Disabled // TODO implement + @Test + public void testSweepDustNoRelay() { + super.testSweepDustNoRelay(); + } + + @Override + @Disabled // TODO implement + @Test + public void testSweepDust() { + super.testSweepDust(); + } + + @Override + @Test + public void testScanTxs() { + // cannot recreate wallet on same lws server, so open it + MoneroWallet scanWallet = openWallet(new MoneroWalletConfig().setSeed(wallet.getSeed())); + testScanTxs(scanWallet); + } + + @Override + @Test + public void testRescanBlockchain() { + super.testRescanBlockchain(); + } + + @Override + @Test + @Disabled // not supported by light wallet + public void testMultisig() { + super.testMultisig(); + } + + @Override + @Test + @Disabled // not supported by light wallet + public void testMultisigStress() { + super.testMultisigStress(); + } + + @Override + @Disabled // not supported by light wallet + @Test + public void testChangePassword() { + super.testChangePassword(); + } + + @Override + @Disabled // not supported by light wallet + @Test + public void testSaveAndClose() { + super.testSaveAndClose(); + } + + @Override + @Test + @Tag("NotificationTest") + @Disabled + public void testNotificationsDifferentWallet() { + super.testNotificationsDifferentWallet(); + } + + @Override + @Test + @Tag("NotificationTest") + @Disabled + public void testNotificationsDifferentWalletWhenRelayed() { + super.testNotificationsDifferentWalletWhenRelayed(); + } + + @Override + @Test + @Tag("NotificationTest") + @Disabled + public void testNotificationsDifferentAccounts() { + super.testNotificationsDifferentAccounts(); + } + + @Override + @Test + @Disabled + @Tag("NotificationTest") + public void testNotificationsSameAccount() { + super.testNotificationsSameAccount(); + } + + @Override + @Test + @Tag("NotificationTest") + @Disabled + public void testNotificationsDifferentAccountSweepOutput() { + super.testNotificationsDifferentAccountSweepOutput(); + } + + @Override + @Test + @Tag("NotificationTest") + @Disabled + public void testNotificationsSameAccountSweepOutputWhenRelayed() { + super.testNotificationsSameAccountSweepOutputWhenRelayed(); + } + + @Override + @Test + public void testStopListening() { + super.testStopListening(); + } + + @Override + @Test + public void testCreateAndReceive() { + super.testCreateAndReceive(); + } + + @Override + @Test + public void testFreezeOutputs() { + super.testFreezeOutputs(); + } + + @Override + @Test + @Disabled + public void testInputKeyImages() { + super.testInputKeyImages(); + } + + @Override + @Test + @Disabled // light wallet cannot prove unrelayed txs + public void testProveUnrelayedTxs() { + super.testProveUnrelayedTxs(); + } + + @Override + @Test + public void testGetDefaultFeePriority() { + super.testGetDefaultFeePriority(); + } + + @Override + @Test + public void testGetSeedLanguage() { + super.testGetSeedLanguage(); + } + + @Override + @Test + public void testGetSubaddressAddressOutOfRange() { + super.testGetSubaddressAddressOutOfRange(); + } + +} diff --git a/src/test/java/TestMoneroWalletRpc.java b/src/test/java/TestMoneroWalletRpc.java index e31bbb91..590c5c5f 100644 --- a/src/test/java/TestMoneroWalletRpc.java +++ b/src/test/java/TestMoneroWalletRpc.java @@ -87,6 +87,11 @@ protected MoneroWalletRpc openWallet(MoneroWalletConfig config) { return TestUtils.openWalletRpc(config); } + @Override + protected MoneroWalletRpc openWallet(MoneroWalletConfig config, boolean startSyncing) { + throw new RuntimeException("Not supported"); + } + @Override protected MoneroWalletRpc createWallet(MoneroWalletConfig config) { return TestUtils.createWalletRpc(config); @@ -108,6 +113,12 @@ protected List getSeedLanguages() { return ((MoneroWalletRpc) wallet).getSeedLanguages(); } + @Override + protected MoneroWalletRpc createWallet(MoneroWalletConfig config, boolean startSyncing) + { + throw new RuntimeException("Not supported"); + } + // ---------------------------- BEGIN TESTS --------------------------------- // Can create a wallet with a randomly generated seed @@ -404,6 +415,56 @@ protected void testSignatureHeaderCheckError(MoneroError e) { // -------------------- OVERRIDES TO BE DIRECTLY RUNNABLE ------------------- + @Override + @Test + @Disabled // Never tested for wallet rpc + public void testSyncRandom() { + super.testSyncRandom(); + } + + + @Override + @Test + @Disabled // Never tested for wallet rpc + public void testResyncExisting() { + super.testResyncExisting(); + } + + @Override + @Test + @Disabled // Never tested for wallet rpc + public void testSyncWalletFromKeys() { + super.testSyncWalletFromKeys(); + } + + @Override + @Test + @Disabled // Never tested for wallet rpc + public void testStartStopSyncing() { + super.testStartStopSyncing(); + } + + @Override + @Test + @Disabled // Not implemented for wallet rpc + public void testGetDaemonMaxPeerHeight() { + super.testGetDaemonMaxPeerHeight(); + } + + @Override + @Test + @Disabled // Never tested for wallet rpc + public void testDaemon() { + super.testDaemon(); + } + + @Override + @Test + @Disabled // Not implemented for wallet rpc + public void testMemoryLeak() { + super.testMemoryLeak(); + } + @Override @Test public void testCreateWalletRandom() { diff --git a/src/test/java/utils/SyncProgressTester.java b/src/test/java/utils/SyncProgressTester.java new file mode 100644 index 00000000..314dd1a5 --- /dev/null +++ b/src/test/java/utils/SyncProgressTester.java @@ -0,0 +1,82 @@ +package utils; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import monero.wallet.MoneroWallet; + +public class SyncProgressTester extends WalletSyncPrinter { + + protected MoneroWallet wallet; + protected Long prevHeight; + protected long startHeight; + protected long prevEndHeight; + protected Long prevCompleteHeight; + protected boolean isDone; + protected Boolean onSyncProgressAfterDone; + + public SyncProgressTester(MoneroWallet wallet, long startHeight, long endHeight) { + this.wallet = wallet; + assertTrue(startHeight >= 0); + assertTrue(endHeight >= 0); + this.startHeight = startHeight; + this.prevEndHeight = endHeight; + this.isDone = false; + } + + @Override + public synchronized void onSyncProgress(long height, long startHeight, long endHeight, double percentDone, String message) { + super.onSyncProgress(height, startHeight, endHeight, percentDone, message); + + // registered wallet listeners will continue to get sync notifications after the wallet's initial sync + if (isDone) { + assertTrue(wallet.getListeners().contains(this), "Listener has completed and is not registered so should not be called again"); + onSyncProgressAfterDone = true; + } + + // update tester's start height if new sync session + if (prevCompleteHeight != null && startHeight == prevCompleteHeight) this.startHeight = startHeight; + + // if sync is complete, record completion height for subsequent start heights + if (Double.compare(percentDone, 1) == 0) prevCompleteHeight = endHeight; + + // otherwise start height is equal to previous completion height + else if (prevCompleteHeight != null) assertEquals((long) prevCompleteHeight, startHeight); + + assertTrue(endHeight > startHeight, "end height > start height"); + assertEquals(this.startHeight, startHeight); + assertTrue(endHeight >= prevEndHeight); // chain can grow while syncing + prevEndHeight = endHeight; + assertTrue(height >= startHeight); + assertTrue(height < endHeight); + double expectedPercentDone = (double) (height - startHeight + 1) / (double) (endHeight - startHeight); + assertTrue(Double.compare(expectedPercentDone, percentDone) == 0); + + if (prevHeight == null) assertEquals(startHeight, height); + else assertEquals(height, prevHeight + 1); + + prevHeight = height; + } + + public void onDone(long chainHeight) { + assertFalse(isDone); + this.isDone = true; + if (prevHeight == null) { + assertNull(prevCompleteHeight); + assertEquals(chainHeight, startHeight); + } else { + assertEquals(chainHeight - 1, (long) prevHeight); // otherwise last height is chain height - 1 + assertEquals(chainHeight, (long) prevCompleteHeight); + } + } + + public Boolean isNotified() { + return prevHeight != null; + } + + public Boolean getOnSyncProgressAfterDone() { + return onSyncProgressAfterDone; + } +} diff --git a/src/test/java/utils/TestUtils.java b/src/test/java/utils/TestUtils.java index 4331f4af..65c39d34 100644 --- a/src/test/java/utils/TestUtils.java +++ b/src/test/java/utils/TestUtils.java @@ -24,6 +24,7 @@ import monero.daemon.MoneroDaemonRpc; import monero.daemon.model.MoneroNetworkType; import monero.wallet.MoneroWalletFull; +import monero.wallet.MoneroWalletLight; import monero.wallet.MoneroWalletRpc; import monero.wallet.model.MoneroTxWallet; import monero.wallet.model.MoneroWalletConfig; @@ -34,7 +35,7 @@ public class TestUtils { // c++ log configuration - public static boolean CPP_LOG_ENABLED = false; + public static boolean CPP_LOG_ENABLED = true; public static String CPP_LOG_PATH = "log_java_tests.txt"; public static int CPP_LOG_LEVEL = 1; public static boolean CPP_LOG_CONSOLE = true; @@ -50,7 +51,7 @@ public class TestUtils { public static final String MONERO_BINS_DIR = ""; // monero daemon rpc endpoint configuration (change per your configuration) - public static final String DAEMON_RPC_URI = "localhost:28081"; + public static final String DAEMON_RPC_URI = "localhost:48081"; public static final String DAEMON_RPC_USERNAME = ""; public static final String DAEMON_RPC_PASSWORD = ""; public static final String DAEMON_LOCAL_PATH = MONERO_BINS_DIR + "/monerod"; @@ -72,19 +73,28 @@ public class TestUtils { public static final String WALLET_RPC_LOCAL_WALLET_DIR = MONERO_BINS_DIR; public static final String WALLET_RPC_ACCESS_CONTROL_ORIGINS = "http://localhost:8080"; // cors access from web browser + // monero wallet lws configuration (change per your configuration) + public static final int WALLET_LWS_PORT_START = 8443; + public static final int WALLET_LWS_ADMIN_PORT_START = 8444; + public static final String WALLET_LWS_DOMAIN = "localhost"; + public static final String WALLET_LWS_ADMIN_DOMAIN = "localhost"; + public static final String WALLET_LWS_URI = WALLET_LWS_DOMAIN + ":" + WALLET_LWS_PORT_START; + public static final String WALLET_LWS_ADMIN_URI = WALLET_LWS_ADMIN_DOMAIN + ":" + WALLET_LWS_ADMIN_PORT_START; + // test wallet config public static final String WALLET_NAME = "test_wallet_1"; public static final String WALLET_PASSWORD = "supersecretpassword123"; public static final String TEST_WALLETS_DIR = "./test_wallets"; public static final String WALLET_FULL_PATH = TEST_WALLETS_DIR + "/" + WALLET_NAME; - + public static final String WALLET_LIGHT_PATH = TEST_WALLETS_DIR + "/test_wallet_light"; // test wallet constants public static final BigInteger MAX_FEE = BigInteger.valueOf(7500000).multiply(BigInteger.valueOf(10000)); public static final MoneroNetworkType NETWORK_TYPE = MoneroNetworkType.TESTNET; public static final String LANGUAGE = "English"; public static final String SEED = "silk mocked cucumber lettuce hope adrenalin aching lush roles fuel revamp baptism wrist long tender teardrop midst pastry pigment equip frying inbound pinched ravine frying"; public static final String ADDRESS = "A1y9sbVt8nqhZAVm3me1U18rUVXcjeNKuBd1oE2cTs8biA9cozPMeyYLhe77nPv12JA3ejJN3qprmREriit2fi6tJDi99RR"; - public static final long FIRST_RECEIVE_HEIGHT = 171; // NOTE: this value must be the height of the wallet's first tx for tests + public static final String PRIVATE_VIEW_KEY = "198820da9166ee114203eb38c29e00b0e8fc7df508aa632d56ead849093d3808"; + public static final long FIRST_RECEIVE_HEIGHT = 160; // NOTE: this value must be the height of the wallet's first tx for tests public static final long SYNC_PERIOD_IN_MS = 5000; // period between wallet syncs in milliseconds public static final String OFFLINE_SERVER_URI = "offline_server_uri"; // dummy server uri to remain offline because wallet2 connects to default if not given public static final long AUTO_CONNECT_TIMEOUT_MS = 3000; @@ -297,6 +307,45 @@ public static MoneroWalletFull getWalletFull() { return walletFull; } + public static MoneroWalletConfig getWalletLightConfig() { + MoneroWalletConfig config = new MoneroWalletConfig(); + return config.setNetworkType(MoneroNetworkType.TESTNET).setServerUri(TestUtils.WALLET_LWS_URI).setSeed(TestUtils.SEED).setAccountLookahead(1).setSubaddressLookahead(11); + } + + private static MoneroWalletLight walletLight; + public static MoneroWalletLight getWalletLight() { + + if (walletLight == null || walletLight.isClosed()) { + // create wallet from seed if it doesn't exist + if (!MoneroWalletLight.walletExists(getWalletLightConfig())) { + + // create directory for test wallets if it doesn't exist + File testWalletsDir = new File(TestUtils.TEST_WALLETS_DIR); + if (!testWalletsDir.exists()) testWalletsDir.mkdirs(); + + // create wallet with connection + MoneroRpcConnection daemonConnection = new MoneroRpcConnection(WALLET_LWS_URI, "", ""); + walletLight = MoneroWalletLight.createWallet(new MoneroWalletConfig().setNetworkType(NETWORK_TYPE).setSeed(TestUtils.SEED).setServer(daemonConnection)); + //assertEquals(TestUtils.FIRST_RECEIVE_HEIGHT, walletLight.getRestoreHeight()); + assertEquals(daemonConnection, walletLight.getDaemonConnection()); + } + // otherwise open existing wallet and update daemon connection + else { + walletLight = MoneroWalletLight.openWallet(getWalletLightConfig()); + } + } + + assertEquals(TestUtils.PRIVATE_VIEW_KEY, walletLight.getPrivateViewKey()); + assertEquals(TestUtils.ADDRESS, walletLight.getPrimaryAddress()); + assertEquals(TestUtils.SEED, walletLight.getSeed()); + + // sync and save wallet + walletLight.sync(new WalletSyncPrinter()); + walletLight.startSyncing(TestUtils.SYNC_PERIOD_IN_MS); + + return walletLight; + } + /** * Creates a new wallet considered to be "ground truth". * diff --git a/src/test/java/utils/WalletEqualityUtils.java b/src/test/java/utils/WalletEqualityUtils.java index b30db1f5..a48a5735 100644 --- a/src/test/java/utils/WalletEqualityUtils.java +++ b/src/test/java/utils/WalletEqualityUtils.java @@ -16,6 +16,7 @@ import monero.daemon.model.MoneroOutput; import monero.daemon.model.MoneroTx; import monero.wallet.MoneroWallet; +import monero.wallet.MoneroWalletLight; import monero.wallet.MoneroWalletRpc.IncomingTransferComparator; import monero.wallet.model.MoneroAccount; import monero.wallet.model.MoneroIncomingTransfer; @@ -65,15 +66,16 @@ public static void testWalletEqualityOnChain(MoneroWallet w1, MoneroWallet w2) { assertEquals(w1.getPrimaryAddress(), w2.getPrimaryAddress()); assertEquals(w1.getPrivateViewKey(), w2.getPrivateViewKey()); assertEquals(w1.getPrivateSpendKey(), w2.getPrivateSpendKey()); + Boolean lightWallet = w1 instanceof MoneroWalletLight || w2 instanceof MoneroWalletLight; MoneroTxQuery txQuery = new MoneroTxQuery().setIsConfirmed(true); - testTxWalletsEqualOnChain(w1.getTxs(txQuery), w2.getTxs(txQuery)); + testTxWalletsEqualOnChain(w1.getTxs(txQuery), w2.getTxs(txQuery), lightWallet); txQuery.setIncludeOutputs(true); - testTxWalletsEqualOnChain(w1.getTxs(txQuery), w2.getTxs(txQuery)); // fetch and compare outputs + testTxWalletsEqualOnChain(w1.getTxs(txQuery), w2.getTxs(txQuery), lightWallet); // fetch and compare outputs testAccountsEqualOnChain(w1.getAccounts(true), w2.getAccounts(true)); assertEquals(w1.getBalance(), w2.getBalance()); assertEquals(w1.getUnlockedBalance(), w2.getUnlockedBalance()); MoneroTransferQuery transferQuery = new MoneroTransferQuery().setTxQuery(new MoneroTxQuery().setIsConfirmed(true)); - testTransfersEqualOnChain(w1.getTransfers(transferQuery), w2.getTransfers(transferQuery)); + testTransfersEqualOnChain(w1.getTransfers(transferQuery), w2.getTransfers(transferQuery), lightWallet); MoneroOutputQuery outputQuery = new MoneroOutputQuery().setTxQuery(new MoneroTxQuery().setIsConfirmed(true)); testOutputWalletsEqualOnChain(w1.getOutputs(outputQuery), w2.getOutputs(outputQuery)); } @@ -141,7 +143,7 @@ private static void testSubaddressesEqualOnChain(MoneroSubaddress subaddress1, M assertEquals(subaddress1, subaddress2); } - public static void testTxWalletsEqualOnChain(List txs1, List txs2) { + public static void testTxWalletsEqualOnChain(List txs1, List txs2, boolean lightWallet) { // remove pool or failed txs for comparison txs1 = new ArrayList(txs1); @@ -181,7 +183,13 @@ public static void testTxWalletsEqualOnChain(List txs1, List txs1, List transfers) + { + if (transfers == null) + { + return; + } + + for (MoneroTransfer moneroTransfer : transfers) { + sanitizeTransfer(moneroTransfer); + } + } + + private static void sanitizeTransfer(MoneroTransfer transfer) + { + if (transfer instanceof MoneroIncomingTransfer) + { + ((MoneroIncomingTransfer)transfer).setNumSuggestedConfirmations(null); + } + } private static void transferCachedInfo(MoneroTxWallet src, MoneroTxWallet tgt) { @@ -223,7 +256,7 @@ private static void transferCachedInfo(MoneroTxWallet src, MoneroTxWallet tgt) { if (tgt.getOutgoingTransfer() != null) tgt.setPaymentId(src.getPaymentId()); } - private static void testTransfersEqualOnChain(List transfers1, List transfers2) { + private static void testTransfersEqualOnChain(List transfers1, List transfers2, Boolean lightWallet) { assertEquals(transfers1.size(), transfers2.size()); // test and collect transfers per transaction @@ -307,6 +340,13 @@ private static void testTransfersEqualOnChain(List transfers1, L } // compare transfer equality + + if (lightWallet) { + // cannot calculate accure numSuggestedConfirmations when running private testnet + sanitizeTransfer(transfer1); + sanitizeTransfer(transfer2); + } + assertEquals(transfer1, transfer2); } } diff --git a/src/test/java/utils/WalletSyncTester.java b/src/test/java/utils/WalletSyncTester.java new file mode 100644 index 00000000..9e841f5f --- /dev/null +++ b/src/test/java/utils/WalletSyncTester.java @@ -0,0 +1,131 @@ +package utils; + +import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.math.BigInteger; + +import monero.wallet.MoneroWallet; +import monero.wallet.model.MoneroOutputWallet; +import monero.wallet.model.MoneroTxQuery; +import monero.wallet.model.MoneroTxWallet; + +public class WalletSyncTester extends SyncProgressTester { + + private Long walletTesterPrevHeight; // renamed from prevHeight to not interfere with super's prevHeight + private MoneroOutputWallet prevOutputReceived; + private MoneroOutputWallet prevOutputSpent; + private BigInteger incomingTotal; + private BigInteger outgoingTotal; + private Boolean onNewBlockAfterDone; + private BigInteger prevBalance; + private BigInteger prevUnlockedBalance; + + public WalletSyncTester(MoneroWallet wallet, long startHeight, long endHeight) { + super(wallet, startHeight, endHeight); + assertTrue(startHeight >= 0); + assertTrue(endHeight >= 0); + incomingTotal = BigInteger.valueOf(0); + outgoingTotal = BigInteger.valueOf(0); + } + + @Override + public synchronized void onNewBlock(long height) { + if (isDone) { + assertTrue(wallet.getListeners().contains(this), "Listener has completed and is not registered so should not be called again"); + onNewBlockAfterDone = true; + } + if (walletTesterPrevHeight != null) { + assertEquals(walletTesterPrevHeight + 1, height); + } + assertTrue(height >= super.startHeight); + walletTesterPrevHeight = height; + } + + @Override + public void onBalancesChanged(BigInteger newBalance, BigInteger newUnlockedBalance) { + if (this.prevBalance != null) assertTrue(!newBalance.equals(this.prevBalance) || !newUnlockedBalance.equals(this.prevUnlockedBalance)); + this.prevBalance = newBalance; + this.prevUnlockedBalance = newUnlockedBalance; + } + + @Override + public void onOutputReceived(MoneroOutputWallet output) { + assertNotNull(output); + prevOutputReceived = output; + + // test output + assertNotNull(output.getAmount()); + assertTrue(output.getAccountIndex() >= 0); + assertTrue(output.getSubaddressIndex() >= 0); + + // test output's tx + assertNotNull(output.getTx()); + assertNotNull(output.getTx().getHash()); + assertEquals(64, output.getTx().getHash().length()); + assertTrue(output.getTx().getVersion() >= 0); + assertTrue(output.getTx().getUnlockTime().compareTo(BigInteger.valueOf(0)) >= 0); + assertNull(output.getTx().getInputs()); + assertEquals(1, output.getTx().getOutputs().size()); + assertTrue(output.getTx().getOutputs().get(0) == output); + + // extra is not sent over the jni bridge + assertNull(output.getTx().getExtra()); + + // add incoming amount to running total + if (output.isLocked()) incomingTotal = incomingTotal.add(output.getAmount()); // TODO: only add if not unlocked, test unlocked received + } + + @Override + public void onOutputSpent(MoneroOutputWallet output) { + assertNotNull(output); + prevOutputSpent = output; + + // test output + assertNotNull(output.getAmount()); + assertTrue(output.getAccountIndex() >= 0); + if (output.getSubaddressIndex() != null) assertTrue(output.getSubaddressIndex() >= 0); // TODO (monero-project): can be undefined because inputs not provided so one created from outgoing transfer + + // test output's tx + assertNotNull(output.getTx()); + assertNotNull(output.getTx().getHash()); + assertEquals(64, output.getTx().getHash().length()); + assertTrue(output.getTx().getVersion() >= 0); + assertTrue(output.getTx().getUnlockTime().compareTo(BigInteger.valueOf(0)) >= 0); + assertEquals(1, output.getTx().getInputs().size()); + assertTrue(output.getTx().getInputs().get(0) == output); + assertNull(output.getTx().getOutputs()); + + // extra is not sent over the jni bridge + assertNull(output.getTx().getExtra()); + + // add outgoing amount to running total + if (output.isLocked()) outgoingTotal = outgoingTotal.add(output.getAmount()); + } + + @Override + public void onDone(long chainHeight) { + super.onDone(chainHeight); + assertNotNull(walletTesterPrevHeight); + assertNotNull(prevOutputReceived); + assertNotNull(prevOutputSpent); + BigInteger expectedBalance = incomingTotal.subtract(outgoingTotal); + + // output notifications do not include pool fees or outgoing amount + BigInteger poolSpendAmount = BigInteger.ZERO; + for (MoneroTxWallet poolTx : wallet.getTxs(new MoneroTxQuery().setInTxPool(true))) { + poolSpendAmount = poolSpendAmount.add(poolTx.getFee()).add(poolTx.getOutgoingAmount() == null ? BigInteger.ZERO : poolTx.getOutgoingAmount()); + } + expectedBalance = expectedBalance.subtract(poolSpendAmount); + + assertEquals(expectedBalance, wallet.getBalance()); + assertEquals(prevBalance, wallet.getBalance()); + assertEquals(prevUnlockedBalance, wallet.getUnlockedBalance()); + } + + public Boolean getOnNewBlockAfterDone() { + return onNewBlockAfterDone; + } +} \ No newline at end of file diff --git a/src/test/java/utils/WalletTxTracker.java b/src/test/java/utils/WalletTxTracker.java index eb4b4436..0841ce6a 100644 --- a/src/test/java/utils/WalletTxTracker.java +++ b/src/test/java/utils/WalletTxTracker.java @@ -8,6 +8,7 @@ import monero.daemon.model.MoneroMiningStatus; import monero.daemon.model.MoneroTx; import monero.wallet.MoneroWallet; +import monero.wallet.MoneroWalletLight; import monero.wallet.model.MoneroTxQuery; import monero.wallet.model.MoneroTxWallet; @@ -94,6 +95,19 @@ private void waitForTxsToClear(boolean clearFromWallet, MoneroWallet... wallets) try { TimeUnit.MILLISECONDS.sleep(TestUtils.SYNC_PERIOD_IN_MS); } catch (InterruptedException e) { throw new RuntimeException(e); } } + + // stop mining if started mining + try { + daemon.stopMining(); + } + catch (Exception e) { } + + // sync wallets with the pool + for (MoneroWallet wallet : wallets) { + while(wallet.getHeight() < daemon.getHeight()) { + wallet.sync(); + } + } } public BigInteger waitForUnlockedBalance(MoneroWallet wallet, Integer accountIndex, Integer subaddressIndex, BigInteger minAmount) { @@ -118,10 +132,19 @@ public BigInteger waitForUnlockedBalance(MoneroWallet wallet, Integer accountInd // wait for unlocked balance // TODO: promote to MoneroWallet interface? System.out.println("Waiting for unlocked balance"); - while (unlockedBalance.compareTo(minAmount) < 0) { - unlockedBalance = wallet.getUnlockedBalance(accountIndex, subaddressIndex); - try { TimeUnit.MILLISECONDS.sleep(TestUtils.SYNC_PERIOD_IN_MS); } - catch (InterruptedException e) { throw new RuntimeException(e); } + if (minAmount == BigInteger.valueOf(0) && wallet instanceof MoneroWalletLight) { + while(unlockedBalance.compareTo(wallet.getBalance(accountIndex, subaddressIndex)) < 0) { + unlockedBalance = wallet.getUnlockedBalance(accountIndex, subaddressIndex); + try { TimeUnit.MILLISECONDS.sleep(TestUtils.SYNC_PERIOD_IN_MS); } + catch (InterruptedException e) { throw new RuntimeException(e); } + } + } + else { + while (unlockedBalance.compareTo(minAmount) < 0) { + unlockedBalance = wallet.getUnlockedBalance(accountIndex, subaddressIndex); + try { TimeUnit.MILLISECONDS.sleep(TestUtils.SYNC_PERIOD_IN_MS); } + catch (InterruptedException e) { throw new RuntimeException(e); } + } } // stop mining if started