Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RIP-20] 12 Words and a number support for android app #87

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public class InputWordsActivity extends BRActivity {
private EditText word10;
private EditText word11;
private EditText word12;
private EditText fastRestoreKey;
private String debugPhrase;

private TextView title;
Expand Down Expand Up @@ -88,7 +89,9 @@ protected void onCreate(Bundle savedInstanceState) {
word10 = (EditText) findViewById(R.id.word10);
word11 = (EditText) findViewById(R.id.word11);
word12 = (EditText) findViewById(R.id.word12);
fastRestoreKey = (EditText) findViewById(R.id.fast_restore_key);
ImageButton faq = (ImageButton) findViewById(R.id.faq_button);
final ImageButton fast_restore_faq = (ImageButton) findViewById(R.id.fast_restore_faq);

if (Utils.isUsingCustomInputMethod(this)) {
BRDialog.showCustomDialog(this, getString(R.string.JailbreakWarnings_title), getString(R.string.Alert_customKeyboard_android),
Expand All @@ -115,6 +118,20 @@ public void onClick(View v) {
}
});

fast_restore_faq.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!BRAnimator.isClickAllowed()) return;
BRDialog.showCustomDialog(app, app.getString(R.string.RecoverWallet_fastrestore_title), app.getString(R.string.RecoverWallet_fastrestore_explanation),
app.getString(R.string.AccessibilityLabels_close), null, new BRDialogView.BROnClickListener() {
@Override
public void onClick(BRDialogView brDialogView) {
brDialogView.dismissWithAnimation();
}
}, null, null, 0);
}
});

FocusListener focusListener = new FocusListener();
word1.setOnFocusChangeListener(focusListener);
word2.setOnFocusChangeListener(focusListener);
Expand Down Expand Up @@ -163,19 +180,22 @@ public void onClick(View v) {
if (!BRAnimator.isClickAllowed()) return;
final Activity app = InputWordsActivity.this;
String phraseToCheck = getPhrase();
int fastRestoreKey = getFastRestoreKey();
if (Utils.isEmulatorOrDebug(app) && !Utils.isNullOrEmpty(debugPhrase)) {
phraseToCheck = debugPhrase;
}
if (phraseToCheck == null) {
return;
}
if (fastRestoreKey == -1) {
return;
}
String cleanPhrase = SmartValidator.cleanPaperKey(app, phraseToCheck);
if (Utils.isNullOrEmpty(cleanPhrase)) {
BRReportsManager.reportBug(new NullPointerException("cleanPhrase is null or empty!"));
return;
}
if (SmartValidator.isPaperKeyValid(app, cleanPhrase)) {

if (restore || resetPin) {
if (SmartValidator.isPaperKeyCorrect(cleanPhrase, app)) {
Utils.hideKeyboard(app);
Expand Down Expand Up @@ -232,6 +252,10 @@ public void onClick(BRDialogView brDialogView) {
m.wipeWalletButKeystore(app);
m.wipeKeyStore(app);
PostAuth.getInstance().setPhraseForKeyStore(cleanPhrase);

if(fastRestoreKey > 0) {
BRSharedPrefs.putKnownSeedTime(app, Utils.getSeedTimeFromFastRestoreKey(fastRestoreKey));
}
BRSharedPrefs.putAllowSpend(app, BRSharedPrefs.getCurrentWalletIso(app), false);
//if this screen is shown then we did not upgrade to the new app, we installed it
BRSharedPrefs.putGreetingsShown(app, true);
Expand Down Expand Up @@ -341,6 +365,33 @@ private String getPhrase() {
return w(w1) + " " + w(w2) + " " + w(w3) + " " + w(w4) + " " + w(w5) + " " + w(w6) + " " + w(w7) + " " + w(w8) + " " + w(w9) + " " + w(w10) + " " + w(w11) + " " + w(w12);
}

private int getFastRestoreKey() {
boolean success = true;

String fastRestoreKeyText = fastRestoreKey.getText().toString().toLowerCase();
int fastRestoreKeyValue = 0;

//Optional field, so empty is just 0
if(Utils.isNullOrEmpty(fastRestoreKeyText)) {
success = true;
} else {
try {
fastRestoreKeyValue = Integer.parseInt(fastRestoreKeyText);
long nowFastRestoreKey = Utils.getCurrentFastRestoreKey();
if (fastRestoreKeyValue < 0 || fastRestoreKeyValue >= nowFastRestoreKey) {
throw new RuntimeException("Invalid fast restore key");
}
} catch (Exception ex) {
SpringAnimator.failShakeAnimation(this, fastRestoreKey);
success = false;
}
}

if (!success) return -1;

return fastRestoreKeyValue;
}

private String w(String word) {
return word.replaceAll(" ", "");
}
Expand All @@ -358,6 +409,7 @@ private void clearWords() {
word10.setText("");
word11.setText("");
word12.setText("");
fastRestoreKey.setText("");
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
import android.os.Bundle;
import androidx.legacy.app.FragmentPagerAdapter;
import androidx.viewpager.widget.ViewPager;

import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.style.UnderlineSpan;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.View;
Expand All @@ -14,6 +18,7 @@
import android.widget.ImageButton;
import android.widget.TextView;

import com.google.android.gms.common.util.AndroidUtilsLight;
import com.ravenwallet.R;
import com.ravenwallet.presenter.activities.util.BRActivity;
import com.ravenwallet.presenter.customviews.BRDialogView;
Expand Down Expand Up @@ -93,9 +98,24 @@ public void onClick(View v) {
@Override
public void onClick(View v) {
updateWordView(false);

}
});
itemIndexText.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (!BRAnimator.isClickAllowed()) return;
//Only allow clicking on the final page which has the fast-restore key
if(wordViewPager.getCurrentItem() != 12) return;
BRDialog.showCustomDialog(app, app.getString(R.string.RecoverWallet_fastrestore_title), app.getString(R.string.SecurityCenter_fastrestoreExplanation),
app.getString(R.string.AccessibilityLabels_close), null, new BRDialogView.BROnClickListener() {
@Override
public void onClick(BRDialogView brDialogView) {
brDialogView.dismissWithAnimation();
}
}, null, null, 0);
}
});

String cleanPhrase = getIntent().getExtras() == null ? null : getIntent().getStringExtra("phrase");
wordMap = new SparseArray<>();

Expand All @@ -120,6 +140,7 @@ public void onClick(BRDialogView brDialogView) {
}
WordPagerAdapter adapter = new WordPagerAdapter(getFragmentManager());
adapter.setWords(wordArray);
adapter.setFastRestoreKey(Utils.getCurrentFastRestoreKey());
wordViewPager.setAdapter(adapter);
for (int i = 0; i < wordArray.length; i++) {
wordMap.append(i, wordArray[i]);
Expand All @@ -133,7 +154,7 @@ private void updateWordView(boolean isNext) {
int currentIndex = wordViewPager.getCurrentItem();
if (isNext) {
setButtonEnabled(true);
if (currentIndex >= 11) {
if (currentIndex >= wordMap.size()) {
PostAuth.getInstance().onPhraseProveAuth(this, false);
} else {
wordViewPager.setCurrentItem(currentIndex + 1);
Expand Down Expand Up @@ -170,7 +191,14 @@ protected void onPause() {
}

private void updateItemIndexText() {
String text = String.format(Locale.getDefault(), getString(R.string.WritePaperPhrase_step), wordViewPager.getCurrentItem() + 1, wordMap.size());
SpannableString text;
if(wordViewPager.getCurrentItem() == wordMap.size()){
//Last item: AKA the fast restore key, Underline it to show it is clickable
text = new SpannableString(String.format(Locale.getDefault(), getString(R.string.SecurityCenter_fastrestoreDetails)));
text.setSpan(new UnderlineSpan(), 0, text.length(), 0);
} else {
text = new SpannableString(String.format(Locale.getDefault(), getString(R.string.WritePaperPhrase_step), wordViewPager.getCurrentItem() + 1, wordMap.size()));
}
itemIndexText.setText(text);
}

Expand All @@ -184,6 +212,7 @@ public void onBackPressed() {
private class WordPagerAdapter extends FragmentPagerAdapter {

private String[] words;
private int fastRestoreKey = -1;

public WordPagerAdapter(FragmentManager fm) {
super(fm);
Expand All @@ -192,15 +221,22 @@ public WordPagerAdapter(FragmentManager fm) {
public void setWords(String[] words) {
this.words = words;
}
public void setFastRestoreKey(int fastRestoreKey) { this.fastRestoreKey = fastRestoreKey; }

@Override
public Fragment getItem(int pos) {
return FragmentPhraseWord.newInstance(words[pos]);
if(pos < words.length)
return FragmentPhraseWord.newInstance(words[pos]);
else
return FragmentPhraseWord.newInstance(Integer.toString(fastRestoreKey));
}

@Override
public int getCount() {
return words == null ? 0 : words.length;
if(words == null)
return 0;
else
return words.length + (fastRestoreKey == -1 ? 0 : 1);
}

}
Expand Down
13 changes: 13 additions & 0 deletions app/src/main/java/com/ravenwallet/tools/manager/BRSharedPrefs.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public class BRSharedPrefs {
private static final String CURRENT_WALLET_CURRENCY_CODE = "currentWalletIso";
private static final String LAST_RESCAN_MODE_USED = "lastRescanModeUsed_RVN";
private static final String LAST_SEND_TRANSACTION_BLOCK_HEIGHT = "lastSendTransactionBlockheight_RVN";
private static final String KNOWN_SEED_TIME = "knownSeedTime_RVN";
private static final String RESCAN_TIME = "rescanTime_RVN";

public interface OnIsoChangedListener {
Expand Down Expand Up @@ -278,6 +279,18 @@ public static void putLastSendTransactionBlockheight(Context activity, long bloc
editor.apply();
}

public static long getKnownSeedTime(Context activity) {
SharedPreferences prefs = activity.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
return prefs.getLong(KNOWN_SEED_TIME, 0);
}

public static void putKnownSeedTime(Context activity, long seedTime) {
SharedPreferences prefs = activity.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putLong(KNOWN_SEED_TIME, seedTime);
editor.apply();
}

public static long getFeeTime(Context activity, String iso) {
SharedPreferences prefs = activity.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
return prefs.getLong("feeTime_" + iso.toUpperCase(), 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ public void onCreateWalletAuth(Activity app, boolean authAsked) {
long start = System.currentTimeMillis();
boolean success = WalletsMaster.getInstance(app).generateRandomSeed(app);
if (success) {
//initWallets() will immediately begin sync, we must set known timestamp first
//a brand new wallet can always use the latest time, since it will start from a checkpoint anyway
BRSharedPrefs.putKnownSeedTime(app, Utils.getCurrentUnixTimestamp());
WalletsMaster.getInstance(app).initWallets(app);
Intent intent = new Intent(app, WriteDownActivity.class);
app.startActivity(intent);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,9 @@ public static void startService(Context context, String currencyCode) {
private void startSyncPolling(Context context, String walletIso) {
final BaseWalletManager walletManager = WalletsMaster.getInstance(context).getWalletByIso(context, walletIso);
if (walletManager == null) return;
final double progress = walletManager.getSyncProgress(BRSharedPrefs.getStartHeight(context,
BRSharedPrefs.getCurrentWalletIso(context)));
Log.e(TAG, "startSyncPolling: Progress:" + progress + " Wallet: " + walletIso);
long currentHeight = BRSharedPrefs.getStartHeight(context,BRSharedPrefs.getCurrentWalletIso(context));
final double progress = walletManager.getSyncProgress(currentHeight);
Log.e(TAG, "startSyncPolling: Height: " + currentHeight + " Progress:" + progress + " Wallet: " + walletIso);

if (progress > PROGRESS_START && progress < PROGRESS_FINISH) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Expand Down
4 changes: 4 additions & 0 deletions app/src/main/java/com/ravenwallet/tools/util/BRConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ public class BRConstants {
public static final long UNIQUE_FEE = 5L;
public static final long CONFIRMS_COUNT = 5L;

//The Genesis time as specified by RIP-20
public static final long GENESIS_TIMESTAMP = 1514962800;
public static final long FAST_SYNC_INTERVAL_SECONDS = 60 * 60 * 24 * 7;

/**
* Support Center article ids.
*/
Expand Down
13 changes: 13 additions & 0 deletions app/src/main/java/com/ravenwallet/tools/util/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,19 @@ public static String retrieveAddressChunk(String scanResult) {
return null;
}

public static long getCurrentUnixTimestamp() {
return System.currentTimeMillis() / 1000;
}

//Integer division will always effectively floor the results here
public static int getCurrentFastRestoreKey() {
return (int)((getCurrentUnixTimestamp() - BRConstants.GENESIS_TIMESTAMP) / BRConstants.FAST_SYNC_INTERVAL_SECONDS);
}

public static long getSeedTimeFromFastRestoreKey(int fastRestoreKey) {
return (((long)fastRestoreKey) * BRConstants.FAST_SYNC_INTERVAL_SECONDS) + BRConstants.GENESIS_TIMESTAMP;
}

public static String getIpfsUrlFromHash(Context app, String hash) {
//TODO: Hash Validation?
return String.format(BRConstants.IPFS_URL_FORMAT, BRSharedPrefs.getPreferredIPFSGateway(app), hash);
Expand Down
32 changes: 29 additions & 3 deletions app/src/main/java/com/ravenwallet/wallet/RvnWalletManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,24 @@ public synchronized static RvnWalletManager getInstance(Context app) {
// long time = 1519190488;
// int time = (int) (System.currentTimeMillis() / DateUtils.SECOND_IN_MILLIS);
long time = BRKeyStore.getWalletCreationTime(app);
if(time == 0 && BRSharedPrefs.getKnownSeedTime(app) > 0) {
//This case happens when we are a brand new restore and we have a know seed time.
//The seed time (based on week, so somewhat in-accurate) is converted to a rough block number
//The oldest checkpoint (every 4 block-months) before 'time' is used as a starting point
//The estimated height is used to determine the point at which to start downloading full blocks
long seedTime = BRSharedPrefs.getKnownSeedTime(app);
long currentTime = System.currentTimeMillis() / 1000;

if(seedTime > 0 && seedTime < currentTime){
time = seedTime;
Log.d(TAG, "getInstance: resuming from known block time: " + time);
long estimatedHeight = (time - BRConstants.GENESIS_TIMESTAMP) / (60);
BRSharedPrefs.putStartHeight(app, BRSharedPrefs.getCurrentWalletIso(app), estimatedHeight);
} else {
//Invalid timestamp used. Ignore
time = 0;
}
}

instance = new RvnWalletManager(app, pubKey, BuildConfig.TESTNET ? BRCoreChainParams.testnetChainParams : BRCoreChainParams.mainnetChainParams, time);
}
Expand Down Expand Up @@ -162,13 +180,18 @@ private RvnWalletManager(final Context app, BRCoreMasterPubKey masterPubKey,
BREventManager.getInstance().pushEvent("wallet.didUseDefaultFeePerKB");
}
getWallet().setFeePerKb(BRSharedPrefs.getFavorStandardFee(app, getIso(app)) ? fee : economyFee);
if (BRSharedPrefs.getStartHeight(app, getIso(app)) == 0)
if (BRSharedPrefs.getStartHeight(app, getIso(app)) == 0) {
BRExecutor.getInstance().forLightWeightBackgroundTasks().execute(new Runnable() {
@Override
public void run() {
BRSharedPrefs.putStartHeight(app, getIso(app), getPeerManager().getLastBlockHeight());
long lastBlockHeight = getPeerManager().getLastBlockHeight();
Log.d(TAG, "connectWallet: starting from last block height " + lastBlockHeight);
BRSharedPrefs.putStartHeight(app, getIso(app), lastBlockHeight);
}
});
} else {
Log.d(TAG, "connectWallet: starting from block " + BRSharedPrefs.getStartHeight(app, getIso(app)));
}

WalletsMaster.getInstance(app).updateFixedPeer(app, this);
// balanceListeners = new ArrayList<>();
Expand Down Expand Up @@ -802,7 +825,10 @@ public void syncStopped(final String error) {
BRExecutor.getInstance().forMainThreadTasks().execute(new Runnable() {
@Override
public void run() {
Toast.makeText(app, "SyncStopped " + getIso(app) + " err(" + error + ") ", Toast.LENGTH_LONG).show();
if (Utils.isNullOrEmpty(error))
Toast.makeText(app, "SyncStopped " + getIso(app), Toast.LENGTH_LONG).show();
else
Toast.makeText(app, "SyncStopped " + getIso(app) + " err(" + error + ") ", Toast.LENGTH_LONG).show();
}
});

Expand Down
Loading