mirror of
https://github.com/bitcoinj/bitcoinj.git
synced 2025-01-19 05:33:44 +01:00
walletfx: Create WalletTemplate implementing AppDelegate
* Introduce `AppDelegate` class for delegating JavaFX `Application` * Move almost all of `Main` to `WalletTemplate` Rationale: * The “template” JavaFX Application main class (`Main`) is now about 30 lines of code * `Main` class allows easy switching between TestNet and MainNet (in fact it could become a command-line argument) and other configuration changes (e.g. `preferredOutputScriptType`) * Prepares the way for the next steps of refactoring
This commit is contained in:
parent
a7161eed8e
commit
d8b6733c9c
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright by the original author or authors.
|
||||
*
|
||||
* 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 org.bitcoinj.walletfx.application;
|
||||
|
||||
import javafx.application.Application;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
/**
|
||||
* A delegate that implements JavaFX {@link Application}
|
||||
*/
|
||||
public interface AppDelegate {
|
||||
/**
|
||||
* Implement this method if you have code to run during {@link Application#init()} or
|
||||
* if you need a reference to the actual {@code Application object}
|
||||
* @param application a reference to the actual {@code Application} object
|
||||
* @throws Exception something bad happened
|
||||
*/
|
||||
default void init(Application application) throws Exception {
|
||||
}
|
||||
void start(Stage primaryStage) throws Exception;
|
||||
void stop() throws Exception;
|
||||
}
|
@ -16,136 +16,44 @@
|
||||
|
||||
package wallettemplate;
|
||||
|
||||
import com.google.common.util.concurrent.*;
|
||||
import javafx.scene.input.*;
|
||||
import org.bitcoinj.utils.AppDataDirectory;
|
||||
import org.bitcoinj.core.NetworkParameters;
|
||||
import org.bitcoinj.core.Utils;
|
||||
import org.bitcoinj.kits.WalletAppKit;
|
||||
import org.bitcoinj.params.*;
|
||||
import org.bitcoinj.script.Script;
|
||||
import org.bitcoinj.utils.BriefLogFormatter;
|
||||
import org.bitcoinj.utils.Threading;
|
||||
import org.bitcoinj.wallet.DeterministicSeed;
|
||||
import javafx.application.Application;
|
||||
import javafx.application.Platform;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.stage.Stage;
|
||||
import org.bitcoinj.walletfx.utils.GuiUtils;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import static org.bitcoinj.walletfx.utils.GuiUtils.informationalAlert;
|
||||
import org.bitcoinj.core.NetworkParameters;
|
||||
import org.bitcoinj.params.TestNet3Params;
|
||||
import org.bitcoinj.script.Script;
|
||||
import org.bitcoinj.walletfx.application.AppDelegate;
|
||||
|
||||
/**
|
||||
* Proxy JavaFX {@link Application} that delegates all functionality
|
||||
* to {@link WalletTemplate}
|
||||
*/
|
||||
public class Main extends Application {
|
||||
public static NetworkParameters params = TestNet3Params.get();
|
||||
public static final Script.ScriptType PREFERRED_OUTPUT_SCRIPT_TYPE = Script.ScriptType.P2WPKH;
|
||||
public static final String APP_NAME = "WalletTemplate";
|
||||
private static final String WALLET_FILE_NAME = APP_NAME.replaceAll("[^a-zA-Z0-9.-]", "_") + "-"
|
||||
+ params.getPaymentProtocolId();
|
||||
private static final NetworkParameters params = TestNet3Params.get();
|
||||
private static final Script.ScriptType PREFERRED_OUTPUT_SCRIPT_TYPE = Script.ScriptType.P2WPKH;
|
||||
private static final String APP_NAME = "WalletTemplate";
|
||||
|
||||
static WalletAppKit bitcoin;
|
||||
static Main instance;
|
||||
|
||||
private MainController controller;
|
||||
|
||||
@Override
|
||||
public void start(Stage mainWindow) throws Exception {
|
||||
try {
|
||||
realStart(mainWindow);
|
||||
} catch (Throwable e) {
|
||||
GuiUtils.crashAlert(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private void realStart(Stage mainWindow) throws IOException {
|
||||
instance = this;
|
||||
// Show the crash dialog for any exceptions that we don't handle and that hit the main loop.
|
||||
GuiUtils.handleCrashesOnThisThread();
|
||||
|
||||
if (Utils.isMac()) {
|
||||
// We could match the Mac Aqua style here, except that (a) Modena doesn't look that bad, and (b)
|
||||
// the date picker widget is kinda broken in AquaFx and I can't be bothered fixing it.
|
||||
// AquaFx.style();
|
||||
}
|
||||
|
||||
// Load the GUI. The MainController class will be automagically created and wired up.
|
||||
URL location = getClass().getResource("main.fxml");
|
||||
FXMLLoader loader = new FXMLLoader(location);
|
||||
Pane mainUI = loader.load();
|
||||
controller = loader.getController();
|
||||
|
||||
Scene scene = controller.controllerStart(mainUI, "wallet.css");
|
||||
mainWindow.setScene(scene);
|
||||
|
||||
// Make log output concise.
|
||||
BriefLogFormatter.init();
|
||||
// Tell bitcoinj to run event handlers on the JavaFX UI thread. This keeps things simple and means
|
||||
// we cannot forget to switch threads when adding event handlers. Unfortunately, the DownloadListener
|
||||
// we give to the app kit is currently an exception and runs on a library thread. It'll get fixed in
|
||||
// a future version.
|
||||
Threading.USER_THREAD = Platform::runLater;
|
||||
// Create the app kit. It won't do any heavyweight initialization until after we start it.
|
||||
setupWalletKit(null);
|
||||
|
||||
if (bitcoin.isChainFileLocked()) {
|
||||
informationalAlert("Already running", "This application is already running and cannot be started twice.");
|
||||
Platform.exit();
|
||||
return;
|
||||
}
|
||||
|
||||
mainWindow.show();
|
||||
|
||||
WalletSetPasswordController.estimateKeyDerivationTimeMsec();
|
||||
|
||||
bitcoin.addListener(new Service.Listener() {
|
||||
@Override
|
||||
public void failed(Service.State from, Throwable failure) {
|
||||
GuiUtils.crashAlert(failure);
|
||||
}
|
||||
}, Platform::runLater);
|
||||
bitcoin.startAsync();
|
||||
|
||||
scene.getAccelerators().put(KeyCombination.valueOf("Shortcut+F"), () -> bitcoin.peerGroup().getDownloadPeer().close());
|
||||
}
|
||||
|
||||
public void setupWalletKit(@Nullable DeterministicSeed seed) {
|
||||
// If seed is non-null it means we are restoring from backup.
|
||||
File appDataDirectory = AppDataDirectory.get(APP_NAME).toFile();
|
||||
bitcoin = new WalletAppKit(params, PREFERRED_OUTPUT_SCRIPT_TYPE, null, appDataDirectory, WALLET_FILE_NAME) {
|
||||
@Override
|
||||
protected void onSetupCompleted() {
|
||||
Platform.runLater(controller::onBitcoinSetup);
|
||||
}
|
||||
};
|
||||
// Now configure and start the appkit. This will take a second or two - we could show a temporary splash screen
|
||||
// or progress widget to keep the user engaged whilst we initialise, but we don't.
|
||||
if (params == RegTestParams.get()) {
|
||||
bitcoin.connectToLocalHost(); // You should run a regtest mode bitcoind locally.
|
||||
}
|
||||
bitcoin.setDownloadListener(controller.progressBarUpdater())
|
||||
.setBlockingStartup(false)
|
||||
.setUserAgent(APP_NAME, "1.0");
|
||||
if (seed != null)
|
||||
bitcoin.restoreWalletFromSeed(seed);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void stop() throws Exception {
|
||||
bitcoin.stopAsync();
|
||||
bitcoin.awaitTerminated();
|
||||
// Forcibly terminate the JVM because Orchid likes to spew non-daemon threads everywhere.
|
||||
Runtime.getRuntime().exit(0);
|
||||
}
|
||||
private final AppDelegate delegate;
|
||||
|
||||
public static void main(String[] args) {
|
||||
launch(args);
|
||||
}
|
||||
|
||||
public Main() {
|
||||
delegate = new WalletTemplate(APP_NAME, params, PREFERRED_OUTPUT_SCRIPT_TYPE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() throws Exception {
|
||||
delegate.init(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(Stage primaryStage) throws Exception {
|
||||
delegate.start(primaryStage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() throws Exception {
|
||||
delegate.stop();
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ import org.bitcoinj.walletfx.utils.BitcoinUIModel;
|
||||
import org.bitcoinj.walletfx.utils.easing.EasingMode;
|
||||
import org.bitcoinj.walletfx.utils.easing.ElasticInterpolator;
|
||||
|
||||
import static wallettemplate.Main.bitcoin;
|
||||
import static wallettemplate.WalletTemplate.bitcoin;
|
||||
|
||||
/**
|
||||
* Gets created auto-magically by FXMLLoader via reflection. The widget fields are set to the GUI controls they're named
|
||||
@ -67,7 +67,7 @@ public class MainController extends OverlayableStackPaneController {
|
||||
// Special case of initOverlay that passes null as the 2nd parameter because ClickableBitcoinAddress is loaded by FXML
|
||||
// TODO: Extract QRCode Pane to separate reusable class that is a more standard OverlayController instance
|
||||
addressControl.initOverlay(this, null);
|
||||
addressControl.setAppName(Main.APP_NAME);
|
||||
addressControl.setAppName(WalletTemplate.instance.applicationName);
|
||||
addressControl.setOpacity(0.0);
|
||||
}
|
||||
|
||||
|
@ -63,13 +63,13 @@ public class SendMoneyController implements OverlayController<SendMoneyControlle
|
||||
|
||||
// Called by FXMLLoader
|
||||
public void initialize() {
|
||||
Coin balance = Main.bitcoin.wallet().getBalance();
|
||||
Coin balance = WalletTemplate.bitcoin.wallet().getBalance();
|
||||
checkState(!balance.isZero());
|
||||
new BitcoinAddressValidator(Main.params, address, sendBtn);
|
||||
new BitcoinAddressValidator(WalletTemplate.instance.params, address, sendBtn);
|
||||
new TextFieldValidator(amountEdit, text ->
|
||||
!WTUtils.didThrow(() -> checkState(Coin.parseCoin(text).compareTo(balance) <= 0)));
|
||||
amountEdit.setText(balance.toPlainString());
|
||||
address.setPromptText(Address.fromKey(Main.params, new ECKey(), Main.PREFERRED_OUTPUT_SCRIPT_TYPE).toString());
|
||||
address.setPromptText(Address.fromKey(WalletTemplate.instance.params, new ECKey(), WalletTemplate.instance.preferredOutputScriptType).toString());
|
||||
}
|
||||
|
||||
public void cancel(ActionEvent event) {
|
||||
@ -80,9 +80,9 @@ public class SendMoneyController implements OverlayController<SendMoneyControlle
|
||||
// Address exception cannot happen as we validated it beforehand.
|
||||
try {
|
||||
Coin amount = Coin.parseCoin(amountEdit.getText());
|
||||
Address destination = Address.fromString(Main.params, address.getText());
|
||||
Address destination = Address.fromString(WalletTemplate.instance.params, address.getText());
|
||||
SendRequest req;
|
||||
if (amount.equals(Main.bitcoin.wallet().getBalance()))
|
||||
if (amount.equals(WalletTemplate.bitcoin.wallet().getBalance()))
|
||||
req = SendRequest.emptyWallet(destination);
|
||||
else
|
||||
req = SendRequest.to(destination, amount);
|
||||
@ -90,7 +90,7 @@ public class SendMoneyController implements OverlayController<SendMoneyControlle
|
||||
// Don't make the user wait for confirmations for now, as the intention is they're sending it
|
||||
// their own money!
|
||||
req.allowUnconfirmed();
|
||||
sendResult = Main.bitcoin.wallet().sendCoins(req);
|
||||
sendResult = WalletTemplate.bitcoin.wallet().sendCoins(req);
|
||||
Futures.addCallback(sendResult.broadcastComplete, new FutureCallback<>() {
|
||||
@Override
|
||||
public void onSuccess(@Nullable Transaction result) {
|
||||
|
@ -79,13 +79,13 @@ public class WalletPasswordController implements OverlayController<WalletPasswor
|
||||
return;
|
||||
}
|
||||
|
||||
final KeyCrypterScrypt keyCrypter = (KeyCrypterScrypt) Main.bitcoin.wallet().getKeyCrypter();
|
||||
final KeyCrypterScrypt keyCrypter = (KeyCrypterScrypt) WalletTemplate.bitcoin.wallet().getKeyCrypter();
|
||||
checkNotNull(keyCrypter); // We should never arrive at this GUI if the wallet isn't actually encrypted.
|
||||
KeyDerivationTasks tasks = new KeyDerivationTasks(keyCrypter, password, getTargetTime()) {
|
||||
@Override
|
||||
protected final void onFinish(KeyParameter aesKey, int timeTakenMsec) {
|
||||
checkGuiThread();
|
||||
if (Main.bitcoin.wallet().checkAESKey(aesKey)) {
|
||||
if (WalletTemplate.bitcoin.wallet().checkAESKey(aesKey)) {
|
||||
WalletPasswordController.this.aesKey.set(aesKey);
|
||||
} else {
|
||||
log.warn("User entered incorrect password");
|
||||
@ -122,11 +122,11 @@ public class WalletPasswordController implements OverlayController<WalletPasswor
|
||||
// Writes the given time to the wallet as a tag so we can find it again in this class.
|
||||
public static void setTargetTime(Duration targetTime) {
|
||||
ByteString bytes = ByteString.copyFrom(Longs.toByteArray(targetTime.toMillis()));
|
||||
Main.bitcoin.wallet().setTag(TAG, bytes);
|
||||
WalletTemplate.bitcoin.wallet().setTag(TAG, bytes);
|
||||
}
|
||||
|
||||
// Reads target time or throws if not set yet (should never happen).
|
||||
public static Duration getTargetTime() throws IllegalArgumentException {
|
||||
return Duration.ofMillis(Longs.fromByteArray(Main.bitcoin.wallet().getTag(TAG).toByteArray()));
|
||||
return Duration.ofMillis(Longs.fromByteArray(WalletTemplate.bitcoin.wallet().getTag(TAG).toByteArray()));
|
||||
}
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ public class WalletSetPasswordController implements OverlayController<WalletSetP
|
||||
WalletPasswordController.setTargetTime(Duration.ofMillis(timeTakenMsec));
|
||||
// The actual encryption part doesn't take very long as most private keys are derived on demand.
|
||||
log.info("Key derived, now encrypting");
|
||||
Main.bitcoin.wallet().encrypt(scrypt, aesKey);
|
||||
WalletTemplate.bitcoin.wallet().encrypt(scrypt, aesKey);
|
||||
log.info("Encryption done");
|
||||
informationalAlert("Wallet encrypted",
|
||||
"You can remove the password at any time from the settings screen.");
|
||||
|
@ -70,7 +70,7 @@ public class WalletSettingsController implements OverlayController<WalletSetting
|
||||
|
||||
// Note: NOT called by FXMLLoader!
|
||||
public void initialize(@Nullable KeyParameter aesKey) {
|
||||
DeterministicSeed seed = Main.bitcoin.wallet().getKeyChainSeed();
|
||||
DeterministicSeed seed = WalletTemplate.bitcoin.wallet().getKeyChainSeed();
|
||||
if (aesKey == null) {
|
||||
if (seed.isEncrypted()) {
|
||||
log.info("Wallet is encrypted, requesting password first.");
|
||||
@ -80,7 +80,7 @@ public class WalletSettingsController implements OverlayController<WalletSetting
|
||||
}
|
||||
} else {
|
||||
this.aesKey = aesKey;
|
||||
seed = seed.decrypt(checkNotNull(Main.bitcoin.wallet().getKeyCrypter()), "", aesKey);
|
||||
seed = seed.decrypt(checkNotNull(WalletTemplate.bitcoin.wallet().getKeyCrypter()), "", aesKey);
|
||||
// Now we can display the wallet seed as appropriate.
|
||||
passwordButton.setText("Remove password");
|
||||
}
|
||||
@ -158,7 +158,7 @@ public class WalletSettingsController implements OverlayController<WalletSetting
|
||||
public void restoreClicked(ActionEvent event) {
|
||||
// Don't allow a restore unless this wallet is presently empty. We don't want to end up with two wallets, too
|
||||
// much complexity, even though WalletAppKit will keep the current one as a backup file in case of disaster.
|
||||
if (Main.bitcoin.wallet().getBalance().value > 0) {
|
||||
if (WalletTemplate.bitcoin.wallet().getBalance().value > 0) {
|
||||
informationalAlert("Wallet is not empty",
|
||||
"You must empty this wallet out before attempting to restore an older one, as mixing wallets " +
|
||||
"together can lead to invalidated backups.");
|
||||
@ -180,14 +180,14 @@ public class WalletSettingsController implements OverlayController<WalletSetting
|
||||
long birthday = datePicker.getValue().atStartOfDay().toEpochSecond(ZoneOffset.UTC);
|
||||
DeterministicSeed seed = new DeterministicSeed(Splitter.on(' ').splitToList(wordsArea.getText()), null, "", birthday);
|
||||
// Shut down bitcoinj and restart it with the new seed.
|
||||
Main.bitcoin.addListener(new Service.Listener() {
|
||||
WalletTemplate.bitcoin.addListener(new Service.Listener() {
|
||||
@Override
|
||||
public void terminated(Service.State from) {
|
||||
Main.instance.setupWalletKit(seed);
|
||||
Main.bitcoin.startAsync();
|
||||
WalletTemplate.instance.setupWalletKit(seed);
|
||||
WalletTemplate.bitcoin.startAsync();
|
||||
}
|
||||
}, Platform::runLater);
|
||||
Main.bitcoin.stopAsync();
|
||||
WalletTemplate.bitcoin.stopAsync();
|
||||
}
|
||||
|
||||
|
||||
@ -195,7 +195,7 @@ public class WalletSettingsController implements OverlayController<WalletSetting
|
||||
if (aesKey == null) {
|
||||
rootController.overlayUI("wallet_set_password.fxml");
|
||||
} else {
|
||||
Main.bitcoin.wallet().decrypt(aesKey);
|
||||
WalletTemplate.bitcoin.wallet().decrypt(aesKey);
|
||||
informationalAlert("Wallet decrypted", "A password will no longer be required to send money or edit settings.");
|
||||
passwordButton.setText("Set password");
|
||||
aesKey = null;
|
||||
|
157
wallettemplate/src/main/java/wallettemplate/WalletTemplate.java
Normal file
157
wallettemplate/src/main/java/wallettemplate/WalletTemplate.java
Normal file
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright by the original author or authors.
|
||||
*
|
||||
* 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 wallettemplate;
|
||||
|
||||
import com.google.common.util.concurrent.Service;
|
||||
import javafx.application.Platform;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.input.KeyCombination;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.stage.Stage;
|
||||
import org.bitcoinj.core.NetworkParameters;
|
||||
import org.bitcoinj.core.Utils;
|
||||
import org.bitcoinj.kits.WalletAppKit;
|
||||
import org.bitcoinj.params.RegTestParams;
|
||||
import org.bitcoinj.script.Script;
|
||||
import org.bitcoinj.utils.AppDataDirectory;
|
||||
import org.bitcoinj.utils.BriefLogFormatter;
|
||||
import org.bitcoinj.utils.Threading;
|
||||
import org.bitcoinj.wallet.DeterministicSeed;
|
||||
import org.bitcoinj.walletfx.application.AppDelegate;
|
||||
import org.bitcoinj.walletfx.utils.GuiUtils;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import static org.bitcoinj.walletfx.utils.GuiUtils.informationalAlert;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class WalletTemplate implements AppDelegate {
|
||||
static WalletAppKit bitcoin;
|
||||
static WalletTemplate instance;
|
||||
|
||||
public final String applicationName;
|
||||
private final String walletFileName;
|
||||
public final NetworkParameters params;
|
||||
public final Script.ScriptType preferredOutputScriptType;
|
||||
|
||||
private MainController controller;
|
||||
|
||||
public WalletTemplate(String applicationName, NetworkParameters params, Script.ScriptType preferredOutputScriptType) {
|
||||
instance = this;
|
||||
this.applicationName = applicationName;
|
||||
this.params = params;
|
||||
this.preferredOutputScriptType = preferredOutputScriptType;
|
||||
this.walletFileName = applicationName.replaceAll("[^a-zA-Z0-9.-]", "_") + "-" + params.getPaymentProtocolId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(Stage mainWindow) throws Exception {
|
||||
try {
|
||||
realStart(mainWindow);
|
||||
} catch (Throwable e) {
|
||||
GuiUtils.crashAlert(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private void realStart(Stage mainWindow) throws IOException {
|
||||
instance = this;
|
||||
// Show the crash dialog for any exceptions that we don't handle and that hit the main loop.
|
||||
GuiUtils.handleCrashesOnThisThread();
|
||||
|
||||
if (Utils.isMac()) {
|
||||
// We could match the Mac Aqua style here, except that (a) Modena doesn't look that bad, and (b)
|
||||
// the date picker widget is kinda broken in AquaFx and I can't be bothered fixing it.
|
||||
// AquaFx.style();
|
||||
}
|
||||
|
||||
// Load the GUI. The MainController class will be automagically created and wired up.
|
||||
URL location = getClass().getResource("main.fxml");
|
||||
FXMLLoader loader = new FXMLLoader(location);
|
||||
Pane mainUI = loader.load();
|
||||
controller = loader.getController();
|
||||
|
||||
Scene scene = controller.controllerStart(mainUI, "wallet.css");
|
||||
mainWindow.setScene(scene);
|
||||
|
||||
// Make log output concise.
|
||||
BriefLogFormatter.init();
|
||||
// Tell bitcoinj to run event handlers on the JavaFX UI thread. This keeps things simple and means
|
||||
// we cannot forget to switch threads when adding event handlers. Unfortunately, the DownloadListener
|
||||
// we give to the app kit is currently an exception and runs on a library thread. It'll get fixed in
|
||||
// a future version.
|
||||
Threading.USER_THREAD = Platform::runLater;
|
||||
// Create the app kit. It won't do any heavyweight initialization until after we start it.
|
||||
setupWalletKit(null);
|
||||
|
||||
if (bitcoin.isChainFileLocked()) {
|
||||
informationalAlert("Already running", "This application is already running and cannot be started twice.");
|
||||
Platform.exit();
|
||||
return;
|
||||
}
|
||||
|
||||
mainWindow.show();
|
||||
|
||||
WalletSetPasswordController.estimateKeyDerivationTimeMsec();
|
||||
|
||||
bitcoin.addListener(new Service.Listener() {
|
||||
@Override
|
||||
public void failed(Service.State from, Throwable failure) {
|
||||
GuiUtils.crashAlert(failure);
|
||||
}
|
||||
}, Platform::runLater);
|
||||
bitcoin.startAsync();
|
||||
|
||||
scene.getAccelerators().put(KeyCombination.valueOf("Shortcut+F"), () -> bitcoin.peerGroup().getDownloadPeer().close());
|
||||
}
|
||||
|
||||
public void setupWalletKit(@Nullable DeterministicSeed seed) {
|
||||
// If seed is non-null it means we are restoring from backup.
|
||||
File appDataDirectory = AppDataDirectory.get(applicationName).toFile();
|
||||
bitcoin = new WalletAppKit(params, preferredOutputScriptType, null, appDataDirectory, walletFileName) {
|
||||
@Override
|
||||
protected void onSetupCompleted() {
|
||||
Platform.runLater(controller::onBitcoinSetup);
|
||||
}
|
||||
};
|
||||
// Now configure and start the appkit. This will take a second or two - we could show a temporary splash screen
|
||||
// or progress widget to keep the user engaged whilst we initialise, but we don't.
|
||||
if (params == RegTestParams.get()) {
|
||||
bitcoin.connectToLocalHost(); // You should run a regtest mode bitcoind locally.
|
||||
}
|
||||
bitcoin.setDownloadListener(controller.progressBarUpdater())
|
||||
.setBlockingStartup(false)
|
||||
.setUserAgent(applicationName, "1.0");
|
||||
if (seed != null)
|
||||
bitcoin.restoreWalletFromSeed(seed);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void stop() throws Exception {
|
||||
bitcoin.stopAsync();
|
||||
bitcoin.awaitTerminated();
|
||||
// Forcibly terminate the JVM because Orchid likes to spew non-daemon threads everywhere.
|
||||
Runtime.getRuntime().exit(0);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user