further electrum server optimisations

This commit is contained in:
Craig Raw 2025-06-02 15:56:46 +02:00
parent b0d0514617
commit 31ce3ce68a
3 changed files with 61 additions and 7 deletions

View file

@ -14,9 +14,16 @@ import java.util.List;
*/ */
public class WalletNodeHistoryChangedEvent { public class WalletNodeHistoryChangedEvent {
private final String scriptHash; private final String scriptHash;
private final String status;
public WalletNodeHistoryChangedEvent(String scriptHash) { public WalletNodeHistoryChangedEvent(String scriptHash) {
this.scriptHash = scriptHash; this.scriptHash = scriptHash;
this.status = null;
}
public WalletNodeHistoryChangedEvent(String scriptHash, String status) {
this.scriptHash = scriptHash;
this.status = status;
} }
public WalletNode getWalletNode(Wallet wallet) { public WalletNode getWalletNode(Wallet wallet) {
@ -70,4 +77,8 @@ public class WalletNodeHistoryChangedEvent {
public String getScriptHash() { public String getScriptHash() {
return scriptHash; return scriptHash;
} }
public String getStatus() {
return status;
}
} }

View file

@ -76,6 +76,10 @@ public class ElectrumServer {
private static final Set<String> sameHeightTxioScriptHashes = ConcurrentHashMap.newKeySet(); private static final Set<String> sameHeightTxioScriptHashes = ConcurrentHashMap.newKeySet();
private final static Set<String> subscribedRecent = ConcurrentHashMap.newKeySet();
private final static Map<String, String> broadcastRecent = new ConcurrentHashMap<>();
private static ElectrumServerRpc electrumServerRpc = new SimpleElectrumServerRpc(); private static ElectrumServerRpc electrumServerRpc = new SimpleElectrumServerRpc();
private static Cormorant cormorant; private static Cormorant cormorant;
@ -1062,9 +1066,10 @@ public class ElectrumServer {
List<BlockTransactionHash> recentTransactions = feeRatesSource.getRecentMempoolTransactions(); List<BlockTransactionHash> recentTransactions = feeRatesSource.getRecentMempoolTransactions();
Map<BlockTransactionHash, Transaction> setReferences = new HashMap<>(); Map<BlockTransactionHash, Transaction> setReferences = new HashMap<>();
setReferences.put(recentTransactions.getFirst(), null); setReferences.put(recentTransactions.getFirst(), null);
Random random = new Random(); if(recentTransactions.size() > 1) {
if(random.nextBoolean()) { Random random = new Random();
setReferences.put(recentTransactions.get(random.nextInt(recentTransactions.size())), null); int halfSize = recentTransactions.size() / 2;
setReferences.put(recentTransactions.get(halfSize == 1 ? 1 : random.nextInt(halfSize) + 1), null);
} }
Map<Sha256Hash, BlockTransaction> transactions = getTransactions(null, setReferences, Collections.emptyMap()); Map<Sha256Hash, BlockTransaction> transactions = getTransactions(null, setReferences, Collections.emptyMap());
return transactions.values().stream().filter(blxTx -> blxTx.getTransaction() != null).toList(); return transactions.values().stream().filter(blxTx -> blxTx.getTransaction() != null).toList();
@ -1602,6 +1607,31 @@ public class ElectrumServer {
Set<MempoolRateSize> mempoolRateSizes = electrumServer.getMempoolRateSizes(); Set<MempoolRateSize> mempoolRateSizes = electrumServer.getMempoolRateSizes();
EventManager.get().post(new MempoolRateSizesUpdatedEvent(mempoolRateSizes)); EventManager.get().post(new MempoolRateSizesUpdatedEvent(mempoolRateSizes));
} }
@Subscribe
public void walletNodeHistoryChanged(WalletNodeHistoryChangedEvent event) {
String status = broadcastRecent.remove(event.getScriptHash());
if(status != null && status.equals(event.getStatus())) {
Map<String, String> subscribeScriptHashes = new HashMap<>();
Random random = new Random();
int subscriptions = random.nextInt(2) + 1;
for(int i = 0; i < subscriptions; i++) {
byte[] randomScriptHashBytes = new byte[32];
random.nextBytes(randomScriptHashBytes);
String randomScriptHash = Utils.bytesToHex(randomScriptHashBytes);
if(!subscribedScriptHashes.containsKey(randomScriptHash)) {
subscribeScriptHashes.put("m/" + subscribeScriptHashes.size(), randomScriptHash);
}
}
try {
electrumServerRpc.subscribeScriptHashes(transport, null, subscribeScriptHashes);
subscribedRecent.addAll(subscribeScriptHashes.values());
} catch(ElectrumServerRpcException e) {
log.debug("Error subscribing to recent mempool transaction outputs", e);
}
}
}
} }
public static class ReadRunnable implements Runnable { public static class ReadRunnable implements Runnable {
@ -2018,13 +2048,12 @@ public class ElectrumServer {
return Network.get() != Network.MAINNET && totalBlocks > 2; return Network.get() != Network.MAINNET && totalBlocks > 2;
} }
private final static Set<String> subscribedRecent = Collections.newSetFromMap(new ConcurrentHashMap<>());
private void subscribeRecent(ElectrumServer electrumServer) { private void subscribeRecent(ElectrumServer electrumServer) {
Set<String> unsubscribeScriptHashes = new HashSet<>(subscribedRecent); Set<String> unsubscribeScriptHashes = new HashSet<>(subscribedRecent);
unsubscribeScriptHashes.removeIf(subscribedScriptHashes::containsKey); unsubscribeScriptHashes.removeIf(subscribedScriptHashes::containsKey);
electrumServerRpc.unsubscribeScriptHashes(transport, unsubscribeScriptHashes); electrumServerRpc.unsubscribeScriptHashes(transport, unsubscribeScriptHashes);
subscribedRecent.removeAll(unsubscribeScriptHashes); subscribedRecent.removeAll(unsubscribeScriptHashes);
broadcastRecent.clear();
Map<String, String> subscribeScriptHashes = new HashMap<>(); Map<String, String> subscribeScriptHashes = new HashMap<>();
List<BlockTransaction> recentTransactions = electrumServer.getRecentMempoolTransactions(); List<BlockTransaction> recentTransactions = electrumServer.getRecentMempoolTransactions();
@ -2033,7 +2062,7 @@ public class ElectrumServer {
TransactionOutput txOutput = blkTx.getTransaction().getOutputs().get(i); TransactionOutput txOutput = blkTx.getTransaction().getOutputs().get(i);
String scriptHash = getScriptHash(txOutput); String scriptHash = getScriptHash(txOutput);
if(!subscribedScriptHashes.containsKey(scriptHash)) { if(!subscribedScriptHashes.containsKey(scriptHash)) {
subscribeScriptHashes.put("m/" + i, getScriptHash(txOutput)); subscribeScriptHashes.put("m/" + subscribeScriptHashes.size(), scriptHash);
} }
if(Math.random() < 0.1d) { if(Math.random() < 0.1d) {
break; break;
@ -2042,6 +2071,17 @@ public class ElectrumServer {
} }
if(!subscribeScriptHashes.isEmpty()) { if(!subscribeScriptHashes.isEmpty()) {
Random random = new Random();
int additionalRandomScriptHashes = random.nextInt(8) + 4;
for(int i = 0; i < additionalRandomScriptHashes; i++) {
byte[] randomScriptHashBytes = new byte[32];
random.nextBytes(randomScriptHashBytes);
String randomScriptHash = Utils.bytesToHex(randomScriptHashBytes);
if(!subscribedScriptHashes.containsKey(randomScriptHash)) {
subscribeScriptHashes.put("m/" + subscribeScriptHashes.size(), randomScriptHash);
}
}
try { try {
electrumServerRpc.subscribeScriptHashes(transport, null, subscribeScriptHashes); electrumServerRpc.subscribeScriptHashes(transport, null, subscribeScriptHashes);
subscribedRecent.addAll(subscribeScriptHashes.values()); subscribedRecent.addAll(subscribeScriptHashes.values());
@ -2066,6 +2106,9 @@ public class ElectrumServer {
Random random = new Random(); Random random = new Random();
if(random.nextBoolean()) { if(random.nextBoolean()) {
BlockTransaction blkTx = recentTransactions.get(random.nextInt(recentTransactions.size())); BlockTransaction blkTx = recentTransactions.get(random.nextInt(recentTransactions.size()));
String scriptHash = getScriptHash(blkTx.getTransaction().getOutputs().getFirst());
String status = getScriptHashStatus(List.of(new ScriptHashTx(0, blkTx.getHashAsString(), blkTx.getFee())));
broadcastRecent.put(scriptHash, status);
electrumServer.broadcastTransaction(blkTx.getTransaction()); electrumServer.broadcastTransaction(blkTx.getTransaction());
} }
} }

View file

@ -38,6 +38,6 @@ public class SubscriptionService {
existingStatuses.add(status); existingStatuses.add(status);
} }
Platform.runLater(() -> EventManager.get().post(new WalletNodeHistoryChangedEvent(scriptHash))); Platform.runLater(() -> EventManager.get().post(new WalletNodeHistoryChangedEvent(scriptHash, status)));
} }
} }