diff --git a/core/src/main/java/org/bitcoinj/core/AbstractManager.java b/core/src/main/java/org/bitcoinj/core/AbstractManager.java index 0af2a3964..dfd37bde7 100644 --- a/core/src/main/java/org/bitcoinj/core/AbstractManager.java +++ b/core/src/main/java/org/bitcoinj/core/AbstractManager.java @@ -281,8 +281,10 @@ protected void saveNow() { /** Requests an asynchronous save on a background thread */ protected void saveLater() { ManagerFiles files = vFileManager; - if (files != null) + if (files != null) { + Context.propagate(context); files.saveLater(); + } } /** diff --git a/core/src/main/java/org/bitcoinj/evolution/SimplifiedMasternodeList.java b/core/src/main/java/org/bitcoinj/evolution/SimplifiedMasternodeList.java index 34aa986c7..b24988f4e 100644 --- a/core/src/main/java/org/bitcoinj/evolution/SimplifiedMasternodeList.java +++ b/core/src/main/java/org/bitcoinj/evolution/SimplifiedMasternodeList.java @@ -33,6 +33,7 @@ import java.nio.ByteBuffer; import java.util.*; import java.util.concurrent.locks.ReentrantLock; +import java.util.stream.Collectors; import java.util.stream.Stream; import static java.lang.Math.min; @@ -338,6 +339,12 @@ public Masternode getMNByAddress(InetSocketAddress socketAddress) { return null; } + public List getMasternodesByVotingKey(KeyId votingKeyId) { + return mnMap.values().stream() + .filter(simplifiedMasternodeListEntry -> simplifiedMasternodeListEntry.keyIdVoting.equals(votingKeyId)) + .collect(Collectors.toList()); + } + public interface ForeachMNCallback { void processMN(SimplifiedMasternodeListEntry mn); } diff --git a/core/src/main/java/org/bitcoinj/quorums/ChainLocksHandler.java b/core/src/main/java/org/bitcoinj/quorums/ChainLocksHandler.java index b963d66b7..93c9487e6 100644 --- a/core/src/main/java/org/bitcoinj/quorums/ChainLocksHandler.java +++ b/core/src/main/java/org/bitcoinj/quorums/ChainLocksHandler.java @@ -97,15 +97,11 @@ public ChainLocksHandler(Context context) { public void setBlockChain(AbstractBlockChain blockChain, AbstractBlockChain headerChain) { this.blockChain = blockChain; this.headerChain = headerChain; - this.blockChain.addNewBestBlockListener(this.newBestBlockListener); this.quorumSigningManager = context.signingManager; } @Override public void close() { - if (blockChain != null) { - blockChain.removeNewBestBlockListener(this.newBestBlockListener); - } blockChain = null; super.close(); } @@ -477,8 +473,6 @@ void cleanup() { } - private final NewBestBlockListener newBestBlockListener = block -> updatedBlockTip(block, null); - private final CopyOnWriteArrayList> chainLockListeners; /** diff --git a/core/src/main/java/org/bitcoinj/quorums/InstantSendManager.java b/core/src/main/java/org/bitcoinj/quorums/InstantSendManager.java index a70897a4d..43ac12a27 100644 --- a/core/src/main/java/org/bitcoinj/quorums/InstantSendManager.java +++ b/core/src/main/java/org/bitcoinj/quorums/InstantSendManager.java @@ -13,6 +13,7 @@ import org.bitcoinj.store.BlockStore; import org.bitcoinj.store.BlockStoreException; import org.bitcoinj.store.FullPrunedBlockStore; +import org.bitcoinj.utils.ContextPropagatingThreadFactory; import org.bitcoinj.utils.Pair; import org.bitcoinj.utils.Threading; import org.slf4j.Logger; @@ -26,6 +27,9 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; public class InstantSendManager implements RecoveredSignatureListener { @@ -36,8 +40,11 @@ public class InstantSendManager implements RecoveredSignatureListener { private static final Logger log = LoggerFactory.getLogger(InstantSendManager.class); ReentrantLock lock = Threading.lock("InstantSendManager"); InstantSendDatabase db; + @Deprecated Thread workThread; + @Deprecated private boolean runWithoutThread = true; + private ScheduledExecutorService scheduledExecutorService; AbstractBlockChain blockChain; //Keep track of when the ISLOCK arrived @@ -52,6 +59,11 @@ public InstantSendManager(Context context, InstantSendDatabase db) { this.quorumSigningManager = context.signingManager; pendingInstantSendLocks = new HashMap<>(); invalidInstantSendLocks = new HashMap<>(); + scheduledExecutorService = createScheduledExecutorService(); + } + + private ScheduledExecutorService createScheduledExecutorService() { + return Executors.newScheduledThreadPool(1, new ContextPropagatingThreadFactory("instantsend")); } @Override @@ -72,6 +84,8 @@ public void setBlockChain(AbstractBlockChain blockChain, @Nullable PeerGroup pee peerGroup.addOnTransactionBroadcastListener(this.transactionBroadcastListener); } context.chainLockHandler.addChainLockListener(this.chainLockListener); + if (scheduledExecutorService == null) + scheduledExecutorService = createScheduledExecutorService(); } public void close(PeerGroup peerGroup) { @@ -84,6 +98,15 @@ public void close(PeerGroup peerGroup) { peerGroup.removeOnTransactionBroadcastListener(this.transactionBroadcastListener); } context.chainLockHandler.removeChainLockListener(this.chainLockListener); + try { + if (!scheduledExecutorService.awaitTermination(3000, TimeUnit.MILLISECONDS)) { + log.warn("scheduled threads still remain"); + } + scheduledExecutorService = null; + } catch (InterruptedException e) { + log.warn("thread termination interrupted"); + // swallow + } } private boolean isInitialized() { @@ -713,8 +736,10 @@ void processInstantSendLock(long from, Sha256Hash hash, InstantSendLock islock) void updateWalletTransaction(Sha256Hash txid, Transaction tx) { TransactionConfidence confidence = tx != null ? tx.getConfidence() : context.getConfidenceTable().get(txid); if(confidence != null) { - confidence.setIXType(TransactionConfidence.IXType.IX_LOCKED); - confidence.queueListeners(TransactionConfidence.Listener.ChangeReason.IX_TYPE); + scheduledExecutorService.schedule(() -> { + confidence.setIXType(TransactionConfidence.IXType.IX_LOCKED); + confidence.queueListeners(TransactionConfidence.Listener.ChangeReason.IX_TYPE); + }, 250, TimeUnit.MILLISECONDS); } else { log.info("Can't find {} in mempool", txid); } diff --git a/core/src/main/java/org/bitcoinj/wallet/CoinJoinExtension.java b/core/src/main/java/org/bitcoinj/wallet/CoinJoinExtension.java index f0d97f16a..49d596c72 100644 --- a/core/src/main/java/org/bitcoinj/wallet/CoinJoinExtension.java +++ b/core/src/main/java/org/bitcoinj/wallet/CoinJoinExtension.java @@ -71,6 +71,8 @@ public class CoinJoinExtension extends AbstractKeyChainGroupExtension { private static final Logger log = LoggerFactory.getLogger(CoinJoinExtension.class); + private static final int COINJOIN_LOOKADHEAD = 400; + private static final int COINJOIN_LOOKADHEAD_THRESHOLD = COINJOIN_LOOKADHEAD - 1; protected AnyKeyChainGroup coinJoinKeyChainGroup; @@ -154,11 +156,19 @@ public void deserializeWalletExtension(Wallet containingWallet, byte[] data) thr coinJoinKeyChainGroup = AnyKeyChainGroup.fromProtobufUnencrypted(containingWallet.params, coinJoinProto.getKeyList(), ECKeyFactory.get(), false); } + if (coinJoinKeyChainGroup.hasKeyChains()) { + setLookaheadSize(); + } rounds = coinJoinProto.getRounds(); CoinJoinClientOptions.setRounds(rounds); loadedKeys = true; } + private void setLookaheadSize() { + coinJoinKeyChainGroup.getActiveKeyChain().setLookaheadSize(COINJOIN_LOOKADHEAD); + coinJoinKeyChainGroup.getActiveKeyChain().setLookaheadThreshold(COINJOIN_LOOKADHEAD_THRESHOLD); + } + public boolean hasKeyChain(ImmutableList path) { if (coinJoinKeyChainGroup == null) return false; @@ -179,7 +189,7 @@ public void addKeyChain(DeterministicSeed seed, ImmutableList path) coinJoinKeyChainGroup = AnyKeyChainGroup.builder(wallet.getParams(), ECKeyFactory.get()).build(); } coinJoinKeyChainGroup.addAndActivateHDChain(AnyDeterministicKeyChain.builder().seed(seed).accountPath(path).build()); - coinJoinKeyChainGroup.getActiveKeyChain().setLookaheadSize(300); + setLookaheadSize(); } } @@ -196,7 +206,7 @@ public void addEncryptedKeyChain(DeterministicSeed seed, ImmutableList