mirror of
https://github.com/m2049r/xmrwallet
synced 2025-09-05 09:58:42 +02:00
Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
bea4b06675 | ||
![]() |
88bc33b5a4 | ||
![]() |
2db36bb824 | ||
![]() |
dd689b1883 | ||
![]() |
641abd13f5 | ||
![]() |
a0b3a7fe5d | ||
![]() |
a0486f581f | ||
![]() |
36161137ec | ||
![]() |
c9927edbd1 |
27
.circleci/config.yml
Normal file
27
.circleci/config.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
working_directory: ~/code
|
||||
docker:
|
||||
- image: bitriseio/android-ndk
|
||||
environment:
|
||||
JVM_OPTS: -Xmx3200m
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }}
|
||||
- run:
|
||||
name: Download Dependencies
|
||||
command: ./gradlew androidDependencies
|
||||
- save_cache:
|
||||
paths:
|
||||
- ~/.gradle
|
||||
key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }}
|
||||
- run:
|
||||
name: Run Tests
|
||||
command: ./gradlew test
|
||||
- store_artifacts:
|
||||
path: app/build/reports
|
||||
destination: reports
|
||||
- store_test_results:
|
||||
path: app/build/test-results
|
@@ -8,8 +8,8 @@ android {
|
||||
applicationId "com.m2049r.xmrwallet"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 25
|
||||
versionCode 68
|
||||
versionName "1.3.8 'Satoshis Dream'"
|
||||
versionCode 72
|
||||
versionName "1.3.12 'Satoshis Dream'"
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
@@ -36,6 +36,15 @@ android {
|
||||
path "CMakeLists.txt"
|
||||
}
|
||||
}
|
||||
|
||||
splits {
|
||||
abi {
|
||||
enable true
|
||||
reset()
|
||||
include "armeabi-v7a"
|
||||
universalApk false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -50,6 +59,8 @@ dependencies {
|
||||
compile "com.squareup.okhttp3:okhttp:$rootProject.ext.okHttpVersion"
|
||||
compile "com.jakewharton.timber:timber:$rootProject.ext.timberVersion"
|
||||
|
||||
compile 'com.nulab-inc:zxcvbn:1.2.3'
|
||||
|
||||
testCompile "junit:junit:$rootProject.ext.junitVersion"
|
||||
testCompile "org.mockito:mockito-all:$rootProject.ext.mockitoVersion"
|
||||
testCompile "com.squareup.okhttp3:mockwebserver:$rootProject.ext.okHttpVersion"
|
||||
|
@@ -21,7 +21,9 @@ import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.design.widget.TextInputLayout;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.text.Editable;
|
||||
import android.text.InputType;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
@@ -32,12 +34,18 @@ import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.m2049r.xmrwallet.util.RestoreHeight;
|
||||
import com.m2049r.xmrwallet.widget.Toolbar;
|
||||
import com.m2049r.xmrwallet.model.Wallet;
|
||||
import com.m2049r.xmrwallet.model.WalletManager;
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
import com.nulabinc.zxcvbn.Strength;
|
||||
import com.nulabinc.zxcvbn.Zxcvbn;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
@@ -125,7 +133,7 @@ public class GenerateFragment extends Fragment {
|
||||
});
|
||||
|
||||
Helper.showKeyboard(getActivity());
|
||||
//##############
|
||||
|
||||
etWalletName.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_NEXT)) {
|
||||
@@ -231,9 +239,7 @@ public class GenerateFragment extends Fragment {
|
||||
}
|
||||
if (!type.equals(TYPE_NEW)) {
|
||||
etWalletRestoreHeight.setVisibility(View.VISIBLE);
|
||||
etWalletRestoreHeight.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener()
|
||||
|
||||
{
|
||||
etWalletRestoreHeight.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_DONE)) {
|
||||
Helper.hideKeyboard(getActivity());
|
||||
@@ -254,11 +260,61 @@ public class GenerateFragment extends Fragment {
|
||||
}
|
||||
});
|
||||
|
||||
etWalletPassword.getEditText().addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
checkPassword();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
}
|
||||
});
|
||||
|
||||
etWalletName.requestFocus();
|
||||
initZxcvbn();
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
Zxcvbn zxcvbn = new Zxcvbn();
|
||||
|
||||
// initialize zxcvbn engine in background thread
|
||||
private void initZxcvbn() {
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
zxcvbn.measure("");
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void checkPassword() {
|
||||
String password = etWalletPassword.getEditText().getText().toString();
|
||||
if (!password.isEmpty()) {
|
||||
Strength strength = zxcvbn.measure(password);
|
||||
int msg;
|
||||
double guessesLog10 = strength.getGuessesLog10();
|
||||
if (guessesLog10 < 10)
|
||||
msg = R.string.password_weak;
|
||||
else if (guessesLog10 < 11)
|
||||
msg = R.string.password_fair;
|
||||
else if (guessesLog10 < 12)
|
||||
msg = R.string.password_good;
|
||||
else if (guessesLog10 < 13)
|
||||
msg = R.string.password_strong;
|
||||
else
|
||||
msg = R.string.password_very_strong;
|
||||
etWalletPassword.setError(getResources().getString(msg));
|
||||
} else {
|
||||
etWalletPassword.setError(null);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkName() {
|
||||
String name = etWalletName.getEditText().getText().toString();
|
||||
boolean ok = true;
|
||||
@@ -281,6 +337,42 @@ public class GenerateFragment extends Fragment {
|
||||
return ok;
|
||||
}
|
||||
|
||||
private boolean checkHeight() {
|
||||
long height = !type.equals(TYPE_NEW) ? getHeight() : 0;
|
||||
boolean ok = true;
|
||||
if (height < 0) {
|
||||
etWalletRestoreHeight.setError(getString(R.string.generate_restoreheight_error));
|
||||
ok = false;
|
||||
}
|
||||
if (ok) {
|
||||
etWalletRestoreHeight.setError(null);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
private long getHeight() {
|
||||
long height = 0;
|
||||
|
||||
String restoreHeight = etWalletRestoreHeight.getEditText().getText().toString().trim();
|
||||
if (restoreHeight.isEmpty()) return -1;
|
||||
try {
|
||||
// is it a date?
|
||||
SimpleDateFormat parser = new SimpleDateFormat("yyyy-MM-dd");
|
||||
parser.setLenient(false);
|
||||
parser.parse(restoreHeight);
|
||||
height = RestoreHeight.getInstance().getHeight(restoreHeight);
|
||||
} catch (ParseException exPE) {
|
||||
try {
|
||||
// or is it a height?
|
||||
height = Long.parseLong(restoreHeight);
|
||||
} catch (NumberFormatException exNFE) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
Timber.d("Using Restore Height = %d", height);
|
||||
return height;
|
||||
}
|
||||
|
||||
private boolean checkMnemonic() {
|
||||
String seed = etWalletMnemonic.getEditText().getText().toString();
|
||||
boolean ok = (seed.split("\\s").length == 25); // 25 words
|
||||
@@ -327,15 +419,13 @@ public class GenerateFragment extends Fragment {
|
||||
|
||||
private void generateWallet() {
|
||||
if (!checkName()) return;
|
||||
if (!checkHeight()) return;
|
||||
|
||||
String name = etWalletName.getEditText().getText().toString();
|
||||
String password = etWalletPassword.getEditText().getText().toString();
|
||||
|
||||
long height;
|
||||
try {
|
||||
height = Long.parseLong(etWalletRestoreHeight.getEditText().getText().toString());
|
||||
} catch (NumberFormatException ex) {
|
||||
height = 0; // Keep calm and carry on!
|
||||
}
|
||||
long height = getHeight();
|
||||
if (height < 0) height = 0;
|
||||
|
||||
if (type.equals(TYPE_NEW)) {
|
||||
bGenerate.setEnabled(false);
|
||||
|
@@ -141,7 +141,7 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
Timber.d("onDestroy()");
|
||||
if (!isSynced()) {
|
||||
if ((mBoundService != null) && !isSynced() && (getWallet() != null)) {
|
||||
saveWallet();
|
||||
}
|
||||
stopWalletService();
|
||||
|
@@ -34,10 +34,11 @@ public class BitcoinAddressValidator {
|
||||
if (decoded == null)
|
||||
return false;
|
||||
|
||||
int v = decoded[0] & 0xFF;
|
||||
if (!testnet) {
|
||||
if ((decoded[0] != 0x00) && (decoded[0] != 0x05)) return false;
|
||||
if ((v != 0x00) && (v != 0x05)) return false;
|
||||
} else {
|
||||
if ((decoded[0] != 0x6f) && (decoded[0] != 0xc4)) return false;
|
||||
if ((v != 0x6f) && (v != 0xc4)) return false;
|
||||
}
|
||||
|
||||
byte[] hash1 = sha256(Arrays.copyOfRange(decoded, 0, 21));
|
||||
@@ -57,7 +58,11 @@ public class BitcoinAddressValidator {
|
||||
|
||||
byte[] result = new byte[25];
|
||||
byte[] numBytes = num.toByteArray();
|
||||
System.arraycopy(numBytes, 0, result, result.length - numBytes.length, numBytes.length);
|
||||
if (num.bitLength() == 200) {
|
||||
System.arraycopy(numBytes, 1, result, 0, 25);
|
||||
} else {
|
||||
System.arraycopy(numBytes, 0, result, result.length - numBytes.length, numBytes.length);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
152
app/src/main/java/com/m2049r/xmrwallet/util/RestoreHeight.java
Normal file
152
app/src/main/java/com/m2049r/xmrwallet/util/RestoreHeight.java
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class RestoreHeight {
|
||||
static private RestoreHeight Singleton = null;
|
||||
|
||||
static public RestoreHeight getInstance() {
|
||||
if (Singleton == null) {
|
||||
synchronized (RestoreHeight.class) {
|
||||
if (Singleton == null) {
|
||||
Singleton = new RestoreHeight();
|
||||
}
|
||||
}
|
||||
}
|
||||
return Singleton;
|
||||
}
|
||||
|
||||
private Map<String, Long> blockheight = new HashMap<>();
|
||||
|
||||
RestoreHeight() {
|
||||
blockheight.put("2014-05-01", 18844L);
|
||||
blockheight.put("2014-06-01", 65406L);
|
||||
blockheight.put("2014-07-01", 108882L);
|
||||
blockheight.put("2014-08-01", 153594L);
|
||||
blockheight.put("2014-09-01", 198072L);
|
||||
blockheight.put("2014-10-01", 241088L);
|
||||
blockheight.put("2014-11-01", 285305L);
|
||||
blockheight.put("2014-12-01", 328069L);
|
||||
blockheight.put("2015-01-01", 372369L);
|
||||
blockheight.put("2015-02-01", 416505L);
|
||||
blockheight.put("2015-03-01", 456631L);
|
||||
blockheight.put("2015-04-01", 501084L);
|
||||
blockheight.put("2015-05-01", 543973L);
|
||||
blockheight.put("2015-06-01", 588326L);
|
||||
blockheight.put("2015-07-01", 631187L);
|
||||
blockheight.put("2015-08-01", 675484L);
|
||||
blockheight.put("2015-09-01", 719725L);
|
||||
blockheight.put("2015-10-01", 762463L);
|
||||
blockheight.put("2015-11-01", 806528L);
|
||||
blockheight.put("2015-12-01", 849041L);
|
||||
blockheight.put("2016-01-01", 892866L);
|
||||
blockheight.put("2016-02-01", 936736L);
|
||||
blockheight.put("2016-03-01", 977691L);
|
||||
blockheight.put("2016-04-01", 1015848L);
|
||||
blockheight.put("2016-05-01", 1037417L);
|
||||
blockheight.put("2016-06-01", 1059651L);
|
||||
blockheight.put("2016-07-01", 1081269L);
|
||||
blockheight.put("2016-08-01", 1103630L);
|
||||
blockheight.put("2016-09-01", 1125983L);
|
||||
blockheight.put("2016-10-01", 1147617L);
|
||||
blockheight.put("2016-11-01", 1169779L);
|
||||
blockheight.put("2016-12-01", 1191402L);
|
||||
blockheight.put("2017-01-01", 1213861L);
|
||||
blockheight.put("2017-02-01", 1236197L);
|
||||
blockheight.put("2017-03-01", 1256358L);
|
||||
blockheight.put("2017-04-01", 1278622L);
|
||||
blockheight.put("2017-05-01", 1300239L);
|
||||
blockheight.put("2017-06-01", 1322564L);
|
||||
blockheight.put("2017-07-01", 1344225L);
|
||||
blockheight.put("2017-08-01", 1366664L);
|
||||
blockheight.put("2017-09-01", 1389113L);
|
||||
blockheight.put("2017-10-01", 1410738L);
|
||||
blockheight.put("2017-11-01", 1433039L);
|
||||
blockheight.put("2017-12-01", 1454639L);
|
||||
blockheight.put("2018-01-01", 1477201L);
|
||||
blockheight.put("2018-02-01", 1499599L);
|
||||
}
|
||||
|
||||
public long getHeight(String date) {
|
||||
SimpleDateFormat parser = new SimpleDateFormat("yyyy-MM-dd");
|
||||
parser.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
parser.setLenient(false);
|
||||
try {
|
||||
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
|
||||
cal.set(Calendar.DST_OFFSET, 0);
|
||||
cal.setTime(parser.parse(date));
|
||||
cal.add(Calendar.DAY_OF_MONTH, -4); // give it some leeway
|
||||
if (cal.get(Calendar.YEAR) < 2014)
|
||||
return 1;
|
||||
if ((cal.get(Calendar.YEAR) == 2014) && (cal.get(Calendar.MONTH) <= 3))
|
||||
// before May 2014
|
||||
return 1;
|
||||
|
||||
Calendar query = (Calendar) cal.clone();
|
||||
|
||||
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
|
||||
formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
cal.set(Calendar.DAY_OF_MONTH, 1);
|
||||
long prevTime = cal.getTimeInMillis();
|
||||
String prevDate = formatter.format(prevTime);
|
||||
// lookup blockheight at first of the month
|
||||
Long prevBc = blockheight.get(prevDate);
|
||||
if (prevBc == null) {
|
||||
// if too recent, go back in time and find latest one we have
|
||||
while (prevBc == null) {
|
||||
cal.add(Calendar.MONTH, -1);
|
||||
if (cal.get(Calendar.YEAR) < 2014) {
|
||||
throw new IllegalStateException("endless loop looking for blockheight");
|
||||
}
|
||||
prevTime = cal.getTimeInMillis();
|
||||
prevDate = formatter.format(prevTime);
|
||||
prevBc = blockheight.get(prevDate);
|
||||
}
|
||||
}
|
||||
long height = prevBc;
|
||||
// now we have a blockheight & a date ON or BEFORE the restore date requested
|
||||
if (date.equals(prevDate)) return height;
|
||||
// see if we have a blockheight after this date
|
||||
cal.add(Calendar.MONTH, 1);
|
||||
long nextTime = cal.getTimeInMillis();
|
||||
String nextDate = formatter.format(nextTime);
|
||||
Long nextBc = blockheight.get(nextDate);
|
||||
if (nextBc != null) { // we have a range - interpolate the blockheight we are looking for
|
||||
long diff = nextBc - prevBc;
|
||||
long diffDays = TimeUnit.DAYS.convert(nextTime - prevTime, TimeUnit.MILLISECONDS);
|
||||
long days = TimeUnit.DAYS.convert(query.getTimeInMillis() - prevTime,
|
||||
TimeUnit.MILLISECONDS);
|
||||
height = Math.round(prevBc + diff * (1.0 * days / diffDays));
|
||||
} else {
|
||||
long days = TimeUnit.DAYS.convert(query.getTimeInMillis() - prevTime,
|
||||
TimeUnit.MILLISECONDS);
|
||||
height = Math.round(prevBc + 1.0 * days * (24 * 60 / 2));
|
||||
}
|
||||
return height;
|
||||
} catch (ParseException ex) {
|
||||
throw new IllegalArgumentException(ex);
|
||||
}
|
||||
}
|
||||
}
|
@@ -144,7 +144,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/generate_restoreheight_hint"
|
||||
android:imeOptions="actionDone"
|
||||
android:inputType="number"
|
||||
android:inputType="date"
|
||||
android:textAlignment="textStart" />
|
||||
</android.support.design.widget.TextInputLayout>
|
||||
|
||||
|
@@ -113,7 +113,8 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_marginBottom="88dp"
|
||||
android:layout_marginEnd="16dp">
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/fabSeedT"
|
||||
|
@@ -18,8 +18,6 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center"
|
||||
android:tag="1"
|
||||
android:text="1" />
|
||||
|
||||
@@ -29,8 +27,6 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center"
|
||||
android:tag="2"
|
||||
android:text="2" />
|
||||
|
||||
@@ -40,8 +36,6 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center"
|
||||
android:tag="3"
|
||||
android:text="3" />
|
||||
</LinearLayout>
|
||||
@@ -50,7 +44,6 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
|
||||
android:gravity="center">
|
||||
|
||||
<TextView
|
||||
@@ -59,8 +52,6 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center"
|
||||
android:tag="4"
|
||||
android:text="4" />
|
||||
|
||||
@@ -70,8 +61,6 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center"
|
||||
android:tag="5"
|
||||
android:text="5" />
|
||||
|
||||
@@ -81,8 +70,6 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center"
|
||||
android:tag="6"
|
||||
android:text="6" />
|
||||
</LinearLayout>
|
||||
@@ -99,8 +86,6 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center"
|
||||
android:tag="7"
|
||||
android:text="7" />
|
||||
|
||||
@@ -110,8 +95,6 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center"
|
||||
android:tag="8"
|
||||
android:text="8" />
|
||||
|
||||
@@ -121,8 +104,6 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center"
|
||||
android:tag="9"
|
||||
android:text="9" />
|
||||
</LinearLayout>
|
||||
@@ -139,8 +120,6 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center"
|
||||
android:text="." />
|
||||
|
||||
<TextView
|
||||
@@ -149,8 +128,6 @@
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center"
|
||||
android:tag="0"
|
||||
android:text="0" />
|
||||
|
||||
@@ -160,7 +137,7 @@
|
||||
android:layout_height="36sp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:src="@drawable/ic_backspace_black_36dp" />
|
||||
</LinearLayout>
|
||||
|
||||
|
@@ -13,6 +13,12 @@
|
||||
<string name="menu_archive">Archivar</string>
|
||||
<string name="menu_backup">Copia de seguridad</string>
|
||||
|
||||
<string name="password_weak">Sigue escribiendo …</string>
|
||||
<string name="password_fair">Mas o menos.</string>
|
||||
<string name="password_good">Puedes hacerlo mejor.</string>
|
||||
<string name="password_strong">Casi …</string>
|
||||
<string name="password_very_strong">¡Bien ahí, hacker nivel 4!</string>
|
||||
|
||||
<string name="label_login_wallets">Monederos</string>
|
||||
<string name="label_donate">Donar</string>
|
||||
<string name="label_ok">Aceptar</string>
|
||||
@@ -126,7 +132,7 @@
|
||||
|
||||
<string name="generate_title">Crear monedero</string>
|
||||
<string name="generate_name_hint">Nombre del monedero</string>
|
||||
<string name="generate_password_hint">Contraseña del monedero</string>
|
||||
<string name="generate_password_hint">Frase de Contraseña</string>
|
||||
<string name="generate_buttonGenerate">¡Házme ya un monedero!</string>
|
||||
<string name="generate_seed">Semilla Mnemotécnica</string>
|
||||
<string name="generate_button_accept">¡He apuntado estas 25 palabras!</string>
|
||||
@@ -142,6 +148,8 @@
|
||||
<string name="generate_wallet_created">Monedero creada</string>
|
||||
<string name="generate_wallet_create_failed">Creación de monedero fallida</string>
|
||||
|
||||
<string name="generate_restoreheight_error">Introduce un número o una fecha (AAAA-MM-DD)</string>
|
||||
|
||||
<string name="generate_wallet_type_key">Claves</string>
|
||||
<string name="generate_wallet_type_new">Nuevo</string>
|
||||
<string name="generate_wallet_type_seed">Semilla</string>
|
||||
@@ -151,7 +159,7 @@
|
||||
<string name="generate_viewkey_hint">Clave de Vista</string>
|
||||
<string name="generate_spendkey_hint">Clave de Gasto</string>
|
||||
<string name="generate_mnemonic_hint">Semilla Mnemotécnica de 25 Palabras</string>
|
||||
<string name="generate_restoreheight_hint">Altura de Restauración</string>
|
||||
<string name="generate_restoreheight_hint">Altura o Fecha (YYYY-MM-DD) de Restauración</string>
|
||||
|
||||
<string name="generate_wallet_label">Monedero</string>
|
||||
<string name="generate_password_label">Contraseña</string>
|
||||
@@ -328,5 +336,5 @@
|
||||
<string name="receive_bitcoin_paymentid_invalid">Debe quedar vacío con una dirección Bitcoin</string>
|
||||
<string name="about_whoami">Soy monerujo</string>
|
||||
<string name="info_send_xmrto_success_order_label">Orden XMR.TO</string>
|
||||
<string name="info_xmrto_enabled">Pago de BTC posible, toque para más info.</string>
|
||||
<string name="info_xmrto_enabled">Pago en BTC activado, toca para más info.</string>
|
||||
</resources>
|
||||
|
@@ -15,6 +15,12 @@
|
||||
<string name="menu_archive">Archive</string>
|
||||
<string name="menu_backup">Backup</string>
|
||||
|
||||
<string name="password_weak">Continue typing …</string>
|
||||
<string name="password_fair">Meh …</string>
|
||||
<string name="password_good">C\'mon, you can do better!</string>
|
||||
<string name="password_strong">Getting there …</string>
|
||||
<string name="password_very_strong">Yeah baby, h4x0r style!</string>
|
||||
|
||||
<string name="label_login_wallets">Wallets</string>
|
||||
<string name="label_donate">Donate</string>
|
||||
<string name="label_ok">OK</string>
|
||||
@@ -197,7 +203,7 @@
|
||||
|
||||
<string name="generate_title">Create Wallet</string>
|
||||
<string name="generate_name_hint">Wallet Name</string>
|
||||
<string name="generate_password_hint">Wallet Password</string>
|
||||
<string name="generate_password_hint">Wallet Passphrase</string>
|
||||
<string name="generate_buttonGenerate">Make me a wallet already!</string>
|
||||
<string name="generate_seed">Mnemonic Seed</string>
|
||||
<string name="generate_button_accept">I have noted these 25 words!</string>
|
||||
@@ -213,6 +219,8 @@
|
||||
<string name="generate_wallet_created">Wallet created</string>
|
||||
<string name="generate_wallet_create_failed">Wallet create failed</string>
|
||||
|
||||
<string name="generate_restoreheight_error">Enter Number or Date (YYYY-MM-DD)</string>
|
||||
|
||||
<string name="generate_wallet_type_key">Keys</string>
|
||||
<string name="generate_wallet_type_new">New</string>
|
||||
<string name="generate_wallet_type_seed">Seed</string>
|
||||
@@ -222,7 +230,7 @@
|
||||
<string name="generate_viewkey_hint">View Key</string>
|
||||
<string name="generate_spendkey_hint">Spend Key</string>
|
||||
<string name="generate_mnemonic_hint">25-Word Mnemonic Seed</string>
|
||||
<string name="generate_restoreheight_hint">Restore Height</string>
|
||||
<string name="generate_restoreheight_hint">Restore Height or Date (YYYY-MM-DD)</string>
|
||||
|
||||
<string name="generate_wallet_label">Wallet</string>
|
||||
<string name="generate_password_label">Password</string>
|
||||
|
@@ -119,6 +119,8 @@
|
||||
<style name="MoneroLabel.NumPad">
|
||||
<item name="android:textSize">36sp</item>
|
||||
<item name="android:textColor">@color/moneroBlack</item>
|
||||
<item name="android:background">?android:attr/selectableItemBackgroundBorderless</item>
|
||||
<item name="android:gravity">center</item>
|
||||
</style>
|
||||
|
||||
<style name="MoneroLabel.Title">
|
||||
|
@@ -25,6 +25,7 @@ public class BitcoinAddressValidatorTest {
|
||||
|
||||
@Test
|
||||
public void validateBTC_shouldValidate() {
|
||||
assertTrue(BitcoinAddressValidator.validate("2N9fzq66uZYQXp7uqrPBH6jKBhjrgTzpGCy", true));
|
||||
assertTrue(BitcoinAddressValidator.validate("1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", false));
|
||||
assertTrue(BitcoinAddressValidator.validate("1Q1pE5vPGEEMqRcVRMbtBK842Y6Pzo6nK9", false));
|
||||
assertTrue(BitcoinAddressValidator.validate("3R2MPpTNQLCNs13qnHz89Rm82jQ27bAwft", false));
|
||||
|
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright (c) 2017 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.util;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
// all ranges go back 5 days
|
||||
|
||||
public class RestoreHeightTest {
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void pre2014() {
|
||||
assertTrue(getHeight("2013-12-01") == 1);
|
||||
assertTrue(getHeight("1958-12-01") == 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void zero() {
|
||||
assertTrue(getHeight("2014-04-27") == 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notZero() {
|
||||
assertTrue(getHeight("2014-05-07") > 1);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void notDateA() {
|
||||
getHeight("2013-13-04");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void notDateB() {
|
||||
getHeight("2013-13-01-");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void notDateC() {
|
||||
getHeight("x013-13-01");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void notDateD() {
|
||||
getHeight("2013-12-41");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test201709() {
|
||||
// getHeight() returns blockheight of < two days ago
|
||||
assertTrue(isInRange(getHeight("2017-09-01"), 1383957, 1387716));
|
||||
assertTrue(isInRange(getHeight("2017-09-05"), 1386967, 1390583));
|
||||
assertTrue(isInRange(getHeight("2017-09-21"), 1398492, 1402068));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test20160324() { // blocktime changed from 1 minute to 2 minutes on this day
|
||||
assertTrue(isInRange(getHeight("2016-03-23"), 998955, 1006105));
|
||||
assertTrue(isInRange(getHeight("2016-03-24"), 1000414, 1007486));
|
||||
assertTrue(isInRange(getHeight("2016-03-25"), 1001800, 1008900));
|
||||
assertTrue(isInRange(getHeight("2016-03-26"), 1003243, 1009985));
|
||||
assertTrue(isInRange(getHeight("2016-03-27"), 1004694, 1010746));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test2014() {
|
||||
assertTrue(isInRange(getHeight("2014-04-26"), 1, 8501));
|
||||
assertTrue(isInRange(getHeight("2014-05-09"), 20289, 28311));
|
||||
assertTrue(isInRange(getHeight("2014-05-17"), 32608, 40075));
|
||||
assertTrue(isInRange(getHeight("2014-05-30"), 52139, 59548));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test2015() {
|
||||
assertTrue(isInRange(getHeight("2015-01-26"), 397914, 405055));
|
||||
assertTrue(isInRange(getHeight("2015-08-13"), 682595, 689748));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test2016() {
|
||||
assertTrue(isInRange(getHeight("2016-01-26"), 918313, 925424));
|
||||
assertTrue(isInRange(getHeight("2016-08-13"), 1107244, 1110793));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test2017() {
|
||||
assertTrue(isInRange(getHeight("2017-01-26"), 1226806, 1230402));
|
||||
assertTrue(isInRange(getHeight("2017-08-13"), 1370264, 1373854));
|
||||
assertTrue(isInRange(getHeight("2017-08-31"), 1383254, 1386967));
|
||||
assertTrue(isInRange(getHeight("2017-06-09"), 1323288, 1326884));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void post201802() {
|
||||
assertTrue(isInRange(getHeight("2018-02-19"), 1507579, 1511127));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postFuture() {
|
||||
long b_20180208 = 1504715;
|
||||
long b_20180808 = b_20180208 + 720 * (28 + 31 + 30 + 31 + 30 + 31);
|
||||
assertTrue(isInRange(getHeight("2018-08-08"), b_20180808 - 720 * 5, b_20180808));
|
||||
}
|
||||
|
||||
|
||||
private boolean isInRange(long n, long min, long max) {
|
||||
if (n > max) return false;
|
||||
if (n < min) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private long getHeight(String date) {
|
||||
return RestoreHeight.getInstance().getHeight(date);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user