mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2025-11-05 11:56:37 +00:00
optimize and reduce electrum server rpc calls
This commit is contained in:
parent
7a4015fdb5
commit
e3138f3392
2 changed files with 82 additions and 50 deletions
|
|
@ -61,11 +61,13 @@ public class ElectrumServer {
|
||||||
|
|
||||||
private static Server previousServer;
|
private static Server previousServer;
|
||||||
|
|
||||||
private static Map<String, String> retrievedScriptHashes = Collections.synchronizedMap(new HashMap<>());
|
private static final Map<String, String> retrievedScriptHashes = Collections.synchronizedMap(new HashMap<>());
|
||||||
|
|
||||||
private static Map<Sha256Hash, BlockTransaction> retrievedTransactions = Collections.synchronizedMap(new HashMap<>());
|
private static final Map<Sha256Hash, BlockTransaction> retrievedTransactions = Collections.synchronizedMap(new HashMap<>());
|
||||||
|
|
||||||
private static Set<String> sameHeightTxioScriptHashes = Collections.synchronizedSet(new HashSet<>());
|
private static final Map<Integer, BlockHeader> retrievedBlockHeaders = Collections.synchronizedMap(new HashMap<>());
|
||||||
|
|
||||||
|
private static final Set<String> sameHeightTxioScriptHashes = Collections.synchronizedSet(new HashSet<>());
|
||||||
|
|
||||||
private static ElectrumServerRpc electrumServerRpc = new SimpleElectrumServerRpc();
|
private static ElectrumServerRpc electrumServerRpc = new SimpleElectrumServerRpc();
|
||||||
|
|
||||||
|
|
@ -113,6 +115,7 @@ public class ElectrumServer {
|
||||||
if(previousServer != null && !electrumServer.equals(previousServer)) {
|
if(previousServer != null && !electrumServer.equals(previousServer)) {
|
||||||
retrievedScriptHashes.clear();
|
retrievedScriptHashes.clear();
|
||||||
retrievedTransactions.clear();
|
retrievedTransactions.clear();
|
||||||
|
retrievedBlockHeaders.clear();
|
||||||
TransactionHistoryService.walletLocks.values().forEach(walletLock -> walletLock.initialized = false);
|
TransactionHistoryService.walletLocks.values().forEach(walletLock -> walletLock.initialized = false);
|
||||||
}
|
}
|
||||||
previousServer = electrumServer;
|
previousServer = electrumServer;
|
||||||
|
|
@ -555,22 +558,29 @@ public class ElectrumServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void getReferencedTransactions(Wallet wallet, Map<WalletNode, Set<BlockTransactionHash>> nodeTransactionMap) throws ServerException {
|
public void getReferencedTransactions(Wallet wallet, Map<WalletNode, Set<BlockTransactionHash>> nodeTransactionMap) throws ServerException {
|
||||||
Set<BlockTransactionHash> references = new TreeSet<>();
|
Map<BlockTransactionHash, Transaction> references = new TreeMap<>();
|
||||||
for(Set<BlockTransactionHash> nodeReferences : nodeTransactionMap.values()) {
|
for(Set<BlockTransactionHash> nodeReferences : nodeTransactionMap.values()) {
|
||||||
references.addAll(nodeReferences);
|
for(BlockTransactionHash nodeReference : nodeReferences) {
|
||||||
|
references.put(nodeReference, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for(Iterator<BlockTransactionHash> iter = references.iterator(); iter.hasNext(); ) {
|
for(Iterator<Map.Entry<BlockTransactionHash, Transaction>> iter = references.entrySet().iterator(); iter.hasNext(); ) {
|
||||||
BlockTransactionHash reference = iter.next();
|
Map.Entry<BlockTransactionHash, Transaction> entry = iter.next();
|
||||||
BlockTransaction blockTransaction = wallet.getTransactions().get(reference.getHash());
|
BlockTransactionHash reference = entry.getKey();
|
||||||
if(blockTransaction != null && reference.getHeight() == blockTransaction.getHeight()) {
|
BlockTransaction blockTransaction = wallet.getWalletTransaction(reference.getHash());
|
||||||
iter.remove();
|
if(blockTransaction != null) {
|
||||||
|
if(reference.getHeight() == blockTransaction.getHeight()) {
|
||||||
|
iter.remove();
|
||||||
|
} else {
|
||||||
|
entry.setValue(blockTransaction.getTransaction());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<Sha256Hash, BlockTransaction> transactionMap = new HashMap<>();
|
Map<Sha256Hash, BlockTransaction> transactionMap = new HashMap<>();
|
||||||
if(!references.isEmpty()) {
|
if(!references.isEmpty()) {
|
||||||
Map<Integer, BlockHeader> blockHeaderMap = getBlockHeaders(wallet, references);
|
Map<Integer, BlockHeader> blockHeaderMap = getBlockHeaders(wallet, references.keySet());
|
||||||
transactionMap = getTransactions(wallet, references, blockHeaderMap);
|
transactionMap = getTransactions(wallet, references, blockHeaderMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -581,24 +591,29 @@ public class ElectrumServer {
|
||||||
|
|
||||||
public Map<Integer, BlockHeader> getBlockHeaders(Wallet wallet, Set<BlockTransactionHash> references) throws ServerException {
|
public Map<Integer, BlockHeader> getBlockHeaders(Wallet wallet, Set<BlockTransactionHash> references) throws ServerException {
|
||||||
try {
|
try {
|
||||||
|
Map<Integer, BlockHeader> blockHeaderMap = new TreeMap<>();
|
||||||
Set<Integer> blockHeights = new TreeSet<>();
|
Set<Integer> blockHeights = new TreeSet<>();
|
||||||
for(BlockTransactionHash reference : references) {
|
for(BlockTransactionHash reference : references) {
|
||||||
if(reference.getHeight() > 0) {
|
if(reference.getHeight() > 0) {
|
||||||
blockHeights.add(reference.getHeight());
|
if(retrievedBlockHeaders.get(reference.getHeight()) != null) {
|
||||||
|
blockHeaderMap.put(reference.getHeight(), retrievedBlockHeaders.get(reference.getHeight()));
|
||||||
|
} else {
|
||||||
|
blockHeights.add(reference.getHeight());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(blockHeights.isEmpty()) {
|
if(blockHeights.isEmpty()) {
|
||||||
return Collections.emptyMap();
|
return blockHeaderMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<Integer, String> result = electrumServerRpc.getBlockHeaders(getTransport(), wallet, blockHeights);
|
Map<Integer, String> result = electrumServerRpc.getBlockHeaders(getTransport(), wallet, blockHeights);
|
||||||
|
|
||||||
Map<Integer, BlockHeader> blockHeaderMap = new TreeMap<>();
|
|
||||||
for(Integer height : result.keySet()) {
|
for(Integer height : result.keySet()) {
|
||||||
byte[] blockHeaderBytes = Utils.hexToBytes(result.get(height));
|
byte[] blockHeaderBytes = Utils.hexToBytes(result.get(height));
|
||||||
BlockHeader blockHeader = new BlockHeader(blockHeaderBytes);
|
BlockHeader blockHeader = new BlockHeader(blockHeaderBytes);
|
||||||
blockHeaderMap.put(height, blockHeader);
|
blockHeaderMap.put(height, blockHeader);
|
||||||
|
updateRetrievedBlockHeaders(height, blockHeader);
|
||||||
blockHeights.remove(height);
|
blockHeights.remove(height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -616,51 +631,61 @@ public class ElectrumServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<Sha256Hash, BlockTransaction> getTransactions(Wallet wallet, Set<BlockTransactionHash> references, Map<Integer, BlockHeader> blockHeaderMap) throws ServerException {
|
public Map<Sha256Hash, BlockTransaction> getTransactions(Wallet wallet, Map<BlockTransactionHash, Transaction> references, Map<Integer, BlockHeader> blockHeaderMap) throws ServerException {
|
||||||
try {
|
try {
|
||||||
Set<BlockTransactionHash> checkReferences = new TreeSet<>(references);
|
Map<Sha256Hash, BlockTransaction> transactionMap = new HashMap<>();
|
||||||
|
Set<BlockTransactionHash> checkReferences = new TreeSet<>(references.keySet());
|
||||||
|
|
||||||
Set<String> txids = new LinkedHashSet<>(references.size());
|
Set<String> txids = new LinkedHashSet<>(references.size());
|
||||||
for(BlockTransactionHash reference : references) {
|
for(BlockTransactionHash reference : references.keySet()) {
|
||||||
txids.add(reference.getHashAsString());
|
if(references.get(reference) == null) {
|
||||||
|
txids.add(reference.getHashAsString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, String> result = electrumServerRpc.getTransactions(getTransport(), wallet, txids);
|
if(!txids.isEmpty()) {
|
||||||
|
Map<String, String> result = electrumServerRpc.getTransactions(getTransport(), wallet, txids);
|
||||||
|
|
||||||
String strErrorTx = Sha256Hash.ZERO_HASH.toString();
|
String strErrorTx = Sha256Hash.ZERO_HASH.toString();
|
||||||
Map<Sha256Hash, BlockTransaction> transactionMap = new HashMap<>();
|
for(String txid : result.keySet()) {
|
||||||
for(String txid : result.keySet()) {
|
Sha256Hash hash = Sha256Hash.wrap(txid);
|
||||||
Sha256Hash hash = Sha256Hash.wrap(txid);
|
String strRawTx = result.get(txid);
|
||||||
String strRawTx = result.get(txid);
|
|
||||||
|
|
||||||
if(strRawTx.equals(strErrorTx)) {
|
if(strRawTx.equals(strErrorTx)) {
|
||||||
transactionMap.put(hash, UNFETCHABLE_BLOCK_TRANSACTION);
|
transactionMap.put(hash, UNFETCHABLE_BLOCK_TRANSACTION);
|
||||||
checkReferences.removeIf(ref -> ref.getHash().equals(hash));
|
checkReferences.removeIf(ref -> ref.getHash().equals(hash));
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] rawtx = Utils.hexToBytes(strRawTx);
|
||||||
|
Transaction transaction;
|
||||||
|
|
||||||
|
try {
|
||||||
|
transaction = new Transaction(rawtx);
|
||||||
|
} catch(ProtocolException e) {
|
||||||
|
log.error("Could not parse tx: " + strRawTx);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<BlockTransactionHash> optionalReference = references.keySet().stream().filter(reference -> reference.getHash().equals(hash)).findFirst();
|
||||||
|
if(optionalReference.isEmpty()) {
|
||||||
|
throw new IllegalStateException("Returned transaction " + hash.toString() + " that was not requested");
|
||||||
|
}
|
||||||
|
BlockTransactionHash reference = optionalReference.get();
|
||||||
|
|
||||||
|
references.put(reference, transaction);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
byte[] rawtx = Utils.hexToBytes(strRawTx);
|
for(BlockTransactionHash reference : references.keySet()) {
|
||||||
Transaction transaction;
|
Transaction transaction = references.get(reference);
|
||||||
|
|
||||||
try {
|
|
||||||
transaction = new Transaction(rawtx);
|
|
||||||
} catch(ProtocolException e) {
|
|
||||||
log.error("Could not parse tx: " + strRawTx);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Optional<BlockTransactionHash> optionalReference = references.stream().filter(reference -> reference.getHash().equals(hash)).findFirst();
|
|
||||||
if(optionalReference.isEmpty()) {
|
|
||||||
throw new IllegalStateException("Returned transaction " + hash.toString() + " that was not requested");
|
|
||||||
}
|
|
||||||
BlockTransactionHash reference = optionalReference.get();
|
|
||||||
|
|
||||||
Date blockDate = null;
|
Date blockDate = null;
|
||||||
if(reference.getHeight() > 0) {
|
if(reference.getHeight() > 0) {
|
||||||
BlockHeader blockHeader = blockHeaderMap.get(reference.getHeight());
|
BlockHeader blockHeader = blockHeaderMap.get(reference.getHeight());
|
||||||
if(blockHeader == null) {
|
if(blockHeader == null) {
|
||||||
transactionMap.put(hash, UNFETCHABLE_BLOCK_TRANSACTION);
|
transactionMap.put(reference.getHash(), UNFETCHABLE_BLOCK_TRANSACTION);
|
||||||
checkReferences.removeIf(ref -> ref.getHash().equals(hash));
|
checkReferences.removeIf(ref -> ref.getHash().equals(reference.getHash()));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
blockDate = blockHeader.getTimeAsDate();
|
blockDate = blockHeader.getTimeAsDate();
|
||||||
|
|
@ -668,7 +693,7 @@ public class ElectrumServer {
|
||||||
|
|
||||||
BlockTransaction blockchainTransaction = new BlockTransaction(reference.getHash(), reference.getHeight(), blockDate, reference.getFee(), transaction);
|
BlockTransaction blockchainTransaction = new BlockTransaction(reference.getHash(), reference.getHeight(), blockDate, reference.getFee(), transaction);
|
||||||
|
|
||||||
transactionMap.put(hash, blockchainTransaction);
|
transactionMap.put(reference.getHash(), blockchainTransaction);
|
||||||
checkReferences.remove(reference);
|
checkReferences.remove(reference);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1024,6 +1049,10 @@ public class ElectrumServer {
|
||||||
existingStatuses.add(status);
|
existingStatuses.add(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void updateRetrievedBlockHeaders(Integer blockHeight, BlockHeader blockHeader) {
|
||||||
|
retrievedBlockHeaders.put(blockHeight, blockHeader);
|
||||||
|
}
|
||||||
|
|
||||||
public static ServerCapability getServerCapability(List<String> serverVersion) {
|
public static ServerCapability getServerCapability(List<String> serverVersion) {
|
||||||
if(!serverVersion.isEmpty()) {
|
if(!serverVersion.isEmpty()) {
|
||||||
String server = serverVersion.getFirst().toLowerCase(Locale.ROOT);
|
String server = serverVersion.getFirst().toLowerCase(Locale.ROOT);
|
||||||
|
|
@ -1630,15 +1659,17 @@ public class ElectrumServer {
|
||||||
ElectrumServer electrumServer = new ElectrumServer();
|
ElectrumServer electrumServer = new ElectrumServer();
|
||||||
List<Set<BlockTransactionHash>> outputTransactionReferences = electrumServer.getOutputTransactionReferences(transaction, indexStart, indexEnd, blockTransactionHashes);
|
List<Set<BlockTransactionHash>> outputTransactionReferences = electrumServer.getOutputTransactionReferences(transaction, indexStart, indexEnd, blockTransactionHashes);
|
||||||
|
|
||||||
Set<BlockTransactionHash> setReferences = new HashSet<>();
|
Map<BlockTransactionHash, Transaction> setReferences = new HashMap<>();
|
||||||
for(Set<BlockTransactionHash> outputReferences : outputTransactionReferences) {
|
for(Set<BlockTransactionHash> outputReferences : outputTransactionReferences) {
|
||||||
if(outputReferences != null) {
|
if(outputReferences != null) {
|
||||||
setReferences.addAll(outputReferences);
|
for(BlockTransactionHash outputReference : outputReferences) {
|
||||||
|
setReferences.put(outputReference, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setReferences.remove(null);
|
setReferences.remove(null);
|
||||||
setReferences.remove(UNFETCHABLE_BLOCK_TRANSACTION);
|
setReferences.remove(UNFETCHABLE_BLOCK_TRANSACTION);
|
||||||
setReferences.removeIf(ref -> transactionMap.get(ref.getHash()) != null);
|
setReferences.keySet().removeIf(ref -> transactionMap.get(ref.getHash()) != null);
|
||||||
|
|
||||||
List<BlockTransaction> blockTransactions = new ArrayList<>(transaction.getOutputs().size());
|
List<BlockTransaction> blockTransactions = new ArrayList<>(transaction.getOutputs().size());
|
||||||
for(int i = 0; i < transaction.getOutputs().size(); i++) {
|
for(int i = 0; i < transaction.getOutputs().size(); i++) {
|
||||||
|
|
@ -1646,7 +1677,7 @@ public class ElectrumServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!setReferences.isEmpty()) {
|
if(!setReferences.isEmpty()) {
|
||||||
Map<Integer, BlockHeader> blockHeaderMap = electrumServer.getBlockHeaders(null, setReferences);
|
Map<Integer, BlockHeader> blockHeaderMap = electrumServer.getBlockHeaders(null, setReferences.keySet());
|
||||||
transactionMap.putAll(electrumServer.getTransactions(null, setReferences, blockHeaderMap));
|
transactionMap.putAll(electrumServer.getTransactions(null, setReferences, blockHeaderMap));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ public class SubscriptionService {
|
||||||
|
|
||||||
@JsonRpcMethod("blockchain.headers.subscribe")
|
@JsonRpcMethod("blockchain.headers.subscribe")
|
||||||
public void newBlockHeaderTip(@JsonRpcParam("header") final BlockHeaderTip header) {
|
public void newBlockHeaderTip(@JsonRpcParam("header") final BlockHeaderTip header) {
|
||||||
|
ElectrumServer.updateRetrievedBlockHeaders(header.height, header.getBlockHeader());
|
||||||
Platform.runLater(() -> EventManager.get().post(new NewBlockEvent(header.height, header.getBlockHeader())));
|
Platform.runLater(() -> EventManager.get().post(new NewBlockEvent(header.height, header.getBlockHeader())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue