From 95f528a340a4fcd2a7eacc9a06165ef7f2ca3e82 Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Sun, 15 Sep 2013 18:10:15 +0200 Subject: [PATCH] Add more features to WalletAppKit, and ensure wallets it creates always have at least one key. --- .../com/google/bitcoin/kits/WalletAppKit.java | 64 ++++++++++++++----- .../bitcoin/examples/ForwardingService.java | 13 +--- 2 files changed, 51 insertions(+), 26 deletions(-) diff --git a/core/src/main/java/com/google/bitcoin/kits/WalletAppKit.java b/core/src/main/java/com/google/bitcoin/kits/WalletAppKit.java index 517d8111e..a505b5290 100644 --- a/core/src/main/java/com/google/bitcoin/kits/WalletAppKit.java +++ b/core/src/main/java/com/google/bitcoin/kits/WalletAppKit.java @@ -26,6 +26,7 @@ import com.google.common.util.concurrent.AbstractIdleService; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.concurrent.TimeUnit; @@ -57,6 +58,9 @@ public class WalletAppKit extends AbstractIdleService { private boolean useAutoSave = true; private PeerAddress[] peerAddresses; + private PeerEventListener downloadListener; + private boolean autoStop = true; + private InputStream checkpoints; public WalletAppKit(NetworkParameters params, File directory, String filePrefix) { this.params = checkNotNull(params); @@ -82,12 +86,37 @@ public class WalletAppKit extends AbstractIdleService { } } + /** If true, the wallet will save itself to disk automatically whenever it changes. */ public WalletAppKit setAutoSave(boolean value) { checkState(state() == State.NEW, "Cannot call after startup"); useAutoSave = value; return this; } + /** + * If you want to learn about the sync process, you can provide a listener here. For instance, a + * {@link DownloadListener} is a good choice. + */ + public WalletAppKit setDownloadListener(PeerEventListener listener) { + this.downloadListener = listener; + return this; + } + + /** If true, will register a shutdown hook to stop the library. Defaults to true. */ + public WalletAppKit setAutoStop(boolean autoStop) { + this.autoStop = autoStop; + return this; + } + + /** + * If set, the file is expected to contain a checkpoints file calculated with BuildCheckpoints. It makes initial + * block sync faster for new users - please refer to the documentation on the bitcoinj website for further details. + */ + public WalletAppKit setCheckpoints(InputStream checkpoints) { + this.checkpoints = checkpoints; + return this; + } + /** *

Override this to load all wallet extensions if any are necessary.

* @@ -113,9 +142,26 @@ public class WalletAppKit extends AbstractIdleService { FileInputStream walletStream = null; try { File chainFile = new File(directory, filePrefix + ".spvchain"); + boolean chainFileExists = chainFile.exists(); vWalletFile = new File(directory, filePrefix + ".wallet"); - boolean shouldReplayWallet = vWalletFile.exists() && !chainFile.exists(); + boolean shouldReplayWallet = vWalletFile.exists() && !chainFileExists; + if (vWalletFile.exists()) { + walletStream = new FileInputStream(vWalletFile); + vWallet = new Wallet(params); + addWalletExtensions(); // All extensions must be present before we deserialize + new WalletProtobufSerializer().readWallet(WalletProtobufSerializer.parseToProto(walletStream), vWallet); + if (shouldReplayWallet) + vWallet.clearTransactions(0); + } else { + vWallet = new Wallet(params); + vWallet.addKey(new ECKey()); + addWalletExtensions(); + } + if (useAutoSave) vWallet.autosaveToFile(vWalletFile, 1, TimeUnit.SECONDS, null); vStore = new SPVBlockStore(params, chainFile); + if (!chainFileExists && checkpoints != null) { + CheckpointManager.checkpoint(params, checkpoints, vStore, vWallet.getEarliestKeyCreationTime()); + } vChain = new BlockChain(params, vStore); vPeerGroup = new PeerGroup(params, vChain); // Set up peer addresses or discovery first, so if wallet extensions try to broadcast a transaction @@ -126,25 +172,12 @@ public class WalletAppKit extends AbstractIdleService { } else { vPeerGroup.addPeerDiscovery(new DnsDiscovery(params)); } - if (vWalletFile.exists()) { - walletStream = new FileInputStream(vWalletFile); - vWallet = new Wallet(params); - addWalletExtensions(); // All extensions must be present before we deserialize - new WalletProtobufSerializer().readWallet(WalletProtobufSerializer.parseToProto(walletStream), vWallet); - if (shouldReplayWallet) - vWallet.clearTransactions(0); - } else { - vWallet = new Wallet(params); - addWalletExtensions(); - } - if (useAutoSave) vWallet.autosaveToFile(vWalletFile, 1, TimeUnit.SECONDS, null); vChain.addWallet(vWallet); vPeerGroup.addWallet(vWallet); onSetupCompleted(); vPeerGroup.startAndWait(); - vPeerGroup.downloadBlockChain(); // Make sure we shut down cleanly. - Runtime.getRuntime().addShutdownHook(new Thread() { + if (autoStop) Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { try { WalletAppKit.this.stopAndWait(); @@ -153,6 +186,7 @@ public class WalletAppKit extends AbstractIdleService { } } }); + vPeerGroup.startBlockChainDownload(downloadListener == null ? new DownloadListener() : downloadListener); } catch (BlockStoreException e) { throw new IOException(e); } finally { diff --git a/examples/src/main/java/com/google/bitcoin/examples/ForwardingService.java b/examples/src/main/java/com/google/bitcoin/examples/ForwardingService.java index 8a868f24d..8f66d217d 100644 --- a/examples/src/main/java/com/google/bitcoin/examples/ForwardingService.java +++ b/examples/src/main/java/com/google/bitcoin/examples/ForwardingService.java @@ -64,17 +64,8 @@ public class ForwardingService { // Parse the address given as the first parameter. forwardingAddress = new Address(params, args[0]); - // Start up a basic app using a class that automates some boilerplate. Ensure we always have at least one key. - kit = new WalletAppKit(params, new File("."), filePrefix) { - @Override - protected void onSetupCompleted() { - // This is called in a background thread after startAndWait is called, as setting up various objects - // can do disk and network IO that may cause UI jank/stuttering in wallet apps if it were to be done - // on the main thread. - if (wallet().getKeychainSize() < 1) - wallet().addKey(new ECKey()); - } - }; + // Start up a basic app using a class that automates some boilerplate. + kit = new WalletAppKit(params, new File("."), filePrefix); if (params == RegTestParams.get()) { // Regression test mode is designed for testing and development only, so there's no public network for it.