From 90d346ff7498bbc21481d156b567174ef7b91c17 Mon Sep 17 00:00:00 2001 From: Ben Abraham Date: Sun, 4 Apr 2021 02:56:10 -0400 Subject: [PATCH 1/4] First pass on RIP-20. Added BR Shared pref for storing known seed timestamp. When starting a fresh sync we will accept checkpoints up to the timestamp of the BR Shared pref Added checkpoints at 4 block-week intervals (roughly one block-month), up to the latest time. Also removed a few duplicate checkpoints. Currently no UI for reading out or inputting the code yet. --- .../activities/InputWordsActivity.java | 2 ++ .../tools/manager/BRSharedPrefs.java | 13 ++++++++++ .../tools/services/SyncService.java | 6 ++--- .../ravenwallet/wallet/RvnWalletManager.java | 24 ++++++++++++++--- app/src/main/jni/core/BRPeerManager.c | 26 ++++++++++++++----- 5 files changed, 58 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/ravenwallet/presenter/activities/InputWordsActivity.java b/app/src/main/java/com/ravenwallet/presenter/activities/InputWordsActivity.java index 40b53edde..2e305f05a 100644 --- a/app/src/main/java/com/ravenwallet/presenter/activities/InputWordsActivity.java +++ b/app/src/main/java/com/ravenwallet/presenter/activities/InputWordsActivity.java @@ -232,6 +232,8 @@ public void onClick(BRDialogView brDialogView) { m.wipeWalletButKeystore(app); m.wipeKeyStore(app); PostAuth.getInstance().setPhraseForKeyStore(cleanPhrase); + BRSharedPrefs.putKnownSeedTime(app, 1_615_000_000); ////Calculate for realz: DEC 09 2020 + //BRSharedPrefs.putKnownSeedHeight(app, 1_500_000); 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); diff --git a/app/src/main/java/com/ravenwallet/tools/manager/BRSharedPrefs.java b/app/src/main/java/com/ravenwallet/tools/manager/BRSharedPrefs.java index 3873182ed..cf4cbc8c6 100644 --- a/app/src/main/java/com/ravenwallet/tools/manager/BRSharedPrefs.java +++ b/app/src/main/java/com/ravenwallet/tools/manager/BRSharedPrefs.java @@ -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 { @@ -266,6 +267,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); diff --git a/app/src/main/java/com/ravenwallet/tools/services/SyncService.java b/app/src/main/java/com/ravenwallet/tools/services/SyncService.java index eda9ceab1..35500890a 100644 --- a/app/src/main/java/com/ravenwallet/tools/services/SyncService.java +++ b/app/src/main/java/com/ravenwallet/tools/services/SyncService.java @@ -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); diff --git a/app/src/main/java/com/ravenwallet/wallet/RvnWalletManager.java b/app/src/main/java/com/ravenwallet/wallet/RvnWalletManager.java index 92d7cd59f..98e8eb121 100644 --- a/app/src/main/java/com/ravenwallet/wallet/RvnWalletManager.java +++ b/app/src/main/java/com/ravenwallet/wallet/RvnWalletManager.java @@ -135,6 +135,16 @@ 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) { + //Not resuming from existing wallet + //Check if we have a know checkpoint we should use instead (based on known seed block height) + time = BRSharedPrefs.getKnownSeedTime(app); + Log.d(TAG, "getInstance: resuming from known block time: " + time); + long estimatedHeight = (time - 1514962800) / (60); + //1514962800 is the KAWPOW epoch, divide by 60 to get est. block height from time + + BRSharedPrefs.putStartHeight(app, BRSharedPrefs.getCurrentWalletIso(app), estimatedHeight + 100); + } instance = new RvnWalletManager(app, pubKey, BuildConfig.TESTNET ? BRCoreChainParams.testnetChainParams : BRCoreChainParams.mainnetChainParams, time); } @@ -162,13 +172,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<>(); @@ -802,7 +817,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(); } }); diff --git a/app/src/main/jni/core/BRPeerManager.c b/app/src/main/jni/core/BRPeerManager.c index 8f05ad960..5c8c645b5 100755 --- a/app/src/main/jni/core/BRPeerManager.c +++ b/app/src/main/jni/core/BRPeerManager.c @@ -71,15 +71,27 @@ static const struct { uint32_t height; const char *hash; uint32_t timestamp; uin { 340704, "000000000001c5e10e9e94b761464548efd2bbcf22509ac6bfec35757da58687", 1535711279, 0x1b024145 }, { 673344, "00000000000041bbd71687002a65c4dac458cb8a775e7ca094d623ac50300979", 1555813777, 0x1a62c6e4 }, { 844704, "0000000000006361abddc32b21bef2bf1df669f731ec18a13adbd426cced17b6", 1566164045, 0x1a78e5ab }, - { 899136, "00000000000015d1f71d3fac92e98d3a01f7ec310c10e90a966dcf1b64369239", 1569424946, 0x1a379754 }, - { 322560, "000000000001d50eaf12266c6ecaefec473fecd9daa7993db05b89e6ab381388", 1533209846, 0x1b04cb9e }, - { 340704, "000000000001c5e10e9e94b761464548efd2bbcf22509ac6bfec35757da58687", 1535711279, 0x1b024145 }, - { 673344, "00000000000041bbd71687002a65c4dac458cb8a775e7ca094d623ac50300979", 1555813777, 0x1a62c6e4 }, - { 844704, "0000000000006361abddc32b21bef2bf1df669f731ec18a13adbd426cced17b6", 1566164045, 0x1a78e5ab }, - { 899136, "00000000000015d1f71d3fac92e98d3a01f7ec310c10e90a966dcf1b64369239", 1569424946, 0x1a379754 }, - { 1196000, "0000000000000636f7177046a677203ea191d540b1aefdff63fd43eec538dd3b", 1587354658, 0x1a254bfb } + { 899136, "00000000000015d1f71d3fac92e98d3a01f7ec310c10e90a966dcf1b64369239", 1569424946, 0x1a379754 }, //Sep 15 2019 + { 1196000, "0000000000000636f7177046a677203ea191d540b1aefdff63fd43eec538dd3b", 1587354658, 0x1a254bfb }, //Apr 19 2020 + //I'm not sure if there is a major problem with checkpoints each month (40,320 blocks) + //Mostly doing it just to give variable-start wallet restore plenty of times to work with + { 1216160, "00000000000020daebec36516ef5f867b0fccce33e68646e7652f80ea96186e7", 1588572488, 0x1a259da6 }, //May 4 2020 + { 1256480, "000000000000e62500d1e41ee8c468d626ccf475eea3f291f76c142560a3ad83", 1590977350, 0x1b016954 }, //May 31 2020 + { 1296800, "000000000001846f32ad426f8a7576bd49b06861aa6cd506cb0dbc9cf105cd85", 1593411973, 0x1b018cff }, //Jun 29 2020 + { 1337120, "0000000000013b6afd6fa8c4cd57143b33138d648bba5b9aefc4be2381611a50", 1595848826, 0x1b02425d }, //Jul 27 2020 + { 1377440, "0000000000017dce6b055831e2eec4a62d529e870d0fc9a7ff4f896a18786c8f", 1598283070, 0x1b01fc40 }, //Aug 24 2020 + { 1417760, "000000000000cc0ad4fc9c2adc673f57bbd09812e1f5f3fda2b2e3d0eb1c5a93", 1600720000, 0x1b02927b }, //Sep 21 2020 + { 1458080, "0000000000002668ed1ffdd0bf1982a48d637a9175c8b030f1d5e300a4a0b029", 1603153479, 0x1b023835 }, //Oct 20 2020 + { 1498400, "00000000000036f29125709aced6507829b96138ea6c1e07bdfbed2f703fffb8", 1605588946, 0x1b025e47 }, //Nov 17 2020 + { 1538720, "000000000000d8755b12242895c515c93f7cfe8094bcb85138954728e7f5dc85", 1608024058, 0x1b02df01 }, //Dec 15 2020 + { 1579040, "000000000001b776ee39f65d67c8d4cfa2f200c359a9b5787c85f31503bb0fab", 1610459367, 0x1b02be6f }, //Jan 12 2021 + { 1619360, "000000000001cb5fde4bb9c89b5c0060a1903ae2ba5f170d7459022ef59965a6", 1612893283, 0x1b023b77 }, //Feb 9 2021 + { 1659680, "000000000000258aa2b5bf5d0003b5717d066303819553b64296e935154773c4", 1615320605, 0x1b0086e1 }, //Mar 9 2021 + //{1700000}, //Almost.... }; +//1 week = 10,080, 4 weeks = 40,320 + static const char *dns_seeds[] = { // "127.0.0.1", NULL "seed-raven.ravencoin.com", "seed-raven.ravencoin.org.", "seed-raven.bitactivate.com.", NULL From 56e3a2482929687f589ebb84a931d07fd414c218 Mon Sep 17 00:00:00 2001 From: Ben Abraham Date: Sun, 4 Apr 2021 05:04:12 -0400 Subject: [PATCH 2/4] Added UI elements on the restore screen to input the fast restore key, as well as display an FAQ dialog explaining what it does. Added proper UI validation around the value, erroring if negative or outside of the current week-value UI is properly fed into BRPrefs, and is functioning correctly. The largest piece remaining is the displayed value on the output of the fast restore key when creating a new wallet. --- .../activities/InputWordsActivity.java | 56 ++++++++++++++++++- .../ravenwallet/tools/util/BRConstants.java | 4 ++ .../com/ravenwallet/tools/util/Utils.java | 13 +++++ .../ravenwallet/wallet/RvnWalletManager.java | 24 +++++--- .../main/res/layout/activity_input_words.xml | 44 ++++++++++++++- app/src/main/res/values/strings.xml | 4 ++ 6 files changed, 133 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/ravenwallet/presenter/activities/InputWordsActivity.java b/app/src/main/java/com/ravenwallet/presenter/activities/InputWordsActivity.java index 2e305f05a..17279a5dd 100644 --- a/app/src/main/java/com/ravenwallet/presenter/activities/InputWordsActivity.java +++ b/app/src/main/java/com/ravenwallet/presenter/activities/InputWordsActivity.java @@ -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; @@ -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), @@ -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); @@ -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); @@ -232,8 +252,10 @@ public void onClick(BRDialogView brDialogView) { m.wipeWalletButKeystore(app); m.wipeKeyStore(app); PostAuth.getInstance().setPhraseForKeyStore(cleanPhrase); - BRSharedPrefs.putKnownSeedTime(app, 1_615_000_000); ////Calculate for realz: DEC 09 2020 - //BRSharedPrefs.putKnownSeedHeight(app, 1_500_000); + + 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); @@ -343,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(" ", ""); } @@ -360,6 +409,7 @@ private void clearWords() { word10.setText(""); word11.setText(""); word12.setText(""); + fastRestoreKey.setText(""); } @Override diff --git a/app/src/main/java/com/ravenwallet/tools/util/BRConstants.java b/app/src/main/java/com/ravenwallet/tools/util/BRConstants.java index 03a419674..df14d9097 100644 --- a/app/src/main/java/com/ravenwallet/tools/util/BRConstants.java +++ b/app/src/main/java/com/ravenwallet/tools/util/BRConstants.java @@ -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. */ diff --git a/app/src/main/java/com/ravenwallet/tools/util/Utils.java b/app/src/main/java/com/ravenwallet/tools/util/Utils.java index a438fd266..33f99e393 100644 --- a/app/src/main/java/com/ravenwallet/tools/util/Utils.java +++ b/app/src/main/java/com/ravenwallet/tools/util/Utils.java @@ -224,4 +224,17 @@ 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 long getCurrentFastRestoreKey() { + return (getCurrentUnixTimestamp() - BRConstants.GENESIS_TIMESTAMP) / BRConstants.FAST_SYNC_INTERVAL_SECONDS; + } + + public static long getSeedTimeFromFastRestoreKey(int fastRestoreKey) { + return (fastRestoreKey * BRConstants.FAST_SYNC_INTERVAL_SECONDS) + BRConstants.GENESIS_TIMESTAMP; + } } \ No newline at end of file diff --git a/app/src/main/java/com/ravenwallet/wallet/RvnWalletManager.java b/app/src/main/java/com/ravenwallet/wallet/RvnWalletManager.java index 98e8eb121..1d21de052 100644 --- a/app/src/main/java/com/ravenwallet/wallet/RvnWalletManager.java +++ b/app/src/main/java/com/ravenwallet/wallet/RvnWalletManager.java @@ -136,14 +136,22 @@ public synchronized static RvnWalletManager getInstance(Context app) { // int time = (int) (System.currentTimeMillis() / DateUtils.SECOND_IN_MILLIS); long time = BRKeyStore.getWalletCreationTime(app); if(time == 0 && BRSharedPrefs.getKnownSeedTime(app) > 0) { - //Not resuming from existing wallet - //Check if we have a know checkpoint we should use instead (based on known seed block height) - time = BRSharedPrefs.getKnownSeedTime(app); - Log.d(TAG, "getInstance: resuming from known block time: " + time); - long estimatedHeight = (time - 1514962800) / (60); - //1514962800 is the KAWPOW epoch, divide by 60 to get est. block height from time - - BRSharedPrefs.putStartHeight(app, BRSharedPrefs.getCurrentWalletIso(app), estimatedHeight + 100); + //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); diff --git a/app/src/main/res/layout/activity_input_words.xml b/app/src/main/res/layout/activity_input_words.xml index cf9398cd2..689a2ccbc 100644 --- a/app/src/main/res/layout/activity_input_words.xml +++ b/app/src/main/res/layout/activity_input_words.xml @@ -106,6 +106,7 @@ app:customTFont="CircularPro-Bold.otf" /> @@ -543,12 +545,52 @@ + + + + + + Reset PIN Enter Paper Key + + Fast-Restore Key (Optional) + + The Fast-Restore Key is an optional feature, used to speed up synchronization of newly installed clients for both new and restored wallets.\n\nUsing this will skip downloading blocks that happened before your wallet was created, rounded to the nearest hard-coded checkpoint (roughly monthly).\n\nIf you don\'t know your code, leave this blank and you will just download from the start. Recover your Ravencoin Wallet with your paper key. From baa7c5f80a029102fa4419a2e9b2753b516ae239 Mon Sep 17 00:00:00 2001 From: Ben Abraham Date: Sun, 4 Apr 2021 19:15:18 -0400 Subject: [PATCH 3/4] Added UI elements on paper key activity to show you the current fast-restore key, as well as details on it's purpose. Adjusted verbiage slightly to instruct leaving the field empty if unknown. Fixed possible type issues in fast-restore key helper functions. --- .../activities/PaperKeyActivity.java | 46 +++++++++++++++++-- .../com/ravenwallet/tools/util/Utils.java | 6 +-- app/src/main/res/values/strings.xml | 4 ++ 3 files changed, 48 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/ravenwallet/presenter/activities/PaperKeyActivity.java b/app/src/main/java/com/ravenwallet/presenter/activities/PaperKeyActivity.java index 7d3ddf0aa..ebb7380de 100644 --- a/app/src/main/java/com/ravenwallet/presenter/activities/PaperKeyActivity.java +++ b/app/src/main/java/com/ravenwallet/presenter/activities/PaperKeyActivity.java @@ -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; @@ -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; @@ -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<>(); @@ -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]); @@ -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); @@ -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); } @@ -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); @@ -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); } } diff --git a/app/src/main/java/com/ravenwallet/tools/util/Utils.java b/app/src/main/java/com/ravenwallet/tools/util/Utils.java index 33f99e393..47ec3fae6 100644 --- a/app/src/main/java/com/ravenwallet/tools/util/Utils.java +++ b/app/src/main/java/com/ravenwallet/tools/util/Utils.java @@ -230,11 +230,11 @@ public static long getCurrentUnixTimestamp() { } //Integer division will always effectively floor the results here - public static long getCurrentFastRestoreKey() { - return (getCurrentUnixTimestamp() - BRConstants.GENESIS_TIMESTAMP) / BRConstants.FAST_SYNC_INTERVAL_SECONDS; + public static int getCurrentFastRestoreKey() { + return (int)((getCurrentUnixTimestamp() - BRConstants.GENESIS_TIMESTAMP) / BRConstants.FAST_SYNC_INTERVAL_SECONDS); } public static long getSeedTimeFromFastRestoreKey(int fastRestoreKey) { - return (fastRestoreKey * BRConstants.FAST_SYNC_INTERVAL_SECONDS) + BRConstants.GENESIS_TIMESTAMP; + return (((long)fastRestoreKey) * BRConstants.FAST_SYNC_INTERVAL_SECONDS) + BRConstants.GENESIS_TIMESTAMP; } } \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b7c042ebe..d4f033565 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -334,6 +334,10 @@ The only way to access your bitcoin if you lose or upgrade your phone. Paper Key + + Fast-Restore Key (Click for Details) + + The Fast-Restore Key is an optional feature, used to speed up synchronization of newly installed clients for both new and restored wallets.\n\nUsing this will skip downloading blocks that happened before your wallet was created, rounded to the nearest hard-coded checkpoint (roughly monthly). Protects your Ravencoin Wallet from unauthorized users. From 18abe72bd4ac2de7ccf55f75b8aa0c4302307ae8 Mon Sep 17 00:00:00 2001 From: Ben Abraham Date: Sun, 4 Apr 2021 20:05:19 -0400 Subject: [PATCH 4/4] Added known seed time after a new wallet has been created, allowing near-immediate sync. --- app/src/main/java/com/ravenwallet/tools/security/PostAuth.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/java/com/ravenwallet/tools/security/PostAuth.java b/app/src/main/java/com/ravenwallet/tools/security/PostAuth.java index bc42c3775..345493a3f 100644 --- a/app/src/main/java/com/ravenwallet/tools/security/PostAuth.java +++ b/app/src/main/java/com/ravenwallet/tools/security/PostAuth.java @@ -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);