diff --git a/core/src/main/java/org/bitcoinj/wallet/KeyChainGroup.java b/core/src/main/java/org/bitcoinj/wallet/KeyChainGroup.java index 5eb219838..c538d3a3e 100644 --- a/core/src/main/java/org/bitcoinj/wallet/KeyChainGroup.java +++ b/core/src/main/java/org/bitcoinj/wallet/KeyChainGroup.java @@ -30,6 +30,7 @@ import org.bitcoinj.crypto.*; import org.bitcoinj.script.*; import org.bitcoinj.script.Script.ScriptType; import org.bitcoinj.utils.*; +import org.bitcoinj.wallet.listeners.CurrentKeyChangeEventListener; import org.bitcoinj.wallet.listeners.KeyChainEventListener; import org.slf4j.*; import org.bouncycastle.crypto.params.*; @@ -194,6 +195,8 @@ public class KeyChainGroup implements KeyBag { private int lookaheadSize = -1; private int lookaheadThreshold = -1; + private final CopyOnWriteArrayList> currentKeyChangeListeners = new CopyOnWriteArrayList<>(); + /** Creates a keychain group with just a basic chain. No deterministic chains will be created automatically. */ public static KeyChainGroup createBasic(NetworkParameters params) { return new KeyChainGroup(params, new BasicKeyChain(), null, -1, -1, null, null); @@ -274,6 +277,7 @@ public class KeyChainGroup implements KeyBag { chains.add(chain); currentKeys.clear(); currentAddresses.clear(); + queueOnCurrentKeyChanged(); } /** @@ -571,6 +575,7 @@ public class KeyChainGroup implements KeyBag { if (entry.getValue() != null && entry.getValue().equals(address)) { log.info("Marking P2SH address as used: {}", address); currentAddresses.put(entry.getKey(), freshAddress(entry.getKey())); + queueOnCurrentKeyChanged(); return; } } @@ -584,6 +589,7 @@ public class KeyChainGroup implements KeyBag { if (entry.getValue() != null && entry.getValue().equals(key)) { log.info("Marking key as used: {}", key); currentKeys.put(entry.getKey(), freshKey(entry.getKey())); + queueOnCurrentKeyChanged(); return; } } @@ -805,6 +811,37 @@ public class KeyChainGroup implements KeyBag { return basic.removeEventListener(listener); } + /** Removes a listener for events that are run when a current key and/or address changes. */ + public void addCurrentKeyChangeEventListener(CurrentKeyChangeEventListener listener) { + addCurrentKeyChangeEventListener(listener, Threading.USER_THREAD); + } + + /** + * Adds a listener for events that are run when a current key and/or address changes, on the given + * executor. + */ + public void addCurrentKeyChangeEventListener(CurrentKeyChangeEventListener listener, Executor executor) { + checkNotNull(listener); + currentKeyChangeListeners.add(new ListenerRegistration<>(listener, executor)); + } + + /** Removes a listener for events that are run when a current key and/or address changes. */ + public boolean removeCurrentKeyChangeEventListener(CurrentKeyChangeEventListener listener) { + checkNotNull(listener); + return ListenerRegistration.removeFromList(listener, currentKeyChangeListeners); + } + + private void queueOnCurrentKeyChanged() { + for (final ListenerRegistration registration : currentKeyChangeListeners) { + registration.executor.execute(new Runnable() { + @Override + public void run() { + registration.listener.onCurrentKeyChanged(); + } + }); + } + } + /** Returns a list of key protobufs obtained by merging the chains. */ public List serializeToProtobuf() { List result; diff --git a/core/src/main/java/org/bitcoinj/wallet/Wallet.java b/core/src/main/java/org/bitcoinj/wallet/Wallet.java index abaefd4ef..d69d10fdb 100644 --- a/core/src/main/java/org/bitcoinj/wallet/Wallet.java +++ b/core/src/main/java/org/bitcoinj/wallet/Wallet.java @@ -62,6 +62,7 @@ import org.bitcoinj.signers.*; import org.bitcoinj.utils.*; import org.bitcoinj.wallet.Protos.Wallet.*; import org.bitcoinj.wallet.WalletTransaction.*; +import org.bitcoinj.wallet.listeners.CurrentKeyChangeEventListener; import org.bitcoinj.wallet.listeners.KeyChainEventListener; import org.bitcoinj.wallet.listeners.ScriptsChangeEventListener; import org.bitcoinj.wallet.listeners.WalletChangeEventListener; @@ -2789,6 +2790,22 @@ public class Wallet extends BaseTaggableObject keyChainGroup.addEventListener(listener, executor); } + /** + * Adds an event listener object. Methods on this object are called when a current key and/or address + * changes. The listener is executed in the user thread. + */ + public void addCurrentKeyChangeEventListener(CurrentKeyChangeEventListener listener) { + keyChainGroup.addCurrentKeyChangeEventListener(listener); + } + + /** + * Adds an event listener object. Methods on this object are called when a current key and/or address + * changes. The listener is executed by the given executor. + */ + public void addCurrentKeyChangeEventListener(Executor executor, CurrentKeyChangeEventListener listener) { + keyChainGroup.addCurrentKeyChangeEventListener(listener, executor); + } + /** * Adds an event listener object. Methods on this object are called when something interesting happens, * like receiving money. Runs the listener methods in the user thread. @@ -2872,6 +2889,14 @@ public class Wallet extends BaseTaggableObject return keyChainGroup.removeEventListener(listener); } + /** + * Removes the given event listener object. Returns true if the listener was removed, false if that + * listener was never added. + */ + public boolean removeCurrentKeyChangeEventListener(CurrentKeyChangeEventListener listener) { + return keyChainGroup.removeCurrentKeyChangeEventListener(listener); + } + /** * Removes the given event listener object. Returns true if the listener was removed, false if that listener * was never added. diff --git a/core/src/main/java/org/bitcoinj/wallet/listeners/CurrentKeyChangeEventListener.java b/core/src/main/java/org/bitcoinj/wallet/listeners/CurrentKeyChangeEventListener.java new file mode 100644 index 000000000..5f370f081 --- /dev/null +++ b/core/src/main/java/org/bitcoinj/wallet/listeners/CurrentKeyChangeEventListener.java @@ -0,0 +1,26 @@ +/* + * 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.wallet.listeners; + +import org.bitcoinj.wallet.KeyChainGroup; + +public interface CurrentKeyChangeEventListener { + /** + * Called by {@link KeyChainGroup} whenever a current key and/or address changes. + */ + void onCurrentKeyChanged(); +} diff --git a/wallettemplate/src/main/java/wallettemplate/utils/BitcoinUIModel.java b/wallettemplate/src/main/java/wallettemplate/utils/BitcoinUIModel.java index f68ef7fc8..91b100df9 100644 --- a/wallettemplate/src/main/java/wallettemplate/utils/BitcoinUIModel.java +++ b/wallettemplate/src/main/java/wallettemplate/utils/BitcoinUIModel.java @@ -20,6 +20,7 @@ import org.bitcoinj.core.Address; import org.bitcoinj.core.Coin; import org.bitcoinj.core.listeners.DownloadProgressTracker; import org.bitcoinj.wallet.Wallet; +import org.bitcoinj.wallet.listeners.CurrentKeyChangeEventListener; import org.bitcoinj.wallet.listeners.WalletChangeEventListener; import javafx.application.Platform; import javafx.beans.property.ReadOnlyDoubleProperty; @@ -48,15 +49,25 @@ public class BitcoinUIModel { public final void setWallet(Wallet wallet) { wallet.addChangeEventListener(Platform::runLater, new WalletChangeEventListener() { @Override - public void onWalletChanged(Wallet wallet) { - update(wallet); + public void onWalletChanged(Wallet w) { + updateBalance(wallet); } }); - update(wallet); + wallet.addCurrentKeyChangeEventListener(Platform::runLater, new CurrentKeyChangeEventListener() { + @Override + public void onCurrentKeyChanged() { + updateAddress(wallet); + } + }); + updateBalance(wallet); + updateAddress(wallet); } - private void update(Wallet wallet) { + private void updateBalance(Wallet wallet) { balance.set(wallet.getBalance()); + } + + private void updateAddress(Wallet wallet) { address.set(wallet.currentReceiveAddress()); }