changes for monero v0.12 (#214)

* new version id & name
* witness checksums
* build docs updated for v0.12
* remove binaries
* setenv HOME for ringdb to 'monero' in shared storage
* min ringsize 7
* remove boost_locale and zmq from build - don't need them for wallet_api
* splits for all archs
* throw IndexOutOfBounds in case the TX is empty
* donate, you ungrateful bastards! (removed donations to make google happy)
This commit is contained in:
m2049r 2018-03-29 22:35:31 +02:00 committed by GitHub
parent b1f530e64a
commit a9092497b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
164 changed files with 943 additions and 548 deletions

View File

@ -60,9 +60,13 @@ set_target_properties(boost_wserialization PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_wserialization.a) ${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_wserialization.a)
############# #############
# Monero set(libs_to_merge wallet cryptonote_core cryptonote_basic mnemonics common cncrypto ringct) # Monero
############# #############
add_library(wallet_api STATIC IMPORTED)
set_target_properties(wallet_api PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libwallet_api.a)
add_library(wallet STATIC IMPORTED) add_library(wallet STATIC IMPORTED)
set_target_properties(wallet PROPERTIES IMPORTED_LOCATION set_target_properties(wallet PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libwallet.a) ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libwallet.a)
@ -91,11 +95,9 @@ add_library(ringct STATIC IMPORTED)
set_target_properties(ringct PROPERTIES IMPORTED_LOCATION set_target_properties(ringct PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libringct.a) ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libringct.a)
##### add_library(ringct_basic STATIC IMPORTED)
set_target_properties(ringct_basic PROPERTIES IMPORTED_LOCATION
add_library(p2p STATIC IMPORTED) ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libringct_basic.a)
set_target_properties(p2p PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libp2p.a)
add_library(blockchain_db STATIC IMPORTED) add_library(blockchain_db STATIC IMPORTED)
set_target_properties(blockchain_db PROPERTIES IMPORTED_LOCATION set_target_properties(blockchain_db PROPERTIES IMPORTED_LOCATION
@ -113,7 +115,6 @@ add_library(unbound STATIC IMPORTED)
set_target_properties(unbound PROPERTIES IMPORTED_LOCATION set_target_properties(unbound PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libunbound.a) ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libunbound.a)
####
add_library(epee STATIC IMPORTED) add_library(epee STATIC IMPORTED)
set_target_properties(epee PROPERTIES IMPORTED_LOCATION set_target_properties(epee PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libepee.a) ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libepee.a)
@ -122,9 +123,21 @@ add_library(blocks STATIC IMPORTED)
set_target_properties(blocks PROPERTIES IMPORTED_LOCATION set_target_properties(blocks PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libblocks.a) ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libblocks.a)
add_library(miniupnpc STATIC IMPORTED) add_library(checkpoints STATIC IMPORTED)
set_target_properties(miniupnpc PROPERTIES IMPORTED_LOCATION set_target_properties(checkpoints PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libminiupnpc.a) ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcheckpoints.a)
add_library(device STATIC IMPORTED)
set_target_properties(device PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libdevice.a)
add_library(multisig STATIC IMPORTED)
set_target_properties(multisig PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libmultisig.a)
add_library(version STATIC IMPORTED)
set_target_properties(version PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libversion.a)
############# #############
# System # System
@ -137,23 +150,26 @@ include_directories( ${EXTERNAL_LIBS_DIR}/monero/include )
message(STATUS EXTERNAL_LIBS_DIR : ${EXTERNAL_LIBS_DIR}) message(STATUS EXTERNAL_LIBS_DIR : ${EXTERNAL_LIBS_DIR})
target_link_libraries( monerujo target_link_libraries( monerujo
wallet_api
wallet wallet
cryptonote_core cryptonote_core
cryptonote_basic cryptonote_basic
mnemonics mnemonics
ringct ringct
ringct_basic
common common
cncrypto cncrypto
blockchain_db blockchain_db
lmdb lmdb
easylogging easylogging
unbound unbound
p2p
epee epee
blocks blocks
miniupnpc checkpoints
device
multisig
version
boost_chrono boost_chrono
boost_date_time boost_date_time

View File

@ -8,8 +8,8 @@ android {
applicationId "com.m2049r.xmrwallet" applicationId "com.m2049r.xmrwallet"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 25 targetSdkVersion 25
versionCode 74 versionCode 82
versionName "1.3.14 'Satoshis Dream'" versionName "1.4.2 'Monero Spedner'"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild { externalNativeBuild {
cmake { cmake {
@ -42,7 +42,7 @@ android {
enable true enable true
reset() reset()
include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
universalApk false universalApk true
} }
} }
@ -104,5 +104,7 @@ dependencyVerification {
'com.google.zxing:core:bba7724e02a997cec38213af77133ee8e24b0d5cf5fa7ecbc16a4fa93f11ee0d', 'com.google.zxing:core:bba7724e02a997cec38213af77133ee8e24b0d5cf5fa7ecbc16a4fa93f11ee0d',
'com.squareup.okio:okio:734269c3ebc5090e3b23566db558f421f0b4027277c79ad5d176b8ec168bb850', 'com.squareup.okio:okio:734269c3ebc5090e3b23566db558f421f0b4027277c79ad5d176b8ec168bb850',
'com.squareup.okhttp3:okhttp:7265adbd6f028aade307f58569d814835cd02bc9beffb70c25f72c9de50d61c4', 'com.squareup.okhttp3:okhttp:7265adbd6f028aade307f58569d814835cd02bc9beffb70c25f72c9de50d61c4',
'com.jakewharton.timber:timber:35c22867f2673132e97e17857d36bb2fc25f5790f0425406833ed0254d62fc66',
'com.nulab-inc:zxcvbn:18d7862a6abd2705defec478d77dedadf8f3bb7cf811df22995494f05485785f',
] ]
} }

View File

@ -253,17 +253,18 @@ JNIEXPORT jlong JNICALL
Java_com_m2049r_xmrwallet_model_WalletManager_createWalletJ(JNIEnv *env, jobject instance, Java_com_m2049r_xmrwallet_model_WalletManager_createWalletJ(JNIEnv *env, jobject instance,
jstring path, jstring password, jstring path, jstring password,
jstring language, jstring language,
jboolean isTestNet) { jint networkType) {
const char *_path = env->GetStringUTFChars(path, NULL); const char *_path = env->GetStringUTFChars(path, NULL);
const char *_password = env->GetStringUTFChars(password, NULL); const char *_password = env->GetStringUTFChars(password, NULL);
const char *_language = env->GetStringUTFChars(language, NULL); const char *_language = env->GetStringUTFChars(language, NULL);
Monero::NetworkType _networkType = static_cast<Monero::NetworkType>(networkType);
Bitmonero::Wallet *wallet = Bitmonero::Wallet *wallet =
Bitmonero::WalletManagerFactory::getWalletManager()->createWallet( Bitmonero::WalletManagerFactory::getWalletManager()->createWallet(
std::string(_path), std::string(_path),
std::string(_password), std::string(_password),
std::string(_language), std::string(_language),
isTestNet); _networkType);
env->ReleaseStringUTFChars(path, _path); env->ReleaseStringUTFChars(path, _path);
env->ReleaseStringUTFChars(password, _password); env->ReleaseStringUTFChars(password, _password);
@ -274,15 +275,16 @@ Java_com_m2049r_xmrwallet_model_WalletManager_createWalletJ(JNIEnv *env, jobject
JNIEXPORT jlong JNICALL JNIEXPORT jlong JNICALL
Java_com_m2049r_xmrwallet_model_WalletManager_openWalletJ(JNIEnv *env, jobject instance, Java_com_m2049r_xmrwallet_model_WalletManager_openWalletJ(JNIEnv *env, jobject instance,
jstring path, jstring password, jstring path, jstring password,
jboolean isTestNet) { jint networkType) {
const char *_path = env->GetStringUTFChars(path, NULL); const char *_path = env->GetStringUTFChars(path, NULL);
const char *_password = env->GetStringUTFChars(password, NULL); const char *_password = env->GetStringUTFChars(password, NULL);
Monero::NetworkType _networkType = static_cast<Monero::NetworkType>(networkType);
Bitmonero::Wallet *wallet = Bitmonero::Wallet *wallet =
Bitmonero::WalletManagerFactory::getWalletManager()->openWallet( Bitmonero::WalletManagerFactory::getWalletManager()->openWallet(
std::string(_path), std::string(_path),
std::string(_password), std::string(_password),
isTestNet); _networkType);
env->ReleaseStringUTFChars(path, _path); env->ReleaseStringUTFChars(path, _path);
env->ReleaseStringUTFChars(password, _password); env->ReleaseStringUTFChars(password, _password);
@ -293,19 +295,20 @@ JNIEXPORT jlong JNICALL
Java_com_m2049r_xmrwallet_model_WalletManager_recoveryWalletJ(JNIEnv *env, jobject instance, Java_com_m2049r_xmrwallet_model_WalletManager_recoveryWalletJ(JNIEnv *env, jobject instance,
jstring path, jstring password, jstring path, jstring password,
jstring mnemonic, jstring mnemonic,
jboolean isTestNet, jint networkType,
jlong restoreHeight) { jlong restoreHeight) {
const char *_path = env->GetStringUTFChars(path, NULL); const char *_path = env->GetStringUTFChars(path, NULL);
const char *_password = env->GetStringUTFChars(password, NULL); const char *_password = env->GetStringUTFChars(password, NULL);
const char *_mnemonic = env->GetStringUTFChars(mnemonic, NULL); const char *_mnemonic = env->GetStringUTFChars(mnemonic, NULL);
Monero::NetworkType _networkType = static_cast<Monero::NetworkType>(networkType);
Bitmonero::Wallet *wallet = Bitmonero::Wallet *wallet =
Bitmonero::WalletManagerFactory::getWalletManager()->recoveryWallet( Bitmonero::WalletManagerFactory::getWalletManager()->recoveryWallet(
std::string(_path), std::string(_path),
std::string(_password), std::string(_password),
std::string(_mnemonic), std::string(_mnemonic),
isTestNet, _networkType,
restoreHeight); (uint64_t) restoreHeight);
env->ReleaseStringUTFChars(path, _path); env->ReleaseStringUTFChars(path, _path);
env->ReleaseStringUTFChars(password, _password); env->ReleaseStringUTFChars(password, _password);
@ -314,10 +317,10 @@ Java_com_m2049r_xmrwallet_model_WalletManager_recoveryWalletJ(JNIEnv *env, jobje
} }
JNIEXPORT jlong JNICALL JNIEXPORT jlong JNICALL
Java_com_m2049r_xmrwallet_model_WalletManager_createWalletWithKeysJ(JNIEnv *env, jobject instance, Java_com_m2049r_xmrwallet_model_WalletManager_createWalletFromKeysJ(JNIEnv *env, jobject instance,
jstring path, jstring password, jstring path, jstring password,
jstring language, jstring language,
jboolean isTestNet, jint networkType,
jlong restoreHeight, jlong restoreHeight,
jstring addressString, jstring addressString,
jstring viewKeyString, jstring viewKeyString,
@ -325,17 +328,18 @@ Java_com_m2049r_xmrwallet_model_WalletManager_createWalletWithKeysJ(JNIEnv *env,
const char *_path = env->GetStringUTFChars(path, NULL); const char *_path = env->GetStringUTFChars(path, NULL);
const char *_password = env->GetStringUTFChars(password, NULL); const char *_password = env->GetStringUTFChars(password, NULL);
const char *_language = env->GetStringUTFChars(language, NULL); const char *_language = env->GetStringUTFChars(language, NULL);
Monero::NetworkType _networkType = static_cast<Monero::NetworkType>(networkType);
const char *_addressString = env->GetStringUTFChars(addressString, NULL); const char *_addressString = env->GetStringUTFChars(addressString, NULL);
const char *_viewKeyString = env->GetStringUTFChars(viewKeyString, NULL); const char *_viewKeyString = env->GetStringUTFChars(viewKeyString, NULL);
const char *_spendKeyString = env->GetStringUTFChars(spendKeyString, NULL); const char *_spendKeyString = env->GetStringUTFChars(spendKeyString, NULL);
Bitmonero::Wallet *wallet = Bitmonero::Wallet *wallet =
Bitmonero::WalletManagerFactory::getWalletManager()->createWalletWithKeys( Bitmonero::WalletManagerFactory::getWalletManager()->createWalletFromKeys(
std::string(_path), std::string(_path),
std::string(_password), std::string(_password),
std::string(_language), std::string(_language),
isTestNet, _networkType,
restoreHeight, (uint64_t) restoreHeight,
std::string(_addressString), std::string(_addressString),
std::string(_viewKeyString), std::string(_viewKeyString),
std::string(_spendKeyString)); std::string(_spendKeyString));
@ -356,7 +360,7 @@ Java_com_m2049r_xmrwallet_model_WalletManager_walletExists(JNIEnv *env, jobject
bool exists = bool exists =
Bitmonero::WalletManagerFactory::getWalletManager()->walletExists(std::string(_path)); Bitmonero::WalletManagerFactory::getWalletManager()->walletExists(std::string(_path));
env->ReleaseStringUTFChars(path, _path); env->ReleaseStringUTFChars(path, _path);
return exists; return static_cast<jboolean>(exists);
} }
JNIEXPORT jboolean JNICALL JNIEXPORT jboolean JNICALL
@ -371,7 +375,7 @@ Java_com_m2049r_xmrwallet_model_WalletManager_verifyWalletPassword(JNIEnv *env,
std::string(_keys_file_name), std::string(_password), watch_only); std::string(_keys_file_name), std::string(_password), watch_only);
env->ReleaseStringUTFChars(keys_file_name, _keys_file_name); env->ReleaseStringUTFChars(keys_file_name, _keys_file_name);
env->ReleaseStringUTFChars(password, _password); env->ReleaseStringUTFChars(password, _password);
return passwordOk; return static_cast<jboolean>(passwordOk);
} }
@ -440,7 +444,7 @@ Java_com_m2049r_xmrwallet_model_WalletManager_getBlockTarget(JNIEnv *env, jobjec
JNIEXPORT jboolean JNICALL JNIEXPORT jboolean JNICALL
Java_com_m2049r_xmrwallet_model_WalletManager_isMining(JNIEnv *env, jobject instance) { Java_com_m2049r_xmrwallet_model_WalletManager_isMining(JNIEnv *env, jobject instance) {
return Bitmonero::WalletManagerFactory::getWalletManager()->isMining(); return static_cast<jboolean>(Bitmonero::WalletManagerFactory::getWalletManager()->isMining());
} }
JNIEXPORT jboolean JNICALL JNIEXPORT jboolean JNICALL
@ -454,12 +458,12 @@ Java_com_m2049r_xmrwallet_model_WalletManager_startMining(JNIEnv *env, jobject i
background_mining, background_mining,
ignore_battery); ignore_battery);
env->ReleaseStringUTFChars(address, _address); env->ReleaseStringUTFChars(address, _address);
return success; return static_cast<jboolean>(success);
} }
JNIEXPORT jboolean JNICALL JNIEXPORT jboolean JNICALL
Java_com_m2049r_xmrwallet_model_WalletManager_stopMining(JNIEnv *env, jobject instance) { Java_com_m2049r_xmrwallet_model_WalletManager_stopMining(JNIEnv *env, jobject instance) {
return Bitmonero::WalletManagerFactory::getWalletManager()->stopMining(); return static_cast<jboolean>(Bitmonero::WalletManagerFactory::getWalletManager()->stopMining());
} }
JNIEXPORT jstring JNICALL JNIEXPORT jstring JNICALL
@ -493,7 +497,7 @@ Java_com_m2049r_xmrwallet_model_WalletManager_closeJ(JNIEnv *env, jobject instan
} }
} }
LOGD("wallet closed"); LOGD("wallet closed");
return closeSuccess; return static_cast<jboolean>(closeSuccess);
} }
@ -543,7 +547,7 @@ Java_com_m2049r_xmrwallet_model_Wallet_setPassword(JNIEnv *env, jobject instance
Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance); Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance);
bool success = wallet->setPassword(std::string(_password)); bool success = wallet->setPassword(std::string(_password));
env->ReleaseStringUTFChars(password, _password); env->ReleaseStringUTFChars(password, _password);
return success; return static_cast<jboolean>(success);
} }
JNIEXPORT jstring JNICALL JNIEXPORT jstring JNICALL
@ -558,10 +562,10 @@ Java_com_m2049r_xmrwallet_model_Wallet_getPath(JNIEnv *env, jobject instance) {
return env->NewStringUTF(wallet->path().c_str()); return env->NewStringUTF(wallet->path().c_str());
} }
JNIEXPORT jboolean JNICALL JNIEXPORT jint JNICALL
Java_com_m2049r_xmrwallet_model_Wallet_isTestNet(JNIEnv *env, jobject instance) { Java_com_m2049r_xmrwallet_model_Wallet_nettype(JNIEnv *env, jobject instance) {
Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance); Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance);
return wallet->testnet(); return wallet->nettype();
} }
//TODO virtual void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const = 0; //TODO virtual void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const = 0;
@ -599,7 +603,7 @@ Java_com_m2049r_xmrwallet_model_Wallet_store(JNIEnv *env, jobject instance,
LOGE("store() %s", wallet->errorString().c_str()); LOGE("store() %s", wallet->errorString().c_str());
} }
env->ReleaseStringUTFChars(path, _path); env->ReleaseStringUTFChars(path, _path);
return success; return static_cast<jboolean>(success);
} }
JNIEXPORT jstring JNICALL JNIEXPORT jstring JNICALL
@ -619,12 +623,13 @@ Java_com_m2049r_xmrwallet_model_Wallet_initJ(JNIEnv *env, jobject instance,
const char *_daemon_username = env->GetStringUTFChars(daemon_username, NULL); const char *_daemon_username = env->GetStringUTFChars(daemon_username, NULL);
const char *_daemon_password = env->GetStringUTFChars(daemon_password, NULL); const char *_daemon_password = env->GetStringUTFChars(daemon_password, NULL);
Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance); Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance);
bool status = wallet->init(_daemon_address, upper_transaction_size_limit, _daemon_username, bool status = wallet->init(_daemon_address, (uint64_t) upper_transaction_size_limit,
_daemon_username,
_daemon_password); _daemon_password);
env->ReleaseStringUTFChars(daemon_address, _daemon_address); env->ReleaseStringUTFChars(daemon_address, _daemon_address);
env->ReleaseStringUTFChars(daemon_username, _daemon_username); env->ReleaseStringUTFChars(daemon_username, _daemon_username);
env->ReleaseStringUTFChars(daemon_password, _daemon_password); env->ReleaseStringUTFChars(daemon_password, _daemon_password);
return status; return static_cast<jboolean>(status);
} }
// virtual bool createWatchOnly(const std::string &path, const std::string &password, const std::string &language) const = 0; // virtual bool createWatchOnly(const std::string &path, const std::string &password, const std::string &language) const = 0;
@ -655,7 +660,7 @@ Java_com_m2049r_xmrwallet_model_Wallet_getUnlockedBalance(JNIEnv *env, jobject i
JNIEXPORT jboolean JNICALL JNIEXPORT jboolean JNICALL
Java_com_m2049r_xmrwallet_model_Wallet_isWatchOnly(JNIEnv *env, jobject instance) { Java_com_m2049r_xmrwallet_model_Wallet_isWatchOnly(JNIEnv *env, jobject instance) {
Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance); Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance);
return wallet->watchOnly(); return static_cast<jboolean>(wallet->watchOnly());
} }
JNIEXPORT jlong JNICALL JNIEXPORT jlong JNICALL
@ -687,7 +692,7 @@ Java_com_m2049r_xmrwallet_model_Wallet_getDaemonBlockChainTargetHeight(JNIEnv *e
JNIEXPORT jboolean JNICALL JNIEXPORT jboolean JNICALL
Java_com_m2049r_xmrwallet_model_Wallet_isSynchronized(JNIEnv *env, jobject instance) { Java_com_m2049r_xmrwallet_model_Wallet_isSynchronized(JNIEnv *env, jobject instance) {
Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance); Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance);
return wallet->synchronized(); return static_cast<jboolean>(wallet->synchronized());
} }
JNIEXPORT jstring JNICALL JNIEXPORT jstring JNICALL
@ -722,16 +727,17 @@ Java_com_m2049r_xmrwallet_model_Wallet_isPaymentIdValid(JNIEnv *env, jobject cla
const char *_payment_id = env->GetStringUTFChars(payment_id, NULL); const char *_payment_id = env->GetStringUTFChars(payment_id, NULL);
bool isValid = Bitmonero::Wallet::paymentIdValid(_payment_id); bool isValid = Bitmonero::Wallet::paymentIdValid(_payment_id);
env->ReleaseStringUTFChars(payment_id, _payment_id); env->ReleaseStringUTFChars(payment_id, _payment_id);
return isValid; return static_cast<jboolean>(isValid);
} }
JNIEXPORT jboolean JNICALL JNIEXPORT jboolean JNICALL
Java_com_m2049r_xmrwallet_model_Wallet_isAddressValid(JNIEnv *env, jobject clazz, Java_com_m2049r_xmrwallet_model_Wallet_isAddressValid(JNIEnv *env, jobject clazz,
jstring address, jboolean isTestNet) { jstring address, jint networkType) {
const char *_address = env->GetStringUTFChars(address, NULL); const char *_address = env->GetStringUTFChars(address, NULL);
bool isValid = Bitmonero::Wallet::addressValid(_address, isTestNet); Monero::NetworkType _networkType = static_cast<Monero::NetworkType>(networkType);
bool isValid = Bitmonero::Wallet::addressValid(_address, _networkType);
env->ReleaseStringUTFChars(address, _address); env->ReleaseStringUTFChars(address, _address);
return isValid; return static_cast<jboolean>(isValid);
} }
//TODO static static bool keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, bool testnet, std::string &error); //TODO static static bool keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, bool testnet, std::string &error);
@ -739,9 +745,10 @@ Java_com_m2049r_xmrwallet_model_Wallet_isAddressValid(JNIEnv *env, jobject clazz
JNIEXPORT jstring JNICALL JNIEXPORT jstring JNICALL
Java_com_m2049r_xmrwallet_model_Wallet_getPaymentIdFromAddress(JNIEnv *env, jobject clazz, Java_com_m2049r_xmrwallet_model_Wallet_getPaymentIdFromAddress(JNIEnv *env, jobject clazz,
jstring address, jstring address,
jboolean isTestNet) { jint networkType) {
Monero::NetworkType _networkType = static_cast<Monero::NetworkType>(networkType);
const char *_address = env->GetStringUTFChars(address, NULL); const char *_address = env->GetStringUTFChars(address, NULL);
std::string payment_id = Bitmonero::Wallet::paymentIdFromAddress(_address, isTestNet); std::string payment_id = Bitmonero::Wallet::paymentIdFromAddress(_address, _networkType);
env->ReleaseStringUTFChars(address, _address); env->ReleaseStringUTFChars(address, _address);
return env->NewStringUTF(payment_id.c_str()); return env->NewStringUTF(payment_id.c_str());
} }
@ -766,7 +773,7 @@ Java_com_m2049r_xmrwallet_model_Wallet_pauseRefresh(JNIEnv *env, jobject instanc
JNIEXPORT jboolean JNICALL JNIEXPORT jboolean JNICALL
Java_com_m2049r_xmrwallet_model_Wallet_refresh(JNIEnv *env, jobject instance) { Java_com_m2049r_xmrwallet_model_Wallet_refresh(JNIEnv *env, jobject instance) {
Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance); Bitmonero::Wallet *wallet = getHandle<Bitmonero::Wallet>(env, instance);
return wallet->refresh(); return static_cast<jboolean>(wallet->refresh());
} }
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
@ -880,7 +887,7 @@ Java_com_m2049r_xmrwallet_model_Wallet_setUserNote(JNIEnv *env, jobject instance
env->ReleaseStringUTFChars(txid, _txid); env->ReleaseStringUTFChars(txid, _txid);
env->ReleaseStringUTFChars(note, _note); env->ReleaseStringUTFChars(note, _note);
return success; return static_cast<jboolean>(success);
} }
JNIEXPORT jstring JNICALL JNIEXPORT jstring JNICALL
@ -1029,7 +1036,7 @@ Java_com_m2049r_xmrwallet_model_PendingTransaction_commit(JNIEnv *env, jobject i
bool success = tx->commit(_filename, overwrite); bool success = tx->commit(_filename, overwrite);
env->ReleaseStringUTFChars(filename, _filename); env->ReleaseStringUTFChars(filename, _filename);
return success; return static_cast<jboolean>(success);
} }
@ -1051,10 +1058,13 @@ Java_com_m2049r_xmrwallet_model_PendingTransaction_getFee(JNIEnv *env, jobject i
// TODO this returns a vector of strings - deal with this later - for now return first one // TODO this returns a vector of strings - deal with this later - for now return first one
JNIEXPORT jstring JNICALL JNIEXPORT jstring JNICALL
Java_com_m2049r_xmrwallet_model_PendingTransaction_getFirstTxId(JNIEnv *env, jobject instance) { Java_com_m2049r_xmrwallet_model_PendingTransaction_getFirstTxIdJ(JNIEnv *env, jobject instance) {
Bitmonero::PendingTransaction *tx = getHandle<Bitmonero::PendingTransaction>(env, instance); Bitmonero::PendingTransaction *tx = getHandle<Bitmonero::PendingTransaction>(env, instance);
std::vector<std::string> txids = tx->txid(); std::vector<std::string> txids = tx->txid();
return env->NewStringUTF(txids.front().c_str()); if (!txids.empty())
return env->NewStringUTF(txids.front().c_str());
else
return nullptr;
} }

View File

@ -386,7 +386,7 @@ public class GenerateFragment extends Fragment {
private boolean checkAddress() { private boolean checkAddress() {
String address = etWalletAddress.getEditText().getText().toString(); String address = etWalletAddress.getEditText().getText().toString();
boolean ok = Wallet.isAddressValid(address, WalletManager.getInstance().isTestNet()); boolean ok = Wallet.isAddressValid(address);
if (!ok) { if (!ok) {
etWalletAddress.setError(getString(R.string.generate_check_address)); etWalletAddress.setError(getString(R.string.generate_check_address));
} else { } else {

View File

@ -34,6 +34,7 @@ import android.widget.ScrollView;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import com.m2049r.xmrwallet.model.NetworkType;
import com.m2049r.xmrwallet.widget.Toolbar; import com.m2049r.xmrwallet.widget.Toolbar;
import com.m2049r.xmrwallet.model.Wallet; import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager; import com.m2049r.xmrwallet.model.WalletManager;
@ -81,7 +82,7 @@ public class GenerateReviewFragment extends Fragment {
bAccept = (Button) view.findViewById(R.id.bAccept); bAccept = (Button) view.findViewById(R.id.bAccept);
boolean testnet = WalletManager.getInstance().isTestNet(); boolean testnet = WalletManager.getInstance().getNetworkType() != NetworkType.NetworkType_Mainnet;
tvWalletMnemonic.setTextIsSelectable(testnet); tvWalletMnemonic.setTextIsSelectable(testnet);
tvWalletSpendKey.setTextIsSelectable(testnet); tvWalletSpendKey.setTextIsSelectable(testnet);
@ -185,6 +186,7 @@ public class GenerateReviewFragment extends Fragment {
name = wallet.getName(); name = wallet.getName();
status = wallet.getStatus(); status = wallet.getStatus();
if (status != Wallet.Status.Status_Ok) { if (status != Wallet.Status.Status_Ok) {
Timber.e(wallet.getErrorString());
if (closeWallet) wallet.close(); if (closeWallet) wallet.close();
return false; return false;
} }

View File

@ -45,10 +45,12 @@ import android.widget.EditText;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import com.m2049r.xmrwallet.data.WalletNode;
import com.m2049r.xmrwallet.dialog.AboutFragment; import com.m2049r.xmrwallet.dialog.AboutFragment;
import com.m2049r.xmrwallet.dialog.DonationFragment; import com.m2049r.xmrwallet.dialog.CreditsFragment;
import com.m2049r.xmrwallet.dialog.HelpFragment; import com.m2049r.xmrwallet.dialog.HelpFragment;
import com.m2049r.xmrwallet.dialog.PrivacyFragment; import com.m2049r.xmrwallet.dialog.PrivacyFragment;
import com.m2049r.xmrwallet.model.NetworkType;
import com.m2049r.xmrwallet.model.Wallet; import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager; import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.service.WalletService; import com.m2049r.xmrwallet.service.WalletService;
@ -60,7 +62,6 @@ import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket; import java.net.Socket;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
@ -120,8 +121,8 @@ public class LoginActivity extends SecureActivity
case Toolbar.BUTTON_CLOSE: case Toolbar.BUTTON_CLOSE:
finish(); finish();
break; break;
case Toolbar.BUTTON_DONATE: case Toolbar.BUTTON_CREDITS:
DonationFragment.display(getSupportFragmentManager()); CreditsFragment.display(getSupportFragmentManager());
break; break;
case Toolbar.BUTTON_NONE: case Toolbar.BUTTON_NONE:
default: default:
@ -147,14 +148,14 @@ public class LoginActivity extends SecureActivity
} }
@Override @Override
public boolean onWalletSelected(String walletName, String daemon, boolean testnet) { public boolean onWalletSelected(String walletName, String daemon) {
if (daemon.length() == 0) { if (daemon.length() == 0) {
Toast.makeText(this, getString(R.string.prompt_daemon_missing), Toast.LENGTH_SHORT).show(); Toast.makeText(this, getString(R.string.prompt_daemon_missing), Toast.LENGTH_SHORT).show();
return false; return false;
} }
if (checkServiceRunning()) return false; if (checkServiceRunning()) return false;
try { try {
WalletNode aWalletNode = new WalletNode(walletName, daemon, testnet); WalletNode aWalletNode = new WalletNode(walletName, daemon, WalletManager.getInstance().getNetworkType());
new AsyncOpenWallet().execute(aWalletNode); new AsyncOpenWallet().execute(aWalletNode);
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
Timber.e(ex.getLocalizedMessage()); Timber.e(ex.getLocalizedMessage());
@ -165,9 +166,8 @@ public class LoginActivity extends SecureActivity
} }
@Override @Override
public void onWalletDetails(final String walletName, boolean testnet) { public void onWalletDetails(final String walletName) {
setNet(testnet); Timber.d("details for wallet .%s.", walletName);
Timber.d("details for wallet ." + walletName + ".");
if (checkServiceRunning()) return; if (checkServiceRunning()) return;
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
@Override @Override
@ -203,9 +203,8 @@ public class LoginActivity extends SecureActivity
} }
@Override @Override
public void onWalletReceive(String walletName, boolean testnet) { public void onWalletReceive(String walletName) {
setNet(testnet); Timber.d("receive for wallet .%s.", walletName);
Timber.d("receive for wallet ." + walletName + ".");
if (checkServiceRunning()) return; if (checkServiceRunning()) return;
final File walletFile = Helper.getWalletFile(this, walletName); final File walletFile = Helper.getWalletFile(this, walletName);
if (WalletManager.getInstance().walletExists(walletFile)) { if (WalletManager.getInstance().walletExists(walletFile)) {
@ -439,8 +438,7 @@ public class LoginActivity extends SecureActivity
} }
@Override @Override
public void onAddWallet(boolean testnet, String type) { public void onAddWallet(String type) {
setNet(testnet);
if (checkServiceRunning()) return; if (checkServiceRunning()) return;
startGenerateFragment(type); startGenerateFragment(type);
} }
@ -537,7 +535,7 @@ public class LoginActivity extends SecureActivity
} }
private boolean checkWalletPassword(String walletName, String password) { private boolean checkWalletPassword(String walletName, String password) {
String walletPath = new File(Helper.getStorageRoot(getApplicationContext()), String walletPath = new File(Helper.getWalletRoot(getApplicationContext()),
walletName + ".keys").getAbsolutePath(); walletName + ".keys").getAbsolutePath();
// only test view key // only test view key
return WalletManager.getInstance().verifyWalletPassword(walletPath, password, true); return WalletManager.getInstance().verifyWalletPassword(walletPath, password, true);
@ -566,20 +564,30 @@ public class LoginActivity extends SecureActivity
@Override @Override
public File getStorageRoot() { public File getStorageRoot() {
return Helper.getStorageRoot(getApplicationContext()); return Helper.getWalletRoot(getApplicationContext());
} }
//////////////////////////////////////// ////////////////////////////////////////
//////////////////////////////////////// ////////////////////////////////////////
@Override @Override
public void showNet(boolean testnet) { public void showNet() {
if (testnet) { switch (WalletManager.getInstance().getNetworkType()) {
toolbar.setBackgroundResource(R.color.colorPrimaryDark); case NetworkType_Mainnet:
} else { toolbar.setSubtitle(getString(R.string.connect_mainnet));
toolbar.setBackgroundResource(R.drawable.backgound_toolbar_mainnet); toolbar.setBackgroundResource(R.drawable.backgound_toolbar_mainnet);
break;
case NetworkType_Testnet:
toolbar.setSubtitle(getString(R.string.connect_testnet));
toolbar.setBackgroundResource(R.color.colorPrimaryDark);
break;
case NetworkType_Stagenet:
toolbar.setSubtitle(getString(R.string.connect_stagenet));
toolbar.setBackgroundResource(R.color.colorPrimaryDark);
break;
default:
throw new IllegalStateException("NetworkType unknown: " + WalletManager.getInstance().getNetworkType());
} }
toolbar.setSubtitle(getString(testnet ? R.string.connect_testnet : R.string.connect_mainnet));
} }
@Override @Override
@ -636,7 +644,7 @@ public class LoginActivity extends SecureActivity
private class MyProgressDialog extends ProgressDialog { private class MyProgressDialog extends ProgressDialog {
Activity activity; Activity activity;
public MyProgressDialog(Activity activity, int msgId) { MyProgressDialog(Activity activity, int msgId) {
super(activity); super(activity);
this.activity = activity; this.activity = activity;
setCancelable(false); setCancelable(false);
@ -735,6 +743,7 @@ public class LoginActivity extends SecureActivity
} }
void startLoginFragment() { void startLoginFragment() {
Helper.setMoneroHome(this);
Fragment fragment = new LoginFragment(); Fragment fragment = new LoginFragment();
getSupportFragmentManager().beginTransaction() getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, fragment).commit(); .add(R.id.fragment_container, fragment).commit();
@ -784,8 +793,8 @@ public class LoginActivity extends SecureActivity
File newWalletFile; File newWalletFile;
public AsyncCreateWallet(final String name, final String password, AsyncCreateWallet(final String name, final String password,
final WalletCreator walletCreator) { final WalletCreator walletCreator) {
super(); super();
this.walletName = name; this.walletName = name;
this.walletPassword = password; this.walletPassword = password;
@ -1081,6 +1090,7 @@ public class LoginActivity extends SecureActivity
getSupportFragmentManager().findFragmentById(R.id.fragment_container); getSupportFragmentManager().findFragmentById(R.id.fragment_container);
item.setChecked(loginFragment.onTestnetMenuItem()); item.setChecked(loginFragment.onTestnetMenuItem());
} catch (ClassCastException ex) { } catch (ClassCastException ex) {
// never mind then
} }
return true; return true;
default: default:
@ -1088,59 +1098,8 @@ public class LoginActivity extends SecureActivity
} }
} }
private void setNet(boolean testnet) { public void setNetworkType(NetworkType networkType) {
WalletManager.getInstance().setDaemon("", testnet, "", ""); WalletManager.getInstance().setNetworkType(networkType);
}
static class WalletNode {
String name = null;
String host = "";
int port = 28081;
String user = "";
String password = "";
boolean isTestnet;
WalletNode(String walletName, String daemon, boolean isTestnet) {
if ((daemon == null) || daemon.isEmpty()) return;
this.name = walletName;
String daemonAddress;
String a[] = daemon.split("@");
if (a.length == 1) { // no credentials
daemonAddress = a[0];
} else if (a.length == 2) { // credentials
String userPassword[] = a[0].split(":");
if (userPassword.length != 2)
throw new IllegalArgumentException("User:Password invalid");
user = userPassword[0];
if (!user.isEmpty()) password = userPassword[1];
daemonAddress = a[1];
} else {
throw new IllegalArgumentException("Too many @");
}
String da[] = daemonAddress.split(":");
if ((da.length > 2) || (da.length < 1))
throw new IllegalArgumentException("Too many ':' or too few");
host = da[0];
if (da.length == 2) {
try {
port = Integer.parseInt(da[1]);
} catch (NumberFormatException ex) {
throw new IllegalArgumentException("Port not numeric");
}
} else {
port = (isTestnet ? 28081 : 18081);
}
this.isTestnet = isTestnet;
}
String getAddress() {
return host + ":" + port;
}
boolean isValid() {
return !host.isEmpty();
}
} }
private class AsyncOpenWallet extends AsyncTask<WalletNode, Void, Integer> { private class AsyncOpenWallet extends AsyncTask<WalletNode, Void, Integer> {
@ -1167,22 +1126,22 @@ public class LoginActivity extends SecureActivity
try { try {
long timeDA = new Date().getTime(); long timeDA = new Date().getTime();
SocketAddress address = new InetSocketAddress(walletNode.host, walletNode.port); SocketAddress address = walletNode.getSocketAddress();
long timeDB = new Date().getTime(); long timeDB = new Date().getTime();
Timber.d("Resolving " + walletNode.host + " took " + (timeDB - timeDA) + "ms."); Timber.d("Resolving " + walletNode.getAddress() + " took " + (timeDB - timeDA) + "ms.");
Socket socket = new Socket(); Socket socket = new Socket();
long timeA = new Date().getTime(); long timeA = new Date().getTime();
socket.connect(address, LoginActivity.DAEMON_TIMEOUT); socket.connect(address, LoginActivity.DAEMON_TIMEOUT);
socket.close(); socket.close();
long timeB = new Date().getTime(); long timeB = new Date().getTime();
long time = timeB - timeA; long time = timeB - timeA;
Timber.d("Daemon " + walletNode.host + " is " + time + "ms away."); Timber.d("Daemon " + walletNode.getAddress() + " is " + time + "ms away.");
return (time < LoginActivity.DAEMON_TIMEOUT ? OK : TIMEOUT); return (time < LoginActivity.DAEMON_TIMEOUT ? OK : TIMEOUT);
} catch (IOException ex) { } catch (IOException ex) {
Timber.d("Cannot reach daemon " + walletNode.host + "/" + walletNode.port + " because " + ex.getMessage()); Timber.d("Cannot reach daemon %s because %s", walletNode.getAddress(), ex.getMessage());
return IOEX; return IOEX;
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
Timber.d("Cannot reach daemon " + walletNode.host + "/" + walletNode.port + " because " + ex.getMessage()); Timber.d("Cannot reach daemon %s because %s", walletNode.getAddress(), ex.getMessage());
return INVALID; return INVALID;
} }
} }
@ -1196,7 +1155,7 @@ public class LoginActivity extends SecureActivity
dismissProgressDialog(); dismissProgressDialog();
switch (result) { switch (result) {
case OK: case OK:
Timber.d("selected wallet is ." + walletNode.name + "."); Timber.d("selected wallet is .%s.", walletNode.getName());
// now it's getting real, onValidateFields if wallet exists // now it's getting real, onValidateFields if wallet exists
promptAndStart(walletNode); promptAndStart(walletNode);
break; break;
@ -1214,11 +1173,10 @@ public class LoginActivity extends SecureActivity
} }
void promptAndStart(WalletNode walletNode) { void promptAndStart(WalletNode walletNode) {
File walletFile = Helper.getWalletFile(this, walletNode.name); File walletFile = Helper.getWalletFile(this, walletNode.getName());
if (WalletManager.getInstance().walletExists(walletFile)) { if (WalletManager.getInstance().walletExists(walletFile)) {
WalletManager.getInstance(). WalletManager.getInstance().setDaemon(walletNode);
setDaemon(walletNode.getAddress(), walletNode.isTestnet, walletNode.user, walletNode.password); promptPassword(walletNode.getName(), new PasswordAction() {
promptPassword(walletNode.name, new PasswordAction() {
@Override @Override
public void action(String walletName, String password) { public void action(String walletName, String password) {
startWallet(walletName, password); startWallet(walletName, password);

View File

@ -45,6 +45,7 @@ import android.widget.Toast;
import com.m2049r.xmrwallet.dialog.HelpFragment; import com.m2049r.xmrwallet.dialog.HelpFragment;
import com.m2049r.xmrwallet.layout.WalletInfoAdapter; import com.m2049r.xmrwallet.layout.WalletInfoAdapter;
import com.m2049r.xmrwallet.model.NetworkType;
import com.m2049r.xmrwallet.model.WalletManager; import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.util.Helper; import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.NodeList; import com.m2049r.xmrwallet.util.NodeList;
@ -81,11 +82,11 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
File getStorageRoot(); File getStorageRoot();
boolean onWalletSelected(String wallet, String daemon, boolean testnet); boolean onWalletSelected(String wallet, String daemon);
void onWalletDetails(String wallet, boolean testnet); void onWalletDetails(String wallet);
void onWalletReceive(String wallet, boolean testnet); void onWalletReceive(String wallet);
void onWalletRename(String name); void onWalletRename(String name);
@ -93,14 +94,16 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
void onWalletArchive(String walletName); void onWalletArchive(String walletName);
void onAddWallet(boolean testnet, String type); void onAddWallet(String type);
void showNet(boolean testnet); void showNet();
void setToolbarButton(int type); void setToolbarButton(int type);
void setTitle(String title); void setTitle(String title);
void setNetworkType(NetworkType networkType);
} }
@Override @Override
@ -126,8 +129,8 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
super.onResume(); super.onResume();
Timber.d("onResume()"); Timber.d("onResume()");
activityCallback.setTitle(null); activityCallback.setTitle(null);
activityCallback.setToolbarButton(Toolbar.BUTTON_DONATE); activityCallback.setToolbarButton(Toolbar.BUTTON_CREDITS);
activityCallback.showNet(isTestnet()); activityCallback.showNet();
} }
@Override @Override
@ -244,13 +247,13 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
// Callbacks from WalletInfoAdapter // Callbacks from WalletInfoAdapter
@Override @Override
public void onInteraction(final View view, final WalletManager.WalletInfo infoItem) { public void onInteraction(final View view, final WalletManager.WalletInfo infoItem) {
String x = isTestnet() ? "9A-" : "4-"; String addressPrefix = addressPrefix();
if (x.indexOf(infoItem.address.charAt(0)) < 0) { if (addressPrefix.indexOf(infoItem.address.charAt(0)) < 0) {
Toast.makeText(getActivity(), getString(R.string.prompt_wrong_net), Toast.LENGTH_LONG).show(); Toast.makeText(getActivity(), getString(R.string.prompt_wrong_net), Toast.LENGTH_LONG).show();
return; return;
} }
if (activityCallback.onWalletSelected(infoItem.name, getDaemon(), isTestnet())) { if (activityCallback.onWalletSelected(infoItem.name, getDaemon())) {
savePrefs(); savePrefs();
} }
} }
@ -279,11 +282,24 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
return true; return true;
} }
private String addressPrefix() {
switch (WalletManager.getInstance().getNetworkType()) {
case NetworkType_Testnet:
return "9A-";
case NetworkType_Mainnet:
return "4-";
case NetworkType_Stagenet:
return "5-";
default:
throw new IllegalStateException("Unsupported Network: " + WalletManager.getInstance().getNetworkType());
}
}
private void filterList() { private void filterList() {
displayedList.clear(); displayedList.clear();
String x = isTestnet() ? "9A" : "4"; String addressPrefix = addressPrefix();
for (WalletManager.WalletInfo s : walletList) { for (WalletManager.WalletInfo s : walletList) {
if (x.indexOf(s.address.charAt(0)) >= 0) displayedList.add(s); if (addressPrefix.indexOf(s.address.charAt(0)) >= 0) displayedList.add(s);
} }
} }
@ -313,11 +329,11 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
} }
private void showInfo(@NonNull String name) { private void showInfo(@NonNull String name) {
activityCallback.onWalletDetails(name, isTestnet()); activityCallback.onWalletDetails(name);
} }
private void showReceive(@NonNull String name) { private void showReceive(@NonNull String name) {
activityCallback.onWalletReceive(name, isTestnet()); activityCallback.onWalletReceive(name);
} }
@Override @Override
@ -329,29 +345,31 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
@Override @Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.list_menu, menu); inflater.inflate(R.menu.list_menu, menu);
menu.findItem(R.id.action_testnet).setChecked(isTestnet()); menu.findItem(R.id.action_testnet).setChecked(testnetCheckMenu);
super.onCreateOptionsMenu(menu, inflater); super.onCreateOptionsMenu(menu, inflater);
} }
private boolean testnet = BuildConfig.DEBUG; private boolean testnetCheckMenu = BuildConfig.DEBUG;
boolean isTestnet() { //boolean isTestnet() {
return testnet; // return testnet;
} //}
public boolean onTestnetMenuItem() { public boolean onTestnetMenuItem() {
boolean lastState = testnet; boolean lastState = testnetCheckMenu;
setNet(!lastState, true); // set and save setNet(!lastState, true); // set and save
return !lastState; return !lastState;
} }
public void setNet(boolean testnet, boolean save) { public void setNet(boolean testnetChecked, boolean save) {
this.testnet = testnet; this.testnetCheckMenu = testnetChecked;
activityCallback.showNet(testnet); NetworkType net = testnetChecked ? NetworkType.NetworkType_Testnet : NetworkType.NetworkType_Mainnet;
activityCallback.setNetworkType(net);
activityCallback.showNet();
if (save) { if (save) {
savePrefs(true); // use previous state as we just clicked it savePrefs(true); // use previous state as we just clicked it
} }
if (testnet) { if (testnetChecked) {
setDaemon(daemonTestNet); setDaemon(daemonTestNet);
} else { } else {
setDaemon(daemonMainNet); setDaemon(daemonMainNet);
@ -379,7 +397,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
daemonMainNet = new NodeList(sharedPref.getString(PREF_DAEMON_MAINNET, PREF_DAEMONLIST_MAINNET)); daemonMainNet = new NodeList(sharedPref.getString(PREF_DAEMON_MAINNET, PREF_DAEMONLIST_MAINNET));
daemonTestNet = new NodeList(sharedPref.getString(PREF_DAEMON_TESTNET, PREF_DAEMONLIST_TESTNET)); daemonTestNet = new NodeList(sharedPref.getString(PREF_DAEMON_TESTNET, PREF_DAEMONLIST_TESTNET));
setNet(isTestnet(), false); setNet(testnetCheckMenu, false);
showXmrtoEnabled = sharedPref.getBoolean(PREF_SHOW_XMRTO_ENABLED, true); showXmrtoEnabled = sharedPref.getBoolean(PREF_SHOW_XMRTO_ENABLED, true);
} }
@ -398,7 +416,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
void savePrefs(boolean usePreviousTestnetState) { void savePrefs(boolean usePreviousTestnetState) {
Timber.d("SAVE / %s", usePreviousTestnetState); Timber.d("SAVE / %s", usePreviousTestnetState);
// save the daemon address for the net // save the daemon address for the net
boolean testnet = isTestnet() ^ usePreviousTestnetState; boolean testnet = testnetCheckMenu ^ usePreviousTestnetState;
String daemon = getDaemon(); String daemon = getDaemon();
if (testnet) { if (testnet) {
daemonTestNet.setRecent(daemon); daemonTestNet.setRecent(daemon);
@ -484,19 +502,19 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
case R.id.fabNew: case R.id.fabNew:
fabScreen.setVisibility(View.INVISIBLE); fabScreen.setVisibility(View.INVISIBLE);
isFabOpen = false; isFabOpen = false;
activityCallback.onAddWallet(isTestnet(), GenerateFragment.TYPE_NEW); activityCallback.onAddWallet(GenerateFragment.TYPE_NEW);
break; break;
case R.id.fabView: case R.id.fabView:
animateFAB(); animateFAB();
activityCallback.onAddWallet(isTestnet(), GenerateFragment.TYPE_VIEWONLY); activityCallback.onAddWallet(GenerateFragment.TYPE_VIEWONLY);
break; break;
case R.id.fabKey: case R.id.fabKey:
animateFAB(); animateFAB();
activityCallback.onAddWallet(isTestnet(), GenerateFragment.TYPE_KEY); activityCallback.onAddWallet(GenerateFragment.TYPE_KEY);
break; break;
case R.id.fabSeed: case R.id.fabSeed:
animateFAB(); animateFAB();
activityCallback.onAddWallet(isTestnet(), GenerateFragment.TYPE_SEED); activityCallback.onAddWallet(GenerateFragment.TYPE_SEED);
break; break;
case R.id.fabScreen: case R.id.fabScreen:
animateFAB(); animateFAB();

View File

@ -300,7 +300,7 @@ public class ReceiveFragment extends Fragment {
String paymentId = etPaymentId.getEditText().getText().toString(); String paymentId = etPaymentId.getEditText().getText().toString();
String xmrAmount = evAmount.getAmount(); String xmrAmount = evAmount.getAmount();
Timber.d("%s/%s/%s", xmrAmount, paymentId, address); Timber.d("%s/%s/%s", xmrAmount, paymentId, address);
if ((xmrAmount == null) || !Wallet.isAddressValid(address, WalletManager.getInstance().isTestNet())) { if ((xmrAmount == null) || !Wallet.isAddressValid(address)) {
clearQR(); clearQR();
Timber.d("CLEARQR"); Timber.d("CLEARQR");
return; return;

View File

@ -36,7 +36,7 @@ import android.widget.Toast;
import com.m2049r.xmrwallet.data.BarcodeData; import com.m2049r.xmrwallet.data.BarcodeData;
import com.m2049r.xmrwallet.data.TxData; import com.m2049r.xmrwallet.data.TxData;
import com.m2049r.xmrwallet.dialog.DonationFragment; import com.m2049r.xmrwallet.dialog.CreditsFragment;
import com.m2049r.xmrwallet.dialog.HelpFragment; import com.m2049r.xmrwallet.dialog.HelpFragment;
import com.m2049r.xmrwallet.fragment.send.SendAddressWizardFragment; import com.m2049r.xmrwallet.fragment.send.SendAddressWizardFragment;
import com.m2049r.xmrwallet.fragment.send.SendFragment; import com.m2049r.xmrwallet.fragment.send.SendFragment;
@ -159,8 +159,8 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
case R.id.action_info: case R.id.action_info:
onWalletDetails(); onWalletDetails();
return true; return true;
case R.id.action_donate: case R.id.action_credits:
DonationFragment.display(getSupportFragmentManager()); CreditsFragment.display(getSupportFragmentManager());
return true; return true;
case R.id.action_share: case R.id.action_share:
onShareTxInfo(); onShareTxInfo();
@ -213,8 +213,8 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
case Toolbar.BUTTON_CLOSE: case Toolbar.BUTTON_CLOSE:
finish(); finish();
break; break;
case Toolbar.BUTTON_DONATE: case Toolbar.BUTTON_CREDITS:
Toast.makeText(WalletActivity.this, getString(R.string.label_donate), Toast.LENGTH_SHORT).show(); Toast.makeText(WalletActivity.this, getString(R.string.label_credits), Toast.LENGTH_SHORT).show();
case Toolbar.BUTTON_NONE: case Toolbar.BUTTON_NONE:
default: default:
Timber.e("Button " + type + "pressed - how can this be?"); Timber.e("Button " + type + "pressed - how can this be?");
@ -222,13 +222,6 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
} }
}); });
boolean testnet = WalletManager.getInstance().isTestNet();
if (testnet) {
toolbar.setBackgroundResource(R.color.colorPrimaryDark);
} else {
toolbar.setBackgroundResource(R.drawable.backgound_toolbar_mainnet);
}
Fragment walletFragment = new WalletFragment(); Fragment walletFragment = new WalletFragment();
getSupportFragmentManager().beginTransaction() getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, walletFragment, WalletFragment.class.getName()).commit(); .add(R.id.fragment_container, walletFragment, WalletFragment.class.getName()).commit();
@ -238,6 +231,23 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
Timber.d("onCreate() done."); Timber.d("onCreate() done.");
} }
public void showNet() {
switch (WalletManager.getInstance().getNetworkType()) {
case NetworkType_Mainnet:
toolbar.setBackgroundResource(R.drawable.backgound_toolbar_mainnet);
break;
case NetworkType_Testnet:
toolbar.setBackgroundResource(R.color.colorPrimaryDark);
break;
case NetworkType_Stagenet:
toolbar.setBackgroundResource(R.color.colorPrimaryDark);
break;
default:
throw new IllegalStateException("Unsupported Network: " + WalletManager.getInstance().getNetworkType());
}
}
public Wallet getWallet() { public Wallet getWallet() {
if (mBoundService == null) throw new IllegalStateException("WalletService not bound."); if (mBoundService == null) throw new IllegalStateException("WalletService not bound.");
return mBoundService.getWallet(); return mBoundService.getWallet();
@ -798,7 +808,7 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
@Override @Override
public boolean verifyWalletPassword(String password) { public boolean verifyWalletPassword(String password) {
String walletPath = new File(Helper.getStorageRoot(this), String walletPath = new File(Helper.getWalletRoot(this),
getWalletName() + ".keys").getAbsolutePath(); getWalletName() + ".keys").getAbsolutePath();
return WalletManager.getInstance().verifyWalletPassword(walletPath, password, true); return WalletManager.getInstance().verifyWalletPassword(walletPath, password, true);
} }

View File

@ -19,6 +19,8 @@ package com.m2049r.xmrwallet;
import android.app.Application; import android.app.Application;
import com.m2049r.xmrwallet.util.Helper;
import timber.log.Timber; import timber.log.Timber;
public class XmrWalletApplication extends Application { public class XmrWalletApplication extends Application {

View File

@ -18,6 +18,7 @@ package com.m2049r.xmrwallet.data;
import android.net.Uri; import android.net.Uri;
import com.m2049r.xmrwallet.model.NetworkType;
import com.m2049r.xmrwallet.model.Wallet; import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager; import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.util.BitcoinAddressValidator; import com.m2049r.xmrwallet.util.BitcoinAddressValidator;
@ -178,7 +179,7 @@ public class BarcodeData {
return null; // we have an amount but its not a number! return null; // we have an amount but its not a number!
} }
} }
if (!BitcoinAddressValidator.validate(address, WalletManager.getInstance().isTestNet())) { if (!BitcoinAddressValidator.validate(address)) {
Timber.d("address invalid"); Timber.d("address invalid");
return null; return null;
} }
@ -190,7 +191,7 @@ public class BarcodeData {
if (address == null) return null; if (address == null) return null;
if (!BitcoinAddressValidator.validate(address, WalletManager.getInstance().isTestNet())) { if (!BitcoinAddressValidator.validate(address)) {
Timber.d("address invalid"); Timber.d("address invalid");
return null; return null;
} }

View File

@ -0,0 +1,108 @@
/*
* Copyright (c) 2018 m2049r
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.m2049r.xmrwallet.data;
import com.m2049r.xmrwallet.model.NetworkType;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
public class WalletNode {
private final String name;
private final String host;
private final int port;
private final String user;
private final String password;
private final NetworkType networkType;
public WalletNode(String walletName, String daemon, NetworkType networkType) {
if ((daemon == null) || daemon.isEmpty())
throw new IllegalArgumentException("daemon is empty");
this.name = walletName;
String daemonAddress;
String a[] = daemon.split("@");
if (a.length == 1) { // no credentials
daemonAddress = a[0];
user = "";
password = "";
} else if (a.length == 2) { // credentials
String userPassword[] = a[0].split(":");
if (userPassword.length != 2)
throw new IllegalArgumentException("User:Password invalid");
user = userPassword[0];
if (!user.isEmpty()) {
password = userPassword[1];
} else {
password = "";
}
daemonAddress = a[1];
} else {
throw new IllegalArgumentException("Too many @");
}
String da[] = daemonAddress.split(":");
if ((da.length > 2) || (da.length < 1))
throw new IllegalArgumentException("Too many ':' or too few");
host = da[0];
if (da.length == 2) {
try {
port = Integer.parseInt(da[1]);
} catch (NumberFormatException ex) {
throw new IllegalArgumentException("Port not numeric");
}
} else {
switch (networkType) {
case NetworkType_Mainnet:
port = 18081;
break;
case NetworkType_Testnet:
port = 28081;
break;
case NetworkType_Stagenet:
port = 38081;
break;
default:
port = 0;
}
}
this.networkType = networkType;
}
public String getName() {
return name;
}
public String getAddress() {
return host + ":" + port;
}
public String getUsername() {
return user;
}
public String getPassword() {
return password;
}
public SocketAddress getSocketAddress() {
return new InetSocketAddress(host, port);
}
public boolean isValid() {
return !host.isEmpty();
}
}

View File

@ -28,16 +28,14 @@ import android.text.Html;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import com.m2049r.xmrwallet.R; import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.util.Helper;
public class DonationFragment extends DialogFragment { public class CreditsFragment extends DialogFragment {
static final String TAG = "DonationFragment"; static final String TAG = "DonationFragment";
public static DonationFragment newInstance() { public static CreditsFragment newInstance() {
return new DonationFragment(); return new CreditsFragment();
} }
public static void display(FragmentManager fm) { public static void display(FragmentManager fm) {
@ -47,24 +45,14 @@ public class DonationFragment extends DialogFragment {
ft.remove(prev); ft.remove(prev);
} }
DonationFragment.newInstance().show(ft, TAG); CreditsFragment.newInstance().show(ft, TAG);
} }
@Override @Override
public Dialog onCreateDialog(Bundle savedInstanceState) { public Dialog onCreateDialog(Bundle savedInstanceState) {
final View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_donation, null); final View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_credits, null);
((TextView) view.findViewById(R.id.tvCredits)).setText(Html.fromHtml(getString(R.string.donation_credits))); ((TextView) view.findViewById(R.id.tvCredits)).setText(Html.fromHtml(getString(R.string.credits_text)));
(view.findViewById(R.id.bCopyAddress)).
setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Helper.clipBoardCopy(getActivity(), getString(R.string.label_copy_address),
((TextView) view.findViewById(R.id.tvWalletAddress)).getText().toString());
Toast.makeText(getActivity(), getString(R.string.message_copy_address), Toast.LENGTH_SHORT).show();
}
});
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setView(view); builder.setView(view);

View File

@ -212,7 +212,7 @@ public class SendAddressWizardFragment extends SendWizardFragment {
private boolean checkAddressNoError() { private boolean checkAddressNoError() {
String address = etAddress.getEditText().getText().toString(); String address = etAddress.getEditText().getText().toString();
return Wallet.isAddressValid(address) return Wallet.isAddressValid(address)
|| BitcoinAddressValidator.validate(address, WalletManager.getInstance().isTestNet()); || BitcoinAddressValidator.validate(address);
} }
private boolean checkAddress() { private boolean checkAddress() {
@ -228,13 +228,13 @@ public class SendAddressWizardFragment extends SendWizardFragment {
private boolean isIntegratedAddress() { private boolean isIntegratedAddress() {
String address = etAddress.getEditText().getText().toString(); String address = etAddress.getEditText().getText().toString();
return (address.length() == INTEGRATED_ADDRESS_LENGTH) return (address.length() == INTEGRATED_ADDRESS_LENGTH)
&& Wallet.isAddressValid(address, WalletManager.getInstance().isTestNet()); && Wallet.isAddressValid(address);
} }
private boolean isBitcoinAddress() { private boolean isBitcoinAddress() {
String address = etAddress.getEditText().getText().toString(); String address = etAddress.getEditText().getText().toString();
if ((address.length() >= 27) && (address.length() <= 34)) if ((address.length() >= 27) && (address.length() <= 34))
return BitcoinAddressValidator.validate(address, WalletManager.getInstance().isTestNet()); return BitcoinAddressValidator.validate(address);
else else
return false; return false;
} }

View File

@ -54,7 +54,8 @@ public class SendSettingsWizardFragment extends SendWizardFragment {
TxData getTxData(); TxData getTxData();
} }
final static int Mixins[] = {4, 7, 12, 25}; // must match the layout XML // Mixin = Ringsize - 1
final static int Mixins[] = {6, 9, 12, 25}; // must match the layout XML / "@array/mixin"
final static PendingTransaction.Priority Priorities[] = final static PendingTransaction.Priority Priorities[] =
{PendingTransaction.Priority.Priority_Default, {PendingTransaction.Priority.Priority_Default,
PendingTransaction.Priority.Priority_Low, PendingTransaction.Priority.Priority_Low,

View File

@ -137,12 +137,7 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
ivTxType.setVisibility(View.GONE); // gives us more space for the amount ivTxType.setVisibility(View.GONE); // gives us more space for the amount
} }
long realAmount = infoItem.amount; String displayAmount = Helper.getDisplayAmount(infoItem.amount, Helper.DISPLAY_DIGITS_INFO);
if (infoItem.isPending) {
realAmount = realAmount - infoItem.fee;
}
String displayAmount = Helper.getDisplayAmount(realAmount, Helper.DISPLAY_DIGITS_INFO);
if (infoItem.direction == TransactionInfo.Direction.Direction_Out) { if (infoItem.direction == TransactionInfo.Direction.Direction_Out) {
tvAmount.setText(context.getString(R.string.tx_list_amount_negative, displayAmount)); tvAmount.setText(context.getString(R.string.tx_list_amount_negative, displayAmount));
} else { } else {

View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 2018 m2049r
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.m2049r.xmrwallet.model;
public enum NetworkType {
NetworkType_Mainnet(0),
NetworkType_Testnet(1),
NetworkType_Stagenet(2);
public static NetworkType fromInteger(int n) {
switch (n) {
case 0:
return NetworkType_Mainnet;
case 1:
return NetworkType_Testnet;
case 2:
return NetworkType_Stagenet;
}
return null;
}
public int getValue() {
return value;
}
private int value;
NetworkType(int value) {
this.value = value;
}
}

View File

@ -84,7 +84,14 @@ public class PendingTransaction {
public native long getFee(); public native long getFee();
public native String getFirstTxId(); public String getFirstTxId() {
String id = getFirstTxIdJ();
if (id == null)
throw new IndexOutOfBoundsException();
return id;
}
public native String getFirstTxIdJ();
public native long getTxCount(); public native long getTxCount();

View File

@ -79,7 +79,11 @@ public class Wallet {
public native String getPath(); public native String getPath();
public native boolean isTestNet(); public NetworkType getNetworkType() {
return NetworkType.fromInteger(nettype());
}
public native int nettype();
//TODO virtual void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const = 0; //TODO virtual void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const = 0;
//TODO virtual bool useForkRules(uint8_t version, int64_t early_blocks) const = 0; //TODO virtual bool useForkRules(uint8_t version, int64_t early_blocks) const = 0;
@ -155,10 +159,10 @@ public class Wallet {
public static native boolean isPaymentIdValid(String payment_id); public static native boolean isPaymentIdValid(String payment_id);
public static boolean isAddressValid(String address) { public static boolean isAddressValid(String address) {
return isAddressValid(address, WalletManager.getInstance().isTestNet()); return isAddressValid(address, WalletManager.getInstance().getNetworkType().getValue());
} }
public static native boolean isAddressValid(String address, boolean isTestNet); public static native boolean isAddressValid(String address, int networkType);
//TODO static static bool keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, bool testnet, std::string &error); //TODO static static bool keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, bool testnet, std::string &error);

View File

@ -16,6 +16,8 @@
package com.m2049r.xmrwallet.model; package com.m2049r.xmrwallet.model;
import com.m2049r.xmrwallet.data.WalletNode;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileReader; import java.io.FileReader;
@ -69,22 +71,22 @@ public class WalletManager {
} }
public Wallet createWallet(File aFile, String password, String language) { public Wallet createWallet(File aFile, String password, String language) {
long walletHandle = createWalletJ(aFile.getAbsolutePath(), password, language, isTestNet()); long walletHandle = createWalletJ(aFile.getAbsolutePath(), password, language, getNetworkType().getValue());
Wallet wallet = new Wallet(walletHandle); Wallet wallet = new Wallet(walletHandle);
manageWallet(wallet); manageWallet(wallet);
return wallet; return wallet;
} }
private native long createWalletJ(String path, String password, String language, boolean isTestNet); private native long createWalletJ(String path, String password, String language, int networkType);
public Wallet openWallet(String path, String password) { public Wallet openWallet(String path, String password) {
long walletHandle = openWalletJ(path, password, isTestNet()); long walletHandle = openWalletJ(path, password, getNetworkType().getValue());
Wallet wallet = new Wallet(walletHandle); Wallet wallet = new Wallet(walletHandle);
manageWallet(wallet); manageWallet(wallet);
return wallet; return wallet;
} }
private native long openWalletJ(String path, String password, boolean isTestNet); private native long openWalletJ(String path, String password, int networkType);
public Wallet recoveryWallet(File aFile, String password, String mnemonic) { public Wallet recoveryWallet(File aFile, String password, String mnemonic) {
return recoveryWallet(aFile, password, mnemonic, 0); return recoveryWallet(aFile, password, mnemonic, 0);
@ -92,28 +94,28 @@ public class WalletManager {
public Wallet recoveryWallet(File aFile, String password, String mnemonic, long restoreHeight) { public Wallet recoveryWallet(File aFile, String password, String mnemonic, long restoreHeight) {
long walletHandle = recoveryWalletJ(aFile.getAbsolutePath(), password, mnemonic, long walletHandle = recoveryWalletJ(aFile.getAbsolutePath(), password, mnemonic,
isTestNet(), restoreHeight); getNetworkType().getValue(), restoreHeight);
Wallet wallet = new Wallet(walletHandle); Wallet wallet = new Wallet(walletHandle);
manageWallet(wallet); manageWallet(wallet);
return wallet; return wallet;
} }
private native long recoveryWalletJ(String path, String password, String mnemonic, private native long recoveryWalletJ(String path, String password, String mnemonic,
boolean isTestNet, long restoreHeight); int networkType, long restoreHeight);
public Wallet createWalletWithKeys(File aFile, String password, String language, long restoreHeight, public Wallet createWalletWithKeys(File aFile, String password, String language, long restoreHeight,
String addressString, String viewKeyString, String spendKeyString) { String addressString, String viewKeyString, String spendKeyString) {
long walletHandle = createWalletWithKeysJ(aFile.getAbsolutePath(), password, long walletHandle = createWalletFromKeysJ(aFile.getAbsolutePath(), password,
language, isTestNet(), restoreHeight, language, getNetworkType().getValue(), restoreHeight,
addressString, viewKeyString, spendKeyString); addressString, viewKeyString, spendKeyString);
Wallet wallet = new Wallet(walletHandle); Wallet wallet = new Wallet(walletHandle);
manageWallet(wallet); manageWallet(wallet);
return wallet; return wallet;
} }
private native long createWalletWithKeysJ(String path, String password, private native long createWalletFromKeysJ(String path, String password,
String language, String language,
boolean isTestNet, int networkType,
long restoreHeight, long restoreHeight,
String addressString, String addressString,
String viewKeyString, String viewKeyString,
@ -204,24 +206,23 @@ public class WalletManager {
//TODO virtual bool checkPayment(const std::string &address, const std::string &txid, const std::string &txkey, const std::string &daemon_address, uint64_t &received, uint64_t &height, std::string &error) const = 0; //TODO virtual bool checkPayment(const std::string &address, const std::string &txid, const std::string &txkey, const std::string &daemon_address, uint64_t &received, uint64_t &height, std::string &error) const = 0;
private String daemonAddress = null; private String daemonAddress = null;
private boolean testnet = true; private NetworkType networkType = null;
public boolean isTestNet() { public NetworkType getNetworkType() {
if (daemonAddress == null) { return networkType;
return true;
// assume testnet not explicitly initialised
//throw new IllegalStateException("use setDaemon() to initialise daemon and net first!");
}
return testnet;
} }
public void setDaemon(String address, boolean testnet, String username, String password) { //public void setDaemon(String address, NetworkType networkType, String username, String password) {
//Timber.d("SETDAEMON " + username + "/" + password + "/" + address); public void setDaemon(WalletNode walletNode) {
this.daemonAddress = address; this.daemonAddress = walletNode.getAddress();
this.testnet = testnet; this.networkType = networkType;
this.daemonUsername = username; this.daemonUsername = walletNode.getUsername();
this.daemonPassword = password; this.daemonPassword = walletNode.getPassword();
setDaemonAddressJ(address); setDaemonAddressJ(daemonAddress);
}
public void setNetworkType(NetworkType networkType) {
this.networkType = networkType;
} }
public String getDaemonAddress() { public String getDaemonAddress() {

View File

@ -524,7 +524,7 @@ public class WalletService extends Service {
showProgress(20); showProgress(20);
Wallet wallet = null; Wallet wallet = null;
WalletManager walletMgr = WalletManager.getInstance(); WalletManager walletMgr = WalletManager.getInstance();
Timber.d("WalletManager testnet=%s", walletMgr.isTestNet()); Timber.d("WalletManager network=%s", walletMgr.getNetworkType().name());
showProgress(30); showProgress(30);
if (walletMgr.walletExists(path)) { if (walletMgr.walletExists(path)) {
Timber.d("open wallet %s", path); Timber.d("open wallet %s", path);

View File

@ -18,6 +18,9 @@ package com.m2049r.xmrwallet.util;
// based on https://rosettacode.org/wiki/Bitcoin/address_validation#Java // based on https://rosettacode.org/wiki/Bitcoin/address_validation#Java
import com.m2049r.xmrwallet.model.NetworkType;
import com.m2049r.xmrwallet.model.WalletManager;
import java.math.BigInteger; import java.math.BigInteger;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
@ -27,6 +30,11 @@ public class BitcoinAddressValidator {
private static final String ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; private static final String ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
public static boolean validate(String addrress) {
return validate(addrress,
WalletManager.getInstance().getNetworkType() != NetworkType.NetworkType_Mainnet);
}
public static boolean validate(String addrress, boolean testnet) { public static boolean validate(String addrress, boolean testnet) {
if (addrress.length() < 26 || addrress.length() > 35) if (addrress.length() < 26 || addrress.length() > 35)
return false; return false;

View File

@ -31,12 +31,15 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.VectorDrawable; import android.graphics.drawable.VectorDrawable;
import android.os.Environment; import android.os.Environment;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
import android.system.ErrnoException;
import android.system.Os;
import android.view.WindowManager; import android.view.WindowManager;
import android.view.animation.Animation; import android.view.animation.Animation;
import android.view.animation.AnimationUtils; import android.view.animation.AnimationUtils;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import com.m2049r.xmrwallet.R; import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.model.NetworkType;
import com.m2049r.xmrwallet.model.Wallet; import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager; import com.m2049r.xmrwallet.model.WalletManager;
@ -55,16 +58,21 @@ import timber.log.Timber;
public class Helper { public class Helper {
static private final String WALLET_DIR = "monerujo"; static private final String WALLET_DIR = "monerujo";
static private final String HOME_DIR = "monero";
static public int DISPLAY_DIGITS_INFO = 5; static public int DISPLAY_DIGITS_INFO = 5;
static public File getStorageRoot(Context context) { static public File getWalletRoot(Context context) {
return getStorage(context, WALLET_DIR);
}
static public File getStorage(Context context, String folderName) {
if (!isExternalStorageWritable()) { if (!isExternalStorageWritable()) {
String msg = context.getString(R.string.message_strorage_not_writable); String msg = context.getString(R.string.message_strorage_not_writable);
Timber.e(msg); Timber.e(msg);
throw new IllegalStateException(msg); throw new IllegalStateException(msg);
} }
File dir = new File(Environment.getExternalStorageDirectory(), WALLET_DIR); File dir = new File(Environment.getExternalStorageDirectory(), folderName);
if (!dir.exists()) { if (!dir.exists()) {
Timber.i("Creating %s", dir.getAbsolutePath()); Timber.i("Creating %s", dir.getAbsolutePath());
dir.mkdirs(); // try to make it dir.mkdirs(); // try to make it
@ -114,9 +122,9 @@ public class Helper {
} }
static public File getWalletFile(Context context, String aWalletName) { static public File getWalletFile(Context context, String aWalletName) {
File walletDir = getStorageRoot(context); File walletDir = getWalletRoot(context);
File f = new File(walletDir, aWalletName); File f = new File(walletDir, aWalletName);
Timber.d("wallet= %s size= %d", f.getAbsolutePath(), f.length()); Timber.d("wallet=%s size= %d", f.getAbsolutePath(), f.length());
return f; return f;
} }
@ -263,10 +271,20 @@ public class Helper {
} }
static public HttpUrl getXmrToBaseUrl() { static public HttpUrl getXmrToBaseUrl() {
if ((WalletManager.getInstance() == null) || WalletManager.getInstance().isTestNet()) { if ((WalletManager.getInstance() == null)
|| (WalletManager.getInstance().getNetworkType() != NetworkType.NetworkType_Mainnet)) {
return HttpUrl.parse("https://test.xmr.to/api/v2/xmr2btc/"); return HttpUrl.parse("https://test.xmr.to/api/v2/xmr2btc/");
} else { } else {
return HttpUrl.parse("https://xmr.to/api/v2/xmr2btc/"); return HttpUrl.parse("https://xmr.to/api/v2/xmr2btc/");
} }
} }
static public void setMoneroHome(Context context) {
try {
String home = getStorage(context, HOME_DIR).getAbsolutePath();
Os.setenv("HOME", home, true);
} catch (ErrnoException ex) {
throw new IllegalStateException(ex);
}
}
} }

View File

@ -45,7 +45,7 @@ public class Toolbar extends android.support.v7.widget.Toolbar {
ImageView toolbarImage; ImageView toolbarImage;
TextView toolbarTitle; TextView toolbarTitle;
TextView toolbarSubtitle; TextView toolbarSubtitle;
Button bDonate; Button bCredits;
public Toolbar(Context context) { public Toolbar(Context context) {
super(context); super(context);
@ -87,8 +87,8 @@ public class Toolbar extends android.support.v7.widget.Toolbar {
toolbarTitle = (TextView) findViewById(R.id.toolbarTitle); toolbarTitle = (TextView) findViewById(R.id.toolbarTitle);
toolbarSubtitle = (TextView) findViewById(R.id.toolbarSubtitle); toolbarSubtitle = (TextView) findViewById(R.id.toolbarSubtitle);
bDonate = (Button) findViewById(R.id.bDonate); bCredits = (Button) findViewById(R.id.bCredits);
bDonate.setOnClickListener(new View.OnClickListener() { bCredits.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) { public void onClick(View v) {
if (onButtonListener != null) { if (onButtonListener != null) {
onButtonListener.onButton(buttonType); onButtonListener.onButton(buttonType);
@ -116,43 +116,43 @@ public class Toolbar extends android.support.v7.widget.Toolbar {
public final static int BUTTON_NONE = 0; public final static int BUTTON_NONE = 0;
public final static int BUTTON_BACK = 1; public final static int BUTTON_BACK = 1;
public final static int BUTTON_CLOSE = 2; public final static int BUTTON_CLOSE = 2;
public final static int BUTTON_DONATE = 3; public final static int BUTTON_CREDITS = 3;
public final static int BUTTON_CANCEL = 4; public final static int BUTTON_CANCEL = 4;
int buttonType = BUTTON_DONATE; int buttonType = BUTTON_CREDITS;
public void setButton(int type) { public void setButton(int type) {
switch (type) { switch (type) {
case BUTTON_BACK: case BUTTON_BACK:
Timber.d("BUTTON_BACK"); Timber.d("BUTTON_BACK");
bDonate.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_arrow_back_white_24dp, 0, 0, 0); bCredits.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_arrow_back_white_24dp, 0, 0, 0);
bDonate.setText(null); bCredits.setText(null);
bDonate.setVisibility(View.VISIBLE); bCredits.setVisibility(View.VISIBLE);
break; break;
case BUTTON_CLOSE: case BUTTON_CLOSE:
Timber.d("BUTTON_CLOSE"); Timber.d("BUTTON_CLOSE");
bDonate.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_close_white_24dp, 0, 0, 0); bCredits.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_close_white_24dp, 0, 0, 0);
bDonate.setText(R.string.label_close); bCredits.setText(R.string.label_close);
bDonate.setVisibility(View.VISIBLE); bCredits.setVisibility(View.VISIBLE);
break; break;
case BUTTON_DONATE: case BUTTON_CREDITS:
Timber.d("BUTTON_DONATE"); Timber.d("BUTTON_CREDITS");
bDonate.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_favorite_white_24dp, 0, 0, 0); bCredits.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_favorite_white_24dp, 0, 0, 0);
bDonate.setText(R.string.label_donate); bCredits.setText(R.string.label_credits);
bDonate.setVisibility(View.VISIBLE); bCredits.setVisibility(View.VISIBLE);
break; break;
case BUTTON_CANCEL: case BUTTON_CANCEL:
Timber.d("BUTTON_CANCEL"); Timber.d("BUTTON_CANCEL");
bDonate.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_close_white_24dp, 0, 0, 0); bCredits.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_close_white_24dp, 0, 0, 0);
bDonate.setText(R.string.label_cancel); bCredits.setText(R.string.label_cancel);
bDonate.setVisibility(View.VISIBLE); bCredits.setVisibility(View.VISIBLE);
break; break;
case BUTTON_NONE: case BUTTON_NONE:
default: default:
Timber.d("BUTTON_NONE"); Timber.d("BUTTON_NONE");
bDonate.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); bCredits.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
bDonate.setText(null); bCredits.setText(null);
bDonate.setVisibility(View.INVISIBLE); bCredits.setVisibility(View.INVISIBLE);
} }
buttonType = type; buttonType = type;
} }

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="8dp"
android:paddingEnd="16dp"
android:paddingStart="16dp">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="100dp"
android:src="@drawable/gunther_coder" />
<TextView
android:id="@+id/tvCredits"
style="@style/MoneroText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/header_top"
android:autoLink="web"
android:gravity="center"
android:text="@string/credits_text" />
</LinearLayout>
</ScrollView>

View File

@ -1,79 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="8dp"
android:paddingEnd="16dp"
android:paddingStart="16dp">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="100dp"
android:src="@drawable/gunther_donate" />
<TextView
style="@style/MoneroText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/donation_text"
android:textSize="14sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/header_top"
android:background="@drawable/backgound_spinner"
android:orientation="vertical"
android:padding="@dimen/header_top">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
style="@style/MoneroLabel.Heading.Donation"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center"
android:text="@string/donation_address_label" />
<ImageButton
android:id="@+id/bCopyAddress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginEnd="8dp"
android:background="?android:selectableItemBackground"
android:src="@drawable/ic_content_copy_black_24dp" />
</FrameLayout>
<TextView
android:id="@+id/tvWalletAddress"
style="@style/MoneroText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/data_top"
android:text="@string/donation_address"
android:textAlignment="center"
android:textColor="@color/moneroBlack"
android:textSize="17sp" />
</LinearLayout>
<TextView
android:id="@+id/tvCredits"
style="@style/MoneroText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/header_top"
android:autoLink="web"
android:gravity="center"
android:text="@string/donation_credits" />
</LinearLayout>
</ScrollView>

View File

@ -3,12 +3,12 @@
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<Button <Button
android:id="@+id/bDonate" android:id="@+id/bCredits"
style="@style/ToolBarStyle.ActionButton" style="@style/ToolBarStyle.ActionButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:drawableStart="@drawable/ic_favorite_white_24dp" android:drawableStart="@drawable/ic_favorite_white_24dp"
android:text="@string/label_donate" /> android:text="@string/label_credits" />
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content" android:layout_width="wrap_content"

View File

@ -3,10 +3,10 @@
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item <item
android:id="@+id/action_donate" android:id="@+id/action_credits"
android:icon="@drawable/ic_favorite_white_24dp" android:icon="@drawable/ic_favorite_white_24dp"
android:orderInCategory="10" android:orderInCategory="10"
android:title="@string/label_donate" android:title="@string/label_credits"
app:showAsAction="ifRoom" /> app:showAsAction="ifRoom" />
<item <item

View File

@ -4,13 +4,7 @@
<string name="about_close">Cerrar</string> <string name="about_close">Cerrar</string>
<string name="about_version">Versión %1$s (%2$d)</string> <string name="about_version">Versión %1$s (%2$d)</string>
<string name="donation_text"> <string name="credits_text"><![CDATA[
\"¡Donad, bastardos ingratos!\"
</string>
<string name="donation_address_label">Donaciones</string>
<string name="donation_credits"><![CDATA[
<b>Créditos</b> <b>Créditos</b>
<br/> <br/>
m2049r, baltsar777, anhdres, keejef, m2049r, baltsar777, anhdres, keejef,

View File

@ -20,7 +20,7 @@
<string name="password_very_strong">¡Bien ahí, hacker nivel 4!</string> <string name="password_very_strong">¡Bien ahí, hacker nivel 4!</string>
<string name="label_login_wallets">Monederos</string> <string name="label_login_wallets">Monederos</string>
<string name="label_donate">Donar</string> <string name="label_credits">Créditos</string>
<string name="label_ok">Aceptar</string> <string name="label_ok">Aceptar</string>
<string name="label_cancel">Cancelar</string> <string name="label_cancel">Cancelar</string>
<string name="label_close">Cerrar</string> <string name="label_close">Cerrar</string>
@ -59,6 +59,7 @@
<string name="label_daemon">Nodo</string> <string name="label_daemon">Nodo</string>
<string name="prompt_daemon">([&lt;usuario&gt;:&lt;contraseña&gt;@]&lt;daemon&gt;[:&lt;puerto&gt;])</string> <string name="prompt_daemon">([&lt;usuario&gt;:&lt;contraseña&gt;@]&lt;daemon&gt;[:&lt;puerto&gt;])</string>
<string name="prompt_mainnet">Selección de Red</string> <string name="prompt_mainnet">Selección de Red</string>
<string name="connect_stagenet">StageNet</string>
<string name="connect_testnet">TestNet</string> <string name="connect_testnet">TestNet</string>
<string name="connect_mainnet">MainNet</string> <string name="connect_mainnet">MainNet</string>
<string name="status_walletlist_loading">Cargando lista de monederos</string> <string name="status_walletlist_loading">Cargando lista de monederos</string>

View File

@ -5,14 +5,7 @@
<string name="about_whoami">I am monerujo</string> <string name="about_whoami">I am monerujo</string>
<string name="about_version">Version %1$s (%2$d)</string> <string name="about_version">Version %1$s (%2$d)</string>
<string name="donation_text"> <string name="credits_text"><![CDATA[
\"Donate you ungrateful bastards!\"
</string>
<string name="donation_address_label">Donations Address</string>
<string name="donation_address" translatable="false">4AdkPJoxn7JCvAby9szgnt93MSEwdnxdhaASxbTBm6x5dCwmsDep2UYN4FhStDn5i11nsJbpU7oj59ahg8gXb1Mg3viqCuk</string>
<string name="donation_credits"><![CDATA[
<b>Credits</b> <b>Credits</b>
<br/> <br/>
m2049r, baltsar777, anhdres, keejef, m2049r, baltsar777, anhdres, keejef,

View File

@ -22,7 +22,7 @@
<string name="password_very_strong">Yeah baby, h4x0r style!</string> <string name="password_very_strong">Yeah baby, h4x0r style!</string>
<string name="label_login_wallets">Wallets</string> <string name="label_login_wallets">Wallets</string>
<string name="label_donate">Donate</string> <string name="label_credits">Credits</string>
<string name="label_ok">OK</string> <string name="label_ok">OK</string>
<string name="label_cancel">Cancel</string> <string name="label_cancel">Cancel</string>
<string name="label_close">Close</string> <string name="label_close">Close</string>
@ -124,6 +124,7 @@
<string name="label_daemon">Node</string> <string name="label_daemon">Node</string>
<string name="prompt_daemon">([&lt;user&gt;:&lt;pass&gt;@]&lt;daemon&gt;[:&lt;port&gt;])</string> <string name="prompt_daemon">([&lt;user&gt;:&lt;pass&gt;@]&lt;daemon&gt;[:&lt;port&gt;])</string>
<string name="prompt_mainnet">Net Selection</string> <string name="prompt_mainnet">Net Selection</string>
<string name="connect_stagenet">StageNet</string>
<string name="connect_testnet">TestNet</string> <string name="connect_testnet">TestNet</string>
<string name="connect_mainnet">MainNet</string> <string name="connect_mainnet">MainNet</string>
<string name="status_walletlist_loading">Loading Wallet List</string> <string name="status_walletlist_loading">Loading Wallet List</string>
@ -348,8 +349,8 @@
<string name="archive_alert_no">No thanks!</string> <string name="archive_alert_no">No thanks!</string>
<string-array name="mixin" translatable="false"> <string-array name="mixin" translatable="false">
<item>Ringsize 5</item> <item>Ringsize 7</item>
<item>Ringsize 8</item> <item>Ringsize 10</item>
<item>Ringsize 13</item> <item>Ringsize 13</item>
<item>Ringsize 26</item> <item>Ringsize 26</item>
</string-array> </string-array>

View File

@ -4,27 +4,35 @@ Based on https://forum.getmonero.org/5/support/87643/building-monero-v0-10-3-1-f
Do not follow this blindly. Do not follow this blindly.
These instructions are tailored to building ```wallep_api```.
These instructions build all supported architectures: ```'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'```. These instructions build all supported architectures: ```'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'```.
Yes, lots of copy&paste here. TODO: Script this. Yes, lots of copy&paste here. TODO: Script this.
## Prepare Ubuntu environment ## Prepare Ubuntu environment
``` ```Shell
sudo apt-get install build-essential cmake tofrodos libtool-bin sudo apt-get install build-essential cmake tofrodos libtool-bin
sudo mkdir /opt/android sudo mkdir /opt/android
sudo chown $LOGNAME /opt/android sudo chown $LOGNAME /opt/android
``` ```
## Install Android NDK ## Install Android NDK
``` ```Shell
cd /opt/android cd /opt/android
wget https://dl.google.com/android/repository/android-ndk-r15c-linux-x86_64.zip wget https://dl.google.com/android/repository/android-ndk-r16b-linux-x86_64.zip
unzip android-ndk-r15c-linux-x86_64.zip unzip android-ndk-r16b-linux-x86_64.zip
android-ndk-r15c/build/tools/make_standalone_toolchain.py --api 21 --stl=libc++ --arch arm --install-dir /opt/android/tool32 ln -s android-ndk-r16b ndk
android-ndk-r15c/build/tools/make_standalone_toolchain.py --api 21 --stl=libc++ --arch arm64 --install-dir /opt/android/tool64 ndk/build/tools/make_standalone_toolchain.py --api 21 --stl=libc++ --arch arm --install-dir /opt/android/tool/arm
android-ndk-r15c/build/tools/make_standalone_toolchain.py --api 21 --stl=libc++ --arch x86 --install-dir /opt/android/toolx86 ndk/build/tools/make_standalone_toolchain.py --api 21 --stl=libc++ --arch arm64 --install-dir /opt/android/tool/arm64
android-ndk-r15c/build/tools/make_standalone_toolchain.py --api 21 --stl=libc++ --arch x86_64 --install-dir /opt/android/toolx86_64 ndk/build/tools/make_standalone_toolchain.py --api 21 --stl=libc++ --arch x86 --install-dir /opt/android/tool/x86
ndk/build/tools/make_standalone_toolchain.py --api 21 --stl=libc++ --arch x86_64 --install-dir /opt/android/tool/x86_64
```
## Prepare output
```Shell
mkdir -p /opt/android/build
``` ```
## Build OpenSSL ## Build OpenSSL
@ -32,131 +40,81 @@ Best is to compile openssl from sources. Copying from your phone or elsewhere (d
If you don't want to build for all architectures, edit ```build-all-arch.sh``` before running it (Line 12). If you don't want to build for all architectures, edit ```build-all-arch.sh``` before running it (Line 12).
``` ```Shell
cd /opt/android cd /opt/android
git clone https://github.com/m2049r/android-openssl.git git clone https://github.com/m2049r/android-openssl.git
wget https://github.com/openssl/openssl/archive/OpenSSL_1_0_2l.tar.gz wget https://github.com/openssl/openssl/archive/OpenSSL_1_0_2l.tar.gz
cd android-openssl cd android-openssl
tar xfz ../OpenSSL_1_0_2l.tar.gz tar xfz ../OpenSSL_1_0_2l.tar.gz
export ANDROID_NDK_ROOT=/opt/android/android-ndk-r15c ANDROID_NDK_ROOT=/opt/android/ndk ./build-all-arch.sh
./build-all-arch.sh
``` ```
### Make symlinks ### Install & make symlinks
```Shell
mkdir -p /opt/android/build/openssl/{arm,arm64,x86,x86_64}
cp -a /opt/android/android-openssl/prebuilt/armeabi /opt/android/build/openssl/arm/lib
cp -a /opt/android/android-openssl/prebuilt/arm64-v8a /opt/android/build/openssl/arm64/lib
cp -a /opt/android/android-openssl/prebuilt/x86 /opt/android/build/openssl/x86/lib
cp -a /opt/android/android-openssl/prebuilt/x86_64 /opt/android/build/openssl/x86_64/lib
cp -aL /opt/android/android-openssl/openssl-OpenSSL_1_0_2l/include/openssl/ /opt/android/build/openssl/include
ln -s /opt/android/build/openssl/include /opt/android/build/openssl/arm/include
ln -s /opt/android/build/openssl/include /opt/android/build/openssl/arm64/include
ln -s /opt/android/build/openssl/include /opt/android/build/openssl/x86/include
ln -s /opt/android/build/openssl/include /opt/android/build/openssl/x86_64/include
``` ```
cd /opt/android/tool32/sysroot/usr/include ```Shell
ln -s ../../../../android-openssl/openssl-OpenSSL_1_0_2l/include/openssl/ ln -sf /opt/android/build/openssl/include /opt/android/tool/arm/sysroot/usr/include/openssl
cd /opt/android/tool32/sysroot/usr/lib ln -sf /opt/android/build/openssl/lib/armeabi-v7a/*.so /opt/android/tool/arm/sysroot/usr/lib
ln -s ../../../../android-openssl/prebuilt/armeabi/libssl.so
ln -s ../../../../android-openssl/prebuilt/armeabi/libcrypto.so
cd /opt/android/tool64/sysroot/usr/include ln -sf /opt/android/build/openssl/include /opt/android/tool/arm64/sysroot/usr/include/openssl
ln -s ../../../../android-openssl/openssl-OpenSSL_1_0_2l/include/openssl/ ln -sf /opt/android/build/openssl/lib/arm64-v8a/*.so /opt/android/tool/arm64/sysroot/usr/lib
cd /opt/android/tool64/sysroot/usr/lib
ln -s ../../../../android-openssl/prebuilt/arm64-v8a/libssl.so
ln -s ../../../../android-openssl/prebuilt/arm64-v8a/libcrypto.so
cd /opt/android/toolx86/sysroot/usr/include ln -sf /opt/android/build/openssl/include /opt/android/tool/x86/sysroot/usr/include/openssl
ln -s ../../../../android-openssl/openssl-OpenSSL_1_0_2l/include/openssl/ ln -sf /opt/android/build/openssl/lib/x86/*.so /opt/android/tool/x86/sysroot/usr/lib
cd /opt/android/toolx86/sysroot/usr/lib
ln -s ../../../../android-openssl/prebuilt/x86/libssl.so
ln -s ../../../../android-openssl/prebuilt/x86/libcrypto.so
cd /opt/android/toolx86_64/sysroot/usr/include ln -sf /opt/android/build/openssl/include /opt/android/tool/x86_64/sysroot/usr/include/openssl
ln -s ../../../../android-openssl/openssl-OpenSSL_1_0_2l/include/openssl/ ln -sf /opt/android/build/openssl/lib/x86_64/*.so /opt/android/tool/x86_64/sysroot/usr/lib64
cd /opt/android/toolx86_64/sysroot/usr/lib
ln -s ../../../../android-openssl/prebuilt/x86_64/libssl.so
ln -s ../../../../android-openssl/prebuilt/x86_64/libcrypto.so
``` ```
## Build Boost ## Build Boost
``` ```Shell
cd /opt/android cd /opt/android
wget https://sourceforge.net/projects/boost/files/boost/1.58.0/boost_1_58_0.tar.gz/download -O boost_1_58_0.tar.gz wget https://sourceforge.net/projects/boost/files/boost/1.58.0/boost_1_58_0.tar.gz/download -O boost_1_58_0.tar.gz
tar xfz boost_1_58_0.tar.gz tar xfz boost_1_58_0.tar.gz
cd boost_1_58_0 cd boost_1_58_0
./bootstrap.sh ./bootstrap.sh
``` ```
The NDK r15c above gives errors about fsetpos and fgetpos not found(!?!), so we "just" comment them out in the include file: Comment out ```using ::fgetpos;``` & ```using ::fsetpos;``` in ```cstdio```.
* `vi /opt/android/tool32/include/c++/4.9.x/cstdio` (`//using ::fgetpos`, `//using ::fsetpos`)
* `vi /opt/android/tool64/include/c++/4.9.x/cstdio` (`//using ::fgetpos`, `//using ::fsetpos`)
* `vi /opt/android/toolx86/include/c++/4.9.x/cstdio` (`//using ::fgetpos`, `//using ::fsetpos`)
* `vi /opt/android/toolx86_64/include/c++/4.9.x/cstdio` (`//using ::fgetpos`, `//using ::fsetpos`)
Then: Then build & install to ```/opt/android/build/boost``` with
``` ```Shell
PATH=/opt/android/tool32/arm-linux-androideabi/bin:/opt/android/tool32/bin:$PATH ./b2 --build-type=minimal link=static runtime-link=static --with-chrono --with-date_time --with-filesystem --with-program_options --with-regex --with-serialization --with-system --with-thread --build-dir=android32 --stagedir=android32 toolset=clang threading=multi threadapi=pthread target-os=android stage PATH=/opt/android/tool/arm/arm-linux-androideabi/bin:/opt/android/tool/arm/bin:$PATH ./b2 --build-type=minimal link=static runtime-link=static --with-chrono --with-date_time --with-filesystem --with-program_options --with-regex --with-serialization --with-system --with-thread --build-dir=android-arm --prefix=/opt/android/build/boost/arm --includedir=/opt/android/build/boost/include toolset=clang threading=multi threadapi=pthread target-os=android install
PATH=/opt/android/tool64/aarch64-linux-android/bin:/opt/android/tool64/bin:$PATH ./b2 --build-type=minimal link=static runtime-link=static --with-chrono --with-date_time --with-filesystem --with-program_options --with-regex --with-serialization --with-system --with-thread --build-dir=android64 --stagedir=android64 toolset=clang threading=multi threadapi=pthread target-os=android stage ln -sf ../include /opt/android/build/boost/arm
PATH=/opt/android/toolx86/i686-linux-android/bin:/opt/android/toolx86/bin:$PATH ./b2 --build-type=minimal link=static runtime-link=static --with-chrono --with-date_time --with-filesystem --with-program_options --with-regex --with-serialization --with-system --with-thread --build-dir=androidx86 --stagedir=androidx86 toolset=clang threading=multi threadapi=pthread target-os=android stage PATH=/opt/android/tool/arm64/aarch64-linux-android/bin:/opt/android/tool/arm64/bin:$PATH ./b2 --build-type=minimal link=static runtime-link=static --with-chrono --with-date_time --with-filesystem --with-program_options --with-regex --with-serialization --with-system --with-thread --build-dir=android-arm64 --prefix=/opt/android/build/boost/arm64 --includedir=/opt/android/build/boost/include toolset=clang threading=multi threadapi=pthread target-os=android install
PATH=/opt/android/toolx86_64/x86_64-linux-android/bin:/opt/android/toolx86_64/bin:$PATH ./b2 --build-type=minimal link=static runtime-link=static --with-chrono --with-date_time --with-filesystem --with-program_options --with-regex --with-serialization --with-system --with-thread --build-dir=androidx86_64 --stagedir=androidx86_64 toolset=clang threading=multi threadapi=pthread target-os=android stage ln -sf ../include /opt/android/build/boost/arm64
PATH=/opt/android/tool/x86/i686-linux-android/bin:/opt/android/tool/x86/bin:$PATH ./b2 --build-type=minimal link=static runtime-link=static --with-chrono --with-date_time --with-filesystem --with-program_options --with-regex --with-serialization --with-system --with-thread --build-dir=android-x86 --prefix=/opt/android/build/boost/x86 --includedir=/opt/android/build/boost/include toolset=clang threading=multi threadapi=pthread target-os=android install
ln -sf ../include /opt/android/build/boost/x86
PATH=/opt/android/tool/x86_64/x86_64-linux-android/bin:/opt/android/tool/x86_64/bin:$PATH ./b2 --build-type=minimal link=static runtime-link=static --with-chrono --with-date_time --with-filesystem --with-program_options --with-regex --with-serialization --with-system --with-thread --build-dir=android-x86_64 --prefix=/opt/android/build/boost/x86_64 --includedir=/opt/android/build/boost/include toolset=clang threading=multi threadapi=pthread target-os=android install
ln -sf ../include /opt/android/build/boost/x86_64
``` ```
## And finally: Build Monero ## And finally: Build Monero
``` ```Shell
cd /opt/android cd /opt/android
git clone https://github.com/m2049r/monero.git git clone https://github.com/m2049r/monero.git
cd /opt/android/monero cd /opt/android/monero/build
mkdir -p build/release.android32 ./build-all-arch.sh
cd build/release.android32
PATH=/opt/android/tool32/arm-linux-androideabi/bin:/opt/android/tool32/bin:$PATH CC=clang CXX=clang++ cmake -D BUILD_TESTS=OFF -D ARCH="armv7-a" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D BUILD_TAG="android" -D BOOST_ROOT=/opt/android/boost_1_58_0 -D BOOST_LIBRARYDIR=/opt/android/boost_1_58_0/android32/lib -D OPENSSL_ROOT_DIR=/opt/android/android-openssl/openssl-OpenSSL_1_0_2l -D OPENSSL_CRYPTO_LIBRARY=/opt/android/android-openssl/prebuilt/armeabi/libcrypto.so -D OPENSSL_SSL_LIBRARY=/opt/android/android-openssl/prebuilt/armeabi/libssl.so -D CMAKE_POSITION_INDEPENDENT_CODE:BOOL=true ../..
make
find . -name '*.a' -exec cp '{}' lib \;
cd /opt/android/monero
mkdir -p build/release.android64
cd build/release.android64
PATH=/opt/android/tool64/aarch64-linux-android/bin:/opt/android/tool64/bin:$PATH CC=clang CXX=clang++ cmake -D BUILD_TESTS=OFF -D ARCH="armv8-a" -D STATIC=ON -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D BUILD_TAG="android" -D BOOST_ROOT=/opt/android/boost_1_58_0 -D BOOST_LIBRARYDIR=/opt/android/boost_1_58_0/android64/lib -D OPENSSL_ROOT_DIR=/opt/android/android-openssl/openssl-OpenSSL_1_0_2l -D OPENSSL_CRYPTO_LIBRARY=/opt/android/android-openssl/prebuilt/arm64-v8a/libcrypto.so -D OPENSSL_SSL_LIBRARY=/opt/android/android-openssl/prebuilt/arm64-v8a/libssl.so -D CMAKE_POSITION_INDEPENDENT_CODE:BOOL=true ../..
make
find . -name '*.a' -exec cp '{}' lib \;
cd /opt/android/monero
mkdir -p build/release.androidx86
cd build/release.androidx86
PATH=/opt/android/toolx86/i686-linux-android/bin:/opt/android/toolx86/bin:$PATH CC=clang CXX=clang++ cmake -D BUILD_TESTS=OFF -D ARCH="i686" -D STATIC=ON -D BUILD_64=OFF -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D BUILD_TAG="android" -D BOOST_ROOT=/opt/android/boost_1_58_0 -D BOOST_LIBRARYDIR=/opt/android/boost_1_58_0/androidx86/lib -D OPENSSL_ROOT_DIR=/opt/android/android-openssl/openssl-OpenSSL_1_0_2l -D OPENSSL_CRYPTO_LIBRARY=/opt/android/android-openssl/prebuilt/x86/libcrypto.so -D OPENSSL_SSL_LIBRARY=/opt/android/android-openssl/prebuilt/x86/libssl.so -D CMAKE_POSITION_INDEPENDENT_CODE:BOOL=true ../..
make
find . -name '*.a' -exec cp '{}' lib \;
cd /opt/android/monero
mkdir -p build/release.androidx86_64
cd build/release.androidx86_64
PATH=/opt/android/toolx86_64/x86_64-linux-android/bin:/opt/android/toolx86_64/bin:$PATH CC=clang CXX=clang++ cmake -D BUILD_TESTS=OFF -D ARCH="x86-64" -D STATIC=ON -D BUILD_64=ON -D CMAKE_BUILD_TYPE=release -D ANDROID=true -D BUILD_TAG="android" -D BOOST_ROOT=/opt/android/boost_1_58_0 -D BOOST_LIBRARYDIR=/opt/android/boost_1_58_0/androidx86_64/lib -D OPENSSL_ROOT_DIR=/opt/android/android-openssl/openssl-OpenSSL_1_0_2l -D OPENSSL_CRYPTO_LIBRARY=/opt/android/android-openssl/prebuilt/x86_64/libcrypto.so -D OPENSSL_SSL_LIBRARY=/opt/android/android-openssl/prebuilt/x86_64/libssl.so -D CMAKE_POSITION_INDEPENDENT_CODE:BOOL=true ../..
make
find . -name '*.a' -exec cp '{}' lib \;
``` ```
Ignore the warning from ```find``` - all monero static libraries are now in `lib`.
# Bringing it all together # Bringing it all together
- Copy all .a libraries into the appropriate `external-libs` folders. - Copy all .a libraries into the appropriate `external-libs` folders.
- Copy `/opt/android/monero/src/wallet/wallet2_api.h` into `external-libs/monero/include` - Copy `/opt/android/monero/src/wallet/api/wallet2_api.h` into `external-libs/monero/include`
If using default locations, this would mean: If using default locations, this would mean:
``` ```Shell
mkdir -p ~/StudioProjects/xmrwallet/external-libs/boost/lib/armeabi-v7a cd <path-to-xmrwallet>/external-libs
cp -a /opt/android/boost_1_58_0/android32/lib/*.a ~/StudioProjects/xmrwallet/external-libs/boost/lib/armeabi-v7a # remove old stuff
mkdir -p ~/StudioProjects/xmrwallet/external-libs/openssl/lib/armeabi-v7a find . -name "*.a" -or -name "*.h" -type f -delete
cp -a /opt/android/android-openssl/prebuilt/armeabi/*.a ~/StudioProjects/xmrwallet/external-libs/openssl/lib/armeabi-v7a ./collect.sh
mkdir -p ~/StudioProjects/xmrwallet/external-libs/monero/lib/armeabi-v7a
cp -a /opt/android/monero/build/release.android32/lib/*.a ~/StudioProjects/xmrwallet/external-libs/monero/lib/armeabi-v7a
mkdir -p ~/StudioProjects/xmrwallet/external-libs/boost/lib/arm64-v8a
cp -a /opt/android/boost_1_58_0/android64/lib/*.a ~/StudioProjects/xmrwallet/external-libs/boost/lib/arm64-v8a
mkdir -p ~/StudioProjects/xmrwallet/external-libs/openssl/lib/arm64-v8a
cp -a /opt/android/android-openssl/prebuilt/arm64-v8a/*.a ~/StudioProjects/xmrwallet/external-libs/openssl/lib/arm64-v8a
mkdir -p ~/StudioProjects/xmrwallet/external-libs/monero/lib/arm64-v8a
cp -a /opt/android/monero/build/release.android64/lib/*.a ~/StudioProjects/xmrwallet/external-libs/monero/lib/arm64-v8a
mkdir -p ~/StudioProjects/xmrwallet/external-libs/boost/lib/x86
cp -a /opt/android/boost_1_58_0/androidx86/lib/*.a ~/StudioProjects/xmrwallet/external-libs/boost/lib/x86
mkdir -p ~/StudioProjects/xmrwallet/external-libs/openssl/lib/x86
cp -a /opt/android/android-openssl/prebuilt/x86/*.a ~/StudioProjects/xmrwallet/external-libs/openssl/lib/x86
mkdir -p ~/StudioProjects/xmrwallet/external-libs/monero/lib/x86
cp -a /opt/android/monero/build/release.androidx86/lib/*.a ~/StudioProjects/xmrwallet/external-libs/monero/lib/x86
mkdir -p ~/StudioProjects/xmrwallet/external-libs/boost/lib/x86_64
cp -a /opt/android/boost_1_58_0/androidx86_64/lib/*.a ~/StudioProjects/xmrwallet/external-libs/boost/lib/x86_64
mkdir -p ~/StudioProjects/xmrwallet/external-libs/openssl/lib/x86_64
cp -a /opt/android/android-openssl/prebuilt/x86_64/*.a ~/StudioProjects/xmrwallet/external-libs/openssl/lib/x86_64
mkdir -p ~/StudioProjects/xmrwallet/external-libs/monero/lib/x86_64
cp -a /opt/android/monero/build/release.androidx86_64/lib/*.a ~/StudioProjects/xmrwallet/external-libs/monero/lib/x86_64
``` ```

View File

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View File

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View File

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View File

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

45
external-libs/collect.sh Executable file
View File

@ -0,0 +1,45 @@
#!/bin/bash
#
# -D BOOST_ROOT=/opt/android/boost_1_58_0
set -e
orig_path=$PATH
packages=(boost openssl monero)
archs=(arm arm64 x86 x86_64)
for arch in ${archs[@]}; do
case ${arch} in
"arm")
xarch="armeabi-v7a"
;;
"arm64")
xarch="arm64-v8a"
;;
"x86")
xarch="x86"
;;
"x86_64")
xarch="x86_64"
;;
*)
exit 16
;;
esac
for package in ${packages[@]}; do
OUTPUT_DIR=`pwd`/$package/lib/$xarch
mkdir -p $OUTPUT_DIR
rm -f $OUTPUT_DIR/*.a
cp -a /opt/android/build/$package/$arch/lib/*.a $OUTPUT_DIR
if [ $package = "monero" -a -d "/opt/android/build/$package/include" ]; then
rm -rf $OUTPUT_DIR/../../include
cp -a /opt/android/build/$package/include $OUTPUT_DIR/../..
fi
done
done
exit 0

View File

@ -1,4 +1,4 @@
// Copyright (c) 2014-2017, The Monero Project // Copyright (c) 2014-2018, The Monero Project
// //
// All rights reserved. // All rights reserved.
// //
@ -33,14 +33,23 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <list>
#include <set>
#include <ctime> #include <ctime>
#include <iostream> #include <iostream>
// Public interface for libwallet library // Public interface for libwallet library
namespace Monero { namespace Monero {
enum NetworkType : uint8_t {
MAINNET = 0,
TESTNET,
STAGENET
};
namespace Utils { namespace Utils {
bool isAddressLocal(const std::string &hostaddr); bool isAddressLocal(const std::string &hostaddr);
void onStartup();
} }
template<typename T> template<typename T>
@ -68,6 +77,7 @@ struct PendingTransaction
}; };
enum Priority { enum Priority {
Priority_Default = 0,
Priority_Low = 1, Priority_Low = 1,
Priority_Medium = 2, Priority_Medium = 2,
Priority_High = 3, Priority_High = 3,
@ -88,6 +98,8 @@ struct PendingTransaction
* \return * \return
*/ */
virtual uint64_t txCount() const = 0; virtual uint64_t txCount() const = 0;
virtual std::vector<uint32_t> subaddrAccount() const = 0;
virtual std::vector<std::set<uint32_t>> subaddrIndices() const = 0;
}; };
/** /**
@ -101,13 +113,6 @@ struct UnsignedTransaction
Status_Critical Status_Critical
}; };
enum Priority {
Priority_Low = 1,
Priority_Medium = 2,
Priority_High = 3,
Priority_Last
};
virtual ~UnsignedTransaction() = 0; virtual ~UnsignedTransaction() = 0;
virtual int status() const = 0; virtual int status() const = 0;
virtual std::string errorString() const = 0; virtual std::string errorString() const = 0;
@ -155,6 +160,9 @@ struct TransactionInfo
virtual uint64_t amount() const = 0; virtual uint64_t amount() const = 0;
virtual uint64_t fee() const = 0; virtual uint64_t fee() const = 0;
virtual uint64_t blockHeight() const = 0; virtual uint64_t blockHeight() const = 0;
virtual std::set<uint32_t> subaddrIndex() const = 0;
virtual uint32_t subaddrAccount() const = 0;
virtual std::string label() const = 0;
virtual uint64_t confirmations() const = 0; virtual uint64_t confirmations() const = 0;
virtual uint64_t unlockTime() const = 0; virtual uint64_t unlockTime() const = 0;
//! transaction_id //! transaction_id
@ -223,6 +231,66 @@ struct AddressBook
virtual int lookupPaymentID(const std::string &payment_id) const = 0; virtual int lookupPaymentID(const std::string &payment_id) const = 0;
}; };
struct SubaddressRow {
public:
SubaddressRow(std::size_t _rowId, const std::string &_address, const std::string &_label):
m_rowId(_rowId),
m_address(_address),
m_label(_label) {}
private:
std::size_t m_rowId;
std::string m_address;
std::string m_label;
public:
std::string extra;
std::string getAddress() const {return m_address;}
std::string getLabel() const {return m_label;}
std::size_t getRowId() const {return m_rowId;}
};
struct Subaddress
{
virtual ~Subaddress() = 0;
virtual std::vector<SubaddressRow*> getAll() const = 0;
virtual void addRow(uint32_t accountIndex, const std::string &label) = 0;
virtual void setLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label) = 0;
virtual void refresh(uint32_t accountIndex) = 0;
};
struct SubaddressAccountRow {
public:
SubaddressAccountRow(std::size_t _rowId, const std::string &_address, const std::string &_label, const std::string &_balance, const std::string &_unlockedBalance):
m_rowId(_rowId),
m_address(_address),
m_label(_label),
m_balance(_balance),
m_unlockedBalance(_unlockedBalance) {}
private:
std::size_t m_rowId;
std::string m_address;
std::string m_label;
std::string m_balance;
std::string m_unlockedBalance;
public:
std::string extra;
std::string getAddress() const {return m_address;}
std::string getLabel() const {return m_label;}
std::string getBalance() const {return m_balance;}
std::string getUnlockedBalance() const {return m_unlockedBalance;}
std::size_t getRowId() const {return m_rowId;}
};
struct SubaddressAccount
{
virtual ~SubaddressAccount() = 0;
virtual std::vector<SubaddressAccountRow*> getAll() const = 0;
virtual void addRow(const std::string &label) = 0;
virtual void setLabel(uint32_t accountIndex, const std::string &label) = 0;
virtual void refresh() = 0;
};
struct WalletListener struct WalletListener
{ {
virtual ~WalletListener() = 0; virtual ~WalletListener() = 0;
@ -294,9 +362,13 @@ struct Wallet
//! in case error status, returns error string //! in case error status, returns error string
virtual std::string errorString() const = 0; virtual std::string errorString() const = 0;
virtual bool setPassword(const std::string &password) = 0; virtual bool setPassword(const std::string &password) = 0;
virtual std::string address() const = 0; virtual std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const = 0;
std::string mainAddress() const { return address(0, 0); }
virtual std::string path() const = 0; virtual std::string path() const = 0;
virtual bool testnet() const = 0; virtual NetworkType nettype() const = 0;
bool mainnet() const { return nettype() == MAINNET; }
bool testnet() const { return nettype() == TESTNET; }
bool stagenet() const { return nettype() == STAGENET; }
//! returns current hard fork info //! returns current hard fork info
virtual void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const = 0; virtual void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const = 0;
//! check if hard fork rules should be used //! check if hard fork rules should be used
@ -360,9 +432,12 @@ struct Wallet
* *
* \param daemon_address - daemon address in "hostname:port" format * \param daemon_address - daemon address in "hostname:port" format
* \param upper_transaction_size_limit * \param upper_transaction_size_limit
* \param daemon_username
* \param daemon_password
* \param lightWallet - start wallet in light mode, connect to a openmonero compatible server.
* \return - true on success * \return - true on success
*/ */
virtual bool init(const std::string &daemon_address, uint64_t upper_transaction_size_limit, const std::string &daemon_username = "", const std::string &daemon_password = "") = 0; virtual bool init(const std::string &daemon_address, uint64_t upper_transaction_size_limit = 0, const std::string &daemon_username = "", const std::string &daemon_password = "", bool use_ssl = false, bool lightWallet = false) = 0;
/*! /*!
* \brief createWatchOnly - Creates a watch only wallet * \brief createWatchOnly - Creates a watch only wallet
@ -406,8 +481,20 @@ struct Wallet
virtual ConnectionStatus connected() const = 0; virtual ConnectionStatus connected() const = 0;
virtual void setTrustedDaemon(bool arg) = 0; virtual void setTrustedDaemon(bool arg) = 0;
virtual bool trustedDaemon() const = 0; virtual bool trustedDaemon() const = 0;
virtual uint64_t balance() const = 0; virtual uint64_t balance(uint32_t accountIndex = 0) const = 0;
virtual uint64_t unlockedBalance() const = 0; uint64_t balanceAll() const {
uint64_t result = 0;
for (uint32_t i = 0; i < numSubaddressAccounts(); ++i)
result += balance(i);
return result;
}
virtual uint64_t unlockedBalance(uint32_t accountIndex = 0) const = 0;
uint64_t unlockedBalanceAll() const {
uint64_t result = 0;
for (uint32_t i = 0; i < numSubaddressAccounts(); ++i)
result += unlockedBalance(i);
return result;
}
/** /**
* @brief watchOnly - checks if wallet is watch only * @brief watchOnly - checks if wallet is watch only
@ -452,13 +539,28 @@ struct Wallet
static uint64_t amountFromDouble(double amount); static uint64_t amountFromDouble(double amount);
static std::string genPaymentId(); static std::string genPaymentId();
static bool paymentIdValid(const std::string &paiment_id); static bool paymentIdValid(const std::string &paiment_id);
static bool addressValid(const std::string &str, bool testnet); static bool addressValid(const std::string &str, NetworkType nettype);
static bool keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, bool testnet, std::string &error); static bool addressValid(const std::string &str, bool testnet) // deprecated
static std::string paymentIdFromAddress(const std::string &str, bool testnet); {
return addressValid(str, testnet ? TESTNET : MAINNET);
}
static bool keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, NetworkType nettype, std::string &error);
static bool keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, bool testnet, std::string &error) // deprecated
{
return keyValid(secret_key_string, address_string, isViewKey, testnet ? TESTNET : MAINNET, error);
}
static std::string paymentIdFromAddress(const std::string &str, NetworkType nettype);
static std::string paymentIdFromAddress(const std::string &str, bool testnet) // deprecated
{
return paymentIdFromAddress(str, testnet ? TESTNET : MAINNET);
}
static uint64_t maximumAllowedAmount(); static uint64_t maximumAllowedAmount();
// Easylogger wrapper // Easylogger wrapper
static void init(const char *argv0, const char *default_log_base_name); static void init(const char *argv0, const char *default_log_base_name);
static void debug(const std::string &str); static void debug(const std::string &category, const std::string &str);
static void info(const std::string &category, const std::string &str);
static void warning(const std::string &category, const std::string &str);
static void error(const std::string &category, const std::string &str);
/** /**
* @brief StartRefresh - Start/resume refresh thread (refresh every 10 seconds) * @brief StartRefresh - Start/resume refresh thread (refresh every 10 seconds)
@ -492,6 +594,39 @@ struct Wallet
*/ */
virtual int autoRefreshInterval() const = 0; virtual int autoRefreshInterval() const = 0;
/**
* @brief addSubaddressAccount - appends a new subaddress account at the end of the last major index of existing subaddress accounts
* @param label - the label for the new account (which is the as the label of the primary address (accountIndex,0))
*/
virtual void addSubaddressAccount(const std::string& label) = 0;
/**
* @brief numSubaddressAccounts - returns the number of existing subaddress accounts
*/
virtual size_t numSubaddressAccounts() const = 0;
/**
* @brief numSubaddresses - returns the number of existing subaddresses associated with the specified subaddress account
* @param accountIndex - the major index specifying the subaddress account
*/
virtual size_t numSubaddresses(uint32_t accountIndex) const = 0;
/**
* @brief addSubaddress - appends a new subaddress at the end of the last minor index of the specified subaddress account
* @param accountIndex - the major index specifying the subaddress account
* @param label - the label for the new subaddress
*/
virtual void addSubaddress(uint32_t accountIndex, const std::string& label) = 0;
/**
* @brief getSubaddressLabel - gets the label of the specified subaddress
* @param accountIndex - the major index specifying the subaddress account
* @param addressIndex - the minor index specifying the subaddress
*/
virtual std::string getSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex) const = 0;
/**
* @brief setSubaddressLabel - sets the label of the specified subaddress
* @param accountIndex - the major index specifying the subaddress account
* @param addressIndex - the minor index specifying the subaddress
* @param label - the new label for the specified subaddress
*/
virtual void setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex, const std::string &label) = 0;
/*! /*!
* \brief createTransaction creates transaction. if dst_addr is an integrated address, payment_id is ignored * \brief createTransaction creates transaction. if dst_addr is an integrated address, payment_id is ignored
@ -499,6 +634,8 @@ struct Wallet
* \param payment_id optional payment_id, can be empty string * \param payment_id optional payment_id, can be empty string
* \param amount amount * \param amount amount
* \param mixin_count mixin count. if 0 passed, wallet will use default value * \param mixin_count mixin count. if 0 passed, wallet will use default value
* \param subaddr_account subaddress account from which the input funds are taken
* \param subaddr_indices set of subaddress indices to use for transfer or sweeping. if set empty, all are chosen when sweeping, and one or more are automatically chosen when transferring. after execution, returns the set of actually used indices
* \param priority * \param priority
* \return PendingTransaction object. caller is responsible to check PendingTransaction::status() * \return PendingTransaction object. caller is responsible to check PendingTransaction::status()
* after object returned * after object returned
@ -506,7 +643,9 @@ struct Wallet
virtual PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id, virtual PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id,
optional<uint64_t> amount, uint32_t mixin_count, optional<uint64_t> amount, uint32_t mixin_count,
PendingTransaction::Priority = PendingTransaction::Priority_Low) = 0; PendingTransaction::Priority = PendingTransaction::Priority_Low,
uint32_t subaddr_account = 0,
std::set<uint32_t> subaddr_indices = {}) = 0;
/*! /*!
* \brief createSweepUnmixableTransaction creates transaction with unmixable outputs. * \brief createSweepUnmixableTransaction creates transaction with unmixable outputs.
@ -551,8 +690,10 @@ struct Wallet
virtual bool importKeyImages(const std::string &filename) = 0; virtual bool importKeyImages(const std::string &filename) = 0;
virtual TransactionHistory * history() const = 0; virtual TransactionHistory * history() = 0;
virtual AddressBook * addressBook() const = 0; virtual AddressBook * addressBook() = 0;
virtual Subaddress * subaddress() = 0;
virtual SubaddressAccount * subaddressAccount() = 0;
virtual void setListener(WalletListener *) = 0; virtual void setListener(WalletListener *) = 0;
/*! /*!
* \brief defaultMixin - returns number of mixins used in transactions * \brief defaultMixin - returns number of mixins used in transactions
@ -569,7 +710,7 @@ struct Wallet
* \brief setUserNote - attach an arbitrary string note to a txid * \brief setUserNote - attach an arbitrary string note to a txid
* \param txid - the transaction id to attach the note to * \param txid - the transaction id to attach the note to
* \param note - the note * \param note - the note
* \return true if succesful, false otherwise * \return true if successful, false otherwise
*/ */
virtual bool setUserNote(const std::string &txid, const std::string &note) = 0; virtual bool setUserNote(const std::string &txid, const std::string &note) = 0;
/*! /*!
@ -579,6 +720,17 @@ struct Wallet
*/ */
virtual std::string getUserNote(const std::string &txid) const = 0; virtual std::string getUserNote(const std::string &txid) const = 0;
virtual std::string getTxKey(const std::string &txid) const = 0; virtual std::string getTxKey(const std::string &txid) const = 0;
virtual bool checkTxKey(const std::string &txid, std::string tx_key, const std::string &address, uint64_t &received, bool &in_pool, uint64_t &confirmations) = 0;
virtual std::string getTxProof(const std::string &txid, const std::string &address, const std::string &message) const = 0;
virtual bool checkTxProof(const std::string &txid, const std::string &address, const std::string &message, const std::string &signature, bool &good, uint64_t &received, bool &in_pool, uint64_t &confirmations) = 0;
virtual std::string getSpendProof(const std::string &txid, const std::string &message) const = 0;
virtual bool checkSpendProof(const std::string &txid, const std::string &message, const std::string &signature, bool &good) const = 0;
/*!
* \brief getReserveProof - Generates a proof that proves the reserve of unspent funds
* Parameters `account_index` and `amount` are ignored when `all` is true
*/
virtual std::string getReserveProof(bool all, uint32_t account_index, uint64_t amount, const std::string &message) const = 0;
virtual bool checkReserveProof(const std::string &address, const std::string &message, const std::string &signature, bool &good, uint64_t &total, uint64_t &spent) const = 0;
/* /*
* \brief signMessage - sign a message with the spend private key * \brief signMessage - sign a message with the spend private key
@ -604,6 +756,36 @@ struct Wallet
* \return true on success * \return true on success
*/ */
virtual bool rescanSpent() = 0; virtual bool rescanSpent() = 0;
//! blackballs a set of outputs
virtual bool blackballOutputs(const std::vector<std::string> &pubkeys, bool add) = 0;
//! unblackballs an output
virtual bool unblackballOutput(const std::string &pubkey) = 0;
//! gets the ring used for a key image, if any
virtual bool getRing(const std::string &key_image, std::vector<uint64_t> &ring) const = 0;
//! gets the rings used for a txid, if any
virtual bool getRings(const std::string &txid, std::vector<std::pair<std::string, std::vector<uint64_t>>> &rings) const = 0;
//! sets the ring used for a key image
virtual bool setRing(const std::string &key_image, const std::vector<uint64_t> &ring, bool relative) = 0;
//! sets whether pre-fork outs are to be segregated
virtual void segregatePreForkOutputs(bool segregate) = 0;
//! sets the height where segregation should occur
virtual void segregationHeight(uint64_t height) = 0;
//! secondary key reuse mitigation
virtual void keyReuseMitigation2(bool mitigation) = 0;
//! Light wallet authenticate and login
virtual bool lightWalletLogin(bool &isNewWallet) const = 0;
//! Initiates a light wallet import wallet request
virtual bool lightWalletImportWalletRequest(std::string &payment_id, uint64_t &fee, bool &new_request, bool &request_fulfilled, std::string &payment_address, std::string &status) = 0;
}; };
/** /**
@ -616,84 +798,125 @@ struct WalletManager
* \brief Creates new wallet * \brief Creates new wallet
* \param path Name of wallet file * \param path Name of wallet file
* \param password Password of wallet file * \param password Password of wallet file
* \param language Language to be used to generate electrum seed memo * \param language Language to be used to generate electrum seed mnemonic
* \param nettype Network type
* \return Wallet instance (Wallet::status() needs to be called to check if created successfully) * \return Wallet instance (Wallet::status() needs to be called to check if created successfully)
*/ */
virtual Wallet * createWallet(const std::string &path, const std::string &password, const std::string &language, bool testnet = false) = 0; virtual Wallet * createWallet(const std::string &path, const std::string &password, const std::string &language, NetworkType nettype) = 0;
Wallet * createWallet(const std::string &path, const std::string &password, const std::string &language, bool testnet = false) // deprecated
{
return createWallet(path, password, language, testnet ? TESTNET : MAINNET);
}
/*! /*!
* \brief Opens existing wallet * \brief Opens existing wallet
* \param path Name of wallet file * \param path Name of wallet file
* \param password Password of wallet file * \param password Password of wallet file
* \param nettype Network type
* \return Wallet instance (Wallet::status() needs to be called to check if opened successfully) * \return Wallet instance (Wallet::status() needs to be called to check if opened successfully)
*/ */
virtual Wallet * openWallet(const std::string &path, const std::string &password, bool testnet = false) = 0; virtual Wallet * openWallet(const std::string &path, const std::string &password, NetworkType nettype) = 0;
Wallet * openWallet(const std::string &path, const std::string &password, bool testnet = false) // deprecated
{
return openWallet(path, password, testnet ? TESTNET : MAINNET);
}
/*! /*!
* \brief recovers existing wallet using memo (electrum seed) * \brief recovers existing wallet using mnemonic (electrum seed)
* \param path Name of wallet file to be created * \param path Name of wallet file to be created
* \param password Password of wallet file * \param password Password of wallet file
* \param memo memo (25 words electrum seed) * \param mnemonic mnemonic (25 words electrum seed)
* \param testnet testnet * \param nettype Network type
* \param restoreHeight restore from start height * \param restoreHeight restore from start height
* \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully) * \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully)
*/ */
virtual Wallet * recoveryWallet(const std::string &path, const std::string &password, const std::string &memo, bool testnet = false, uint64_t restoreHeight = 0) = 0; virtual Wallet * recoveryWallet(const std::string &path, const std::string &password, const std::string &mnemonic,
NetworkType nettype = MAINNET, uint64_t restoreHeight = 0) = 0;
Wallet * recoveryWallet(const std::string &path, const std::string &password, const std::string &mnemonic,
bool testnet = false, uint64_t restoreHeight = 0) // deprecated
{
return recoveryWallet(path, password, mnemonic, testnet ? TESTNET : MAINNET, restoreHeight);
}
/*! /*!
* \deprecated this method creates a wallet WITHOUT a psssphrase, use the alternate recoverWallet() method * \deprecated this method creates a wallet WITHOUT a passphrase, use the alternate recoverWallet() method
* \brief recovers existing wallet using memo (electrum seed) * \brief recovers existing wallet using mnemonic (electrum seed)
* \param path Name of wallet file to be created * \param path Name of wallet file to be created
* \param memo memo (25 words electrum seed) * \param mnemonic mnemonic (25 words electrum seed)
* \param testnet testnet * \param nettype Network type
* \param restoreHeight restore from start height * \param restoreHeight restore from start height
* \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully) * \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully)
*/ */
virtual Wallet * recoveryWallet(const std::string &path, const std::string &memo, bool testnet = false, uint64_t restoreHeight = 0) = 0; virtual Wallet * recoveryWallet(const std::string &path, const std::string &mnemonic, NetworkType nettype, uint64_t restoreHeight = 0) = 0;
Wallet * recoveryWallet(const std::string &path, const std::string &mnemonic, bool testnet = false, uint64_t restoreHeight = 0) // deprecated
{
return recoveryWallet(path, mnemonic, testnet ? TESTNET : MAINNET, restoreHeight);
}
/*! /*!
* \brief recovers existing wallet using keys. Creates a view only wallet if spend key is omitted * \brief recovers existing wallet using keys. Creates a view only wallet if spend key is omitted
* \param path Name of wallet file to be created * \param path Name of wallet file to be created
* \param password Password of wallet file * \param password Password of wallet file
* \param language language * \param language language
* \param testnet testnet * \param nettype Network type
* \param restoreHeight restore from start height * \param restoreHeight restore from start height
* \param addressString public address * \param addressString public address
* \param viewKeyString view key * \param viewKeyString view key
* \param spendKeyString spend key (optional) * \param spendKeyString spend key (optional)
* \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully) * \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully)
*/ */
virtual Wallet * createWalletWithKeys(const std::string &path, virtual Wallet * createWalletFromKeys(const std::string &path,
const std::string &password, const std::string &password,
const std::string &language, const std::string &language,
bool testnet, NetworkType nettype,
uint64_t restoreHeight, uint64_t restoreHeight,
const std::string &addressString, const std::string &addressString,
const std::string &viewKeyString, const std::string &viewKeyString,
const std::string &spendKeyString = "") = 0; const std::string &spendKeyString = "") = 0;
Wallet * createWalletFromKeys(const std::string &path,
const std::string &password,
const std::string &language,
bool testnet,
uint64_t restoreHeight,
const std::string &addressString,
const std::string &viewKeyString,
const std::string &spendKeyString = "") // deprecated
{
return createWalletFromKeys(path, password, language, testnet ? TESTNET : MAINNET, restoreHeight, addressString, viewKeyString, spendKeyString);
}
/*! /*!
* \deprecated this method creates a wallet WITHOUT a psssphrase, use createWalletWithKeys() instead * \deprecated this method creates a wallet WITHOUT a passphrase, use createWalletFromKeys(..., password, ...) instead
* \brief recovers existing wallet using keys. Creates a view only wallet if spend key is omitted * \brief recovers existing wallet using keys. Creates a view only wallet if spend key is omitted
* \param path Name of wallet file to be created * \param path Name of wallet file to be created
* \param language language * \param language language
* \param testnet testnet * \param nettype Network type
* \param restoreHeight restore from start height * \param restoreHeight restore from start height
* \param addressString public address * \param addressString public address
* \param viewKeyString view key * \param viewKeyString view key
* \param spendKeyString spend key (optional) * \param spendKeyString spend key (optional)
* \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully) * \return Wallet instance (Wallet::status() needs to be called to check if recovered successfully)
*/ */
virtual Wallet * createWalletFromKeys(const std::string &path, virtual Wallet * createWalletFromKeys(const std::string &path,
const std::string &language, const std::string &language,
bool testnet, NetworkType nettype,
uint64_t restoreHeight, uint64_t restoreHeight,
const std::string &addressString, const std::string &addressString,
const std::string &viewKeyString, const std::string &viewKeyString,
const std::string &spendKeyString = "") = 0; const std::string &spendKeyString = "") = 0;
Wallet * createWalletFromKeys(const std::string &path,
const std::string &language,
bool testnet,
uint64_t restoreHeight,
const std::string &addressString,
const std::string &viewKeyString,
const std::string &spendKeyString = "") // deprecated
{
return createWalletFromKeys(path, language, testnet ? TESTNET : MAINNET, restoreHeight, addressString, viewKeyString, spendKeyString);
}
/*! /*!
* \brief Closes wallet. In case operation succeded, wallet object deleted. in case operation failed, wallet object not deleted * \brief Closes wallet. In case operation succeeded, wallet object deleted. in case operation failed, wallet object not deleted
* \param wallet previously opened / created wallet instance * \param wallet previously opened / created wallet instance
* \return None * \return None
*/ */
@ -714,10 +937,10 @@ struct WalletManager
* @brief verifyWalletPassword - check if the given filename is the wallet * @brief verifyWalletPassword - check if the given filename is the wallet
* @param keys_file_name - location of keys file * @param keys_file_name - location of keys file
* @param password - password to verify * @param password - password to verify
* @param watch_only - verify only view keys? * @param no_spend_key - verify only view keys?
* @return - true if password is correct * @return - true if password is correct
*/ */
virtual bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool watch_only) const = 0; virtual bool verifyWalletPassword(const std::string &keys_file_name, const std::string &password, bool no_spend_key) const = 0;
/*! /*!
* \brief findWallets - searches for the wallet files by given path name recursively * \brief findWallets - searches for the wallet files by given path name recursively
@ -726,19 +949,6 @@ struct WalletManager
*/ */
virtual std::vector<std::string> findWallets(const std::string &path) = 0; virtual std::vector<std::string> findWallets(const std::string &path) = 0;
/*!
* \brief checkPayment - checks a payment was made using a txkey
* \param address - the address the payment was sent to
* \param txid - the transaction id for that payment
* \param txkey - the transaction's secret key
* \param daemon_address - the address (host and port) to the daemon to request transaction data
* \param received - if succesful, will hold the amount of monero received
* \param height - if succesful, will hold the height of the transaction (0 if only in the pool)
* \param error - if unsuccesful, will hold an error string with more information about the error
* \return - true is succesful, false otherwise
*/
virtual bool checkPayment(const std::string &address, const std::string &txid, const std::string &txkey, const std::string &daemon_address, uint64_t &received, uint64_t &height, std::string &error) const = 0;
//! returns verbose error string regarding last error; //! returns verbose error string regarding last error;
virtual std::string errorString() const = 0; virtual std::string errorString() const = 0;
@ -776,7 +986,7 @@ struct WalletManager
virtual std::string resolveOpenAlias(const std::string &address, bool &dnssec_valid) const = 0; virtual std::string resolveOpenAlias(const std::string &address, bool &dnssec_valid) const = 0;
//! checks for an update and returns version, hash and url //! checks for an update and returns version, hash and url
static std::tuple<bool, std::string, std::string, std::string, std::string> checkUpdates(const std::string &software, const std::string &subdir); static std::tuple<bool, std::string, std::string, std::string, std::string> checkUpdates(const std::string &software, std::string subdir);
}; };

View File

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View File

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

Some files were not shown because too many files have changed in this diff Show More