/** * Work out which restore method to use, depending on if it is a Trezor wallet or not and what * backups there are */ public void calculateRestoreMethod() { Optional<HardwareWalletService> hardwareWalletService = CoreServices.getOrCreateHardwareWalletService(); // User has selected restore wallet - see if wallet is hard Trezor wallet // If so no need to enter a seed phrase - use the rootNode from the master public key to work // out the wallet id HardwareWalletContext context = hardwareWalletService.get().getContext(); // Create a wallet id from the rootNode to work out the wallet root directory if (context.getDeterministicKey().isPresent()) { walletId = Optional.of(new WalletId(context.getDeterministicKey().get().getIdentifier())); String walletRoot = WalletManager.createWalletRoot(walletId.get()); log.debug("Hardware wallet root : {}", walletRoot); } // Ensure Trezor is cancelled hardwareWalletService.get().requestCancel(); restoreMethod = RESTORE_WALLET_HARD_TREZOR; if (!isLocalZipBackupPresent()) { // Next ask for the cloud backup location state = RESTORE_WALLET_SELECT_BACKUP_LOCATION; } else { // Next select one of the local backups state = RESTORE_WALLET_SELECT_BACKUP; } }
/** * Create a Trezor hard wallet from a backup summary, decrypting it with a password created from * the Trezor supplied entropy */ private boolean createTrezorHardWallet() { // Get the model that contains the selected wallet backup to use SelectBackupSummaryModel selectedBackupSummaryModel = getWizardModel().getSelectBackupSummaryModel(); if (selectedBackupSummaryModel == null || selectedBackupSummaryModel.getValue() == null || selectedBackupSummaryModel.getValue().getFile() == null) { log.debug("No wallet backup to use from the model"); return false; } log.debug( "Loading hard wallet backup '" + selectedBackupSummaryModel.getValue().getFile() + "'"); try { // For Trezor hard wallets the backups are encrypted with the entropy derived password String walletPassword = null; Optional<HardwareWalletService> hardwareWalletService = CoreServices.getOrCreateHardwareWalletService(); if (hardwareWalletService.isPresent() && hardwareWalletService.get().getContext().getEntropy().isPresent()) { walletPassword = Hex.toHexString(hardwareWalletService.get().getContext().getEntropy().get()); } // Check there is a wallet password - if not then cannot decrypt backup if (walletPassword == null) { log.debug( "Cannot work out the password to decrypt the backup - there is no entropy from the Trezor"); return false; } KeyParameter backupAESKey = AESUtils.createAESKey( walletPassword.getBytes(Charsets.UTF_8), WalletManager.scryptSalt()); WalletId loadedWalletId = BackupManager.INSTANCE.loadZipBackup( selectedBackupSummaryModel.getValue().getFile(), backupAESKey); // Attempt to open the wallet final Optional<WalletSummary> walletSummaryOptional = WalletManager.INSTANCE.openWalletFromWalletId( InstallationManager.getOrCreateApplicationDataDirectory(), loadedWalletId, walletPassword); // If the wallet is present then it was opened successfully return walletSummaryOptional.isPresent(); } catch (Exception e) { log.error("Failed to restore Trezor hard wallet.", e); } // Must have failed to be here return false; }
/** * @param state The state object * @param mode The mode (e.g. standard, Trezor etc) */ public WelcomeWizardModel(WelcomeWizardState state, WelcomeWizardMode mode) { super(state); log.debug("Welcome wizard starting in state '{}' with mode '{}'", state.name(), mode.name()); this.seedPhraseGenerator = CoreServices.newSeedPhraseGenerator(); this.restoringSoftWallet = WelcomeWizardState.WELCOME_SELECT_WALLET.equals(state); this.mode = mode; // If restoring a Trezor hard wallet, work out the initial screen to show if (WelcomeWizardState.RESTORE_WALLET_SELECT_BACKUP.equals(state)) { calculateRestoreMethod(); log.debug("Starting hard wallet restore with state: {}", state); } }
/** @return True if synchronization is occurring correctly */ private boolean handleSynchronizationStatus() { log.debug("Synchronizing..."); try { return CoreServices.getOrCreateBitcoinNetworkService().isStartedOk(); } catch (Exception e) { log.error("Failed to start Bitcoin network.", e); } // Must have failed to be here return false; }
@Override public void showPrevious() { Optional<HardwareWalletService> hardwareWalletService; switch (state) { case WELCOME_LICENCE: state = WELCOME_LICENCE; break; case WELCOME_SELECT_LANGUAGE: state = WELCOME_LICENCE; break; case WELCOME_ATTACH_HARDWARE_WALLET: state = WELCOME_SELECT_LANGUAGE; break; case WELCOME_SELECT_WALLET: state = WELCOME_ATTACH_HARDWARE_WALLET; break; case CREATE_WALLET_PREPARATION: state = WELCOME_SELECT_WALLET; break; case CREATE_WALLET_SELECT_BACKUP_LOCATION: state = CREATE_WALLET_PREPARATION; break; case CREATE_WALLET_SEED_PHRASE: state = CREATE_WALLET_SELECT_BACKUP_LOCATION; break; case CREATE_WALLET_CONFIRM_SEED_PHRASE: state = CREATE_WALLET_SEED_PHRASE; break; case CREATE_WALLET_REPORT: state = CREATE_WALLET_SEED_PHRASE; break; case TREZOR_CREATE_WALLET_PREPARATION: hardwareWalletService = CoreServices.getOrCreateHardwareWalletService(); if (hardwareWalletService.isPresent() && hardwareWalletService.get().isDeviceReady()) { // A Trezor is connected mode = WelcomeWizardMode.TREZOR; } else { // Standard mode mode = WelcomeWizardMode.STANDARD; } // Back out to select wallet as the most general solution state = WELCOME_SELECT_WALLET; break; case TREZOR_CREATE_WALLET_SELECT_BACKUP_LOCATION: state = TREZOR_CREATE_WALLET_PREPARATION; break; case TREZOR_CREATE_WALLET_ENTER_DETAILS: state = TREZOR_CREATE_WALLET_SELECT_BACKUP_LOCATION; break; case TREZOR_CREATE_WALLET_REQUEST_CREATE_WALLET: throw new IllegalStateException( "'Previous' is not permitted here - user is committed to creating wallet"); case TREZOR_CREATE_WALLET_CONFIRM_CREATE_WALLET: throw new IllegalStateException( "'Previous' is not permitted here - user is committed to creating wallet"); case TREZOR_CREATE_WALLET_CONFIRM_ENTROPY: throw new IllegalStateException( "'Previous' is not permitted here - user is committed to creating wallet"); case TREZOR_CREATE_WALLET_ENTER_NEW_PIN: throw new IllegalStateException( "'Previous' is not permitted here - user is committed to creating wallet"); case TREZOR_CREATE_WALLET_CONFIRM_NEW_PIN: throw new IllegalStateException( "'Previous' is not permitted here - user is committed to creating wallet"); case TREZOR_CREATE_WALLET_CONFIRM_WORD: throw new IllegalStateException( "'Previous' is not permitted here - user is committed to creating wallet"); case TREZOR_CREATE_WALLET_REPORT: throw new IllegalStateException( "'Previous' is not permitted here - user is committed to creating wallet"); case RESTORE_PASSWORD_SEED_PHRASE: state = WELCOME_SELECT_WALLET; break; case RESTORE_PASSWORD_REPORT: state = RESTORE_PASSWORD_SEED_PHRASE; break; case RESTORE_WALLET_SEED_PHRASE: state = WELCOME_SELECT_WALLET; break; case RESTORE_WALLET_SELECT_BACKUP_LOCATION: state = RESTORE_WALLET_SEED_PHRASE; break; case RESTORE_WALLET_SELECT_BACKUP: state = RESTORE_WALLET_SELECT_BACKUP_LOCATION; break; case RESTORE_WALLET_TIMESTAMP: state = RESTORE_WALLET_SELECT_BACKUP_LOCATION; break; case RESTORE_WALLET_REPORT: state = restoreMethod; break; default: throw new IllegalStateException("Unknown state: " + state.name()); } }
@Override public void showNext() { Optional<HardwareWalletService> hardwareWalletService; switch (state) { case WELCOME_LICENCE: state = WELCOME_SELECT_LANGUAGE; break; case WELCOME_SELECT_LANGUAGE: state = WELCOME_ATTACH_HARDWARE_WALLET; break; case WELCOME_ATTACH_HARDWARE_WALLET: hardwareWalletService = CoreServices.getOrCreateHardwareWalletService(); if (hardwareWalletService.isPresent() && hardwareWalletService.get().isDeviceReady()) { // A Trezor is connected mode = WelcomeWizardMode.TREZOR; if (hardwareWalletService.get().isWalletPresent()) { // User may want to create or restore since they have an initialised device state = WELCOME_SELECT_WALLET; } else { // User can only create from an uninitialised device state = TREZOR_CREATE_WALLET_PREPARATION; } } else { // Standard mode mode = WelcomeWizardMode.STANDARD; state = WELCOME_SELECT_WALLET; } break; case WELCOME_SELECT_WALLET: hardwareWalletService = CoreServices.getOrCreateHardwareWalletService(); if (RESTORE_WALLET_SELECT_BACKUP.equals(selectWalletChoice)) { if (hardwareWalletService.isPresent() && hardwareWalletService.get().isDeviceReady() && hardwareWalletService.get().isWalletPresent()) { // Initialised hardware wallet is attached calculateRestoreMethod(); break; } } else { // Ensure Trezor is reset if it is attached and initialised if (hardwareWalletService.isPresent() && hardwareWalletService.get().isDeviceReady()) { hardwareWalletService.get().requestCancel(); hardwareWalletService.get().getContext().resetToConnected(); } state = selectWalletChoice; } break; case CREATE_WALLET_PREPARATION: state = CREATE_WALLET_SELECT_BACKUP_LOCATION; break; case CREATE_WALLET_SELECT_BACKUP_LOCATION: state = CREATE_WALLET_SEED_PHRASE; break; case CREATE_WALLET_SEED_PHRASE: state = CREATE_WALLET_CONFIRM_SEED_PHRASE; // Fail safe to ensure that the generator hasn't gone screwy Preconditions.checkState( SeedPhraseSize.isValid(getCreateWalletSeedPhrase().size()), "'actualSeedPhrase' is not a valid length"); break; case CREATE_WALLET_CONFIRM_SEED_PHRASE: state = CREATE_WALLET_CREATE_PASSWORD; break; case CREATE_WALLET_CREATE_PASSWORD: state = CREATE_WALLET_REPORT; break; case CREATE_WALLET_REPORT: throw new IllegalStateException("'Next' is not permitted here"); case TREZOR_CREATE_WALLET_PREPARATION: state = TREZOR_CREATE_WALLET_SELECT_BACKUP_LOCATION; break; case TREZOR_CREATE_WALLET_SELECT_BACKUP_LOCATION: state = TREZOR_CREATE_WALLET_ENTER_DETAILS; break; case TREZOR_CREATE_WALLET_ENTER_DETAILS: state = TREZOR_CREATE_WALLET_REQUEST_CREATE_WALLET; break; case TREZOR_CREATE_WALLET_REQUEST_CREATE_WALLET: state = TREZOR_CREATE_WALLET_CONFIRM_CREATE_WALLET; break; case TREZOR_CREATE_WALLET_CONFIRM_CREATE_WALLET: state = TREZOR_CREATE_WALLET_CONFIRM_ENTROPY; break; case TREZOR_CREATE_WALLET_CONFIRM_ENTROPY: state = TREZOR_CREATE_WALLET_ENTER_NEW_PIN; break; case TREZOR_CREATE_WALLET_ENTER_NEW_PIN: state = TREZOR_CREATE_WALLET_CONFIRM_NEW_PIN; break; case TREZOR_CREATE_WALLET_CONFIRM_NEW_PIN: state = TREZOR_CREATE_WALLET_CONFIRM_WORD; break; case TREZOR_CREATE_WALLET_CONFIRM_WORD: state = TREZOR_CREATE_WALLET_REPORT; break; case TREZOR_CREATE_WALLET_REPORT: break; case RESTORE_PASSWORD_SEED_PHRASE: state = RESTORE_PASSWORD_REPORT; break; case RESTORE_PASSWORD_REPORT: break; case RESTORE_WALLET_SEED_PHRASE: if (!isLocalZipBackupPresent()) { restoreMethod = RESTORE_WALLET_SELECT_BACKUP_LOCATION; } else { restoreMethod = RESTORE_WALLET_SELECT_BACKUP; } state = restoreMethod; break; case RESTORE_WALLET_SELECT_BACKUP_LOCATION: if (isCloudBackupPresent()) { restoreMethod = RESTORE_WALLET_SELECT_BACKUP; } else { restoreMethod = RESTORE_WALLET_TIMESTAMP; } state = restoreMethod; break; case RESTORE_WALLET_SELECT_BACKUP: state = RESTORE_WALLET_REPORT; break; case RESTORE_WALLET_TIMESTAMP: state = RESTORE_WALLET_REPORT; break; case RESTORE_WALLET_REPORT: throw new IllegalStateException("'Next' is not permitted here"); default: throw new IllegalStateException("Unknown state: " + state.name()); } }