diff --git a/drongo b/drongo index fff658a3..6a2af38b 160000 --- a/drongo +++ b/drongo @@ -1 +1 @@ -Subproject commit fff658a3ab33a3f63f5a1cd03c2b7cc1f20bec4a +Subproject commit 6a2af38b8a628f36b75c11626b0a8fd608e9d5a0 diff --git a/src/main/java/com/sparrowwallet/sparrow/control/TransactionHexArea.java b/src/main/java/com/sparrowwallet/sparrow/control/TransactionHexArea.java index 62c255de..99c6784a 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/TransactionHexArea.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/TransactionHexArea.java @@ -123,7 +123,8 @@ public class TransactionHexArea extends CodeArea { cursor = addSegment(segments, cursor, 8, "locktime"); if(cursor != getLength()) { - throw new IllegalStateException("Cursor position does not match transaction serialisation " + cursor + ": " + getLength()); + //While this is normally a good sanity check, the truncation applied means it may fail, so it is left commented out + //throw new IllegalStateException("Cursor position does not match transaction serialisation " + cursor + ": " + getLength()); } return segments; diff --git a/src/main/java/com/sparrowwallet/sparrow/net/BatchedElectrumServerRpc.java b/src/main/java/com/sparrowwallet/sparrow/net/BatchedElectrumServerRpc.java new file mode 100644 index 00000000..4ba1b528 --- /dev/null +++ b/src/main/java/com/sparrowwallet/sparrow/net/BatchedElectrumServerRpc.java @@ -0,0 +1,205 @@ +package com.sparrowwallet.sparrow.net; + +import com.github.arteam.simplejsonrpc.client.JsonRpcClient; +import com.github.arteam.simplejsonrpc.client.Transport; +import com.github.arteam.simplejsonrpc.client.builder.BatchRequestBuilder; +import com.github.arteam.simplejsonrpc.client.exception.JsonRpcBatchException; +import com.github.arteam.simplejsonrpc.client.exception.JsonRpcException; +import com.sparrowwallet.drongo.protocol.Sha256Hash; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class BatchedElectrumServerRpc implements ElectrumServerRpc { + private static final Logger log = LoggerFactory.getLogger(BatchedElectrumServerRpc.class); + + @Override + public void ping(Transport transport) { + try { + JsonRpcClient client = new JsonRpcClient(transport); + client.createRequest().method("server.ping").id(1).executeNullable(); + } catch(JsonRpcException e) { + throw new ElectrumServerRpcException("Error pinging server", e); + } + } + + @Override + public List getServerVersion(Transport transport, String clientName, String[] supportedVersions) { + try { + JsonRpcClient client = new JsonRpcClient(transport); + return client.createRequest().returnAsList(String.class).method("server.version").id(1).param("client_name", "Sparrow").param("protocol_version", supportedVersions).execute(); + } catch(JsonRpcException e) { + throw new ElectrumServerRpcException("Error getting server version", e); + } + } + + @Override + public String getServerBanner(Transport transport) { + try { + JsonRpcClient client = new JsonRpcClient(transport); + return client.createRequest().returnAs(String.class).method("server.banner").id(1).execute(); + } catch(JsonRpcException e) { + throw new ElectrumServerRpcException("Error getting server banner", e); + } + } + + @Override + public BlockHeaderTip subscribeBlockHeaders(Transport transport) { + try { + JsonRpcClient client = new JsonRpcClient(transport); + return client.createRequest().returnAs(BlockHeaderTip.class).method("blockchain.headers.subscribe").id(1).execute(); + } catch(JsonRpcException e) { + throw new ElectrumServerRpcException("Error subscribing to block headers", e); + } + } + + @Override + @SuppressWarnings("unchecked") + public Map getScriptHashHistory(Transport transport, Map pathScriptHashes, boolean failOnError) { + JsonRpcClient client = new JsonRpcClient(transport); + BatchRequestBuilder batchRequest = client.createBatchRequest().keysType(String.class).returnType(ScriptHashTx[].class); + + for(String path : pathScriptHashes.keySet()) { + batchRequest.add(path, "blockchain.scripthash.get_history", pathScriptHashes.get(path)); + } + + try { + return batchRequest.execute(); + } catch (JsonRpcBatchException e) { + if(failOnError) { + throw new ElectrumServerRpcException("Failed to retrieve references for paths: " + e.getErrors().keySet(), e); + } + + Map result = (Map)e.getSuccesses(); + for(Object key : e.getErrors().keySet()) { + result.put((String)key, new ScriptHashTx[] {ScriptHashTx.ERROR_TX}); + } + + return result; + } + } + + @Override + @SuppressWarnings("unchecked") + public Map getScriptHashMempool(Transport transport, Map pathScriptHashes, boolean failOnError) { + JsonRpcClient client = new JsonRpcClient(transport); + BatchRequestBuilder batchRequest = client.createBatchRequest().keysType(String.class).returnType(ScriptHashTx[].class); + + for(String path : pathScriptHashes.keySet()) { + batchRequest.add(path, "blockchain.scripthash.get_mempool", pathScriptHashes.get(path)); + } + + try { + return batchRequest.execute(); + } catch (JsonRpcBatchException e) { + if(failOnError) { + throw new ElectrumServerRpcException("Failed to retrieve references for paths: " + e.getErrors().keySet(), e); + } + + Map result = (Map)e.getSuccesses(); + for(Object key : e.getErrors().keySet()) { + result.put((String)key, new ScriptHashTx[] {ScriptHashTx.ERROR_TX}); + } + + return result; + } + } + + @Override + public Map subscribeScriptHashes(Transport transport, Map pathScriptHashes) { + JsonRpcClient client = new JsonRpcClient(transport); + BatchRequestBuilder batchRequest = client.createBatchRequest().keysType(String.class).returnType(String.class); + + for(String path : pathScriptHashes.keySet()) { + batchRequest.add(path, "blockchain.scripthash.subscribe", pathScriptHashes.get(path)); + } + + try { + return batchRequest.execute(); + } catch(JsonRpcBatchException e) { + //Even if we have some successes, failure to subscribe for all script hashes will result in outdated wallet view. Don't proceed. + throw new ElectrumServerRpcException("Failed to subscribe for updates for paths: " + e.getErrors().keySet(), e); + } + } + + @Override + @SuppressWarnings("unchecked") + public Map getBlockHeaders(Transport transport, Set blockHeights) { + JsonRpcClient client = new JsonRpcClient(transport); + BatchRequestBuilder batchRequest = client.createBatchRequest().keysType(Integer.class).returnType(String.class); + for(Integer height : blockHeights) { + batchRequest.add(height, "blockchain.block.header", height); + } + + try { + return batchRequest.execute(); + } catch (JsonRpcBatchException e) { + return (Map)e.getSuccesses(); + } + } + + @Override + @SuppressWarnings("unchecked") + public Map getTransactions(Transport transport, Set txids) { + JsonRpcClient client = new JsonRpcClient(transport); + BatchRequestBuilder batchRequest = client.createBatchRequest().keysType(String.class).returnType(String.class); + for(String txid : txids) { + batchRequest.add(txid, "blockchain.transaction.get", txid); + } + + try { + return batchRequest.execute(); + } catch (JsonRpcBatchException e) { + Map result = (Map)e.getSuccesses(); + + String strErrorTx = Sha256Hash.ZERO_HASH.toString(); + for(Object hash : e.getErrors().keySet()) { + String txhash = (String)hash; + result.put(txhash, strErrorTx); + } + + return result; + } + } + + @Override + @SuppressWarnings("unchecked") + public Map getVerboseTransactions(Transport transport, Set txids) { + JsonRpcClient client = new JsonRpcClient(transport); + BatchRequestBuilder batchRequest = client.createBatchRequest().keysType(String.class).returnType(VerboseTransaction.class); + for(String txid : txids) { + batchRequest.add(txid, "blockchain.transaction.get", txid, true); + } + + try { + return batchRequest.execute(); + } catch (JsonRpcBatchException e) { + log.warn("Some errors retrieving transactions: " + e.getErrors()); + return (Map)e.getSuccesses(); + } + } + + @Override + public Map getFeeEstimates(Transport transport, List targetBlocks) { + JsonRpcClient client = new JsonRpcClient(transport); + BatchRequestBuilder batchRequest = client.createBatchRequest().keysType(Integer.class).returnType(Double.class); + for(Integer targetBlock : targetBlocks) { + batchRequest.add(targetBlock, "blockchain.estimatefee", targetBlock); + } + + return batchRequest.execute(); + } + + @Override + public String broadcastTransaction(Transport transport, String txHex) { + try { + JsonRpcClient client = new JsonRpcClient(transport); + return client.createRequest().returnAs(String.class).method("blockchain.transaction.broadcast").id(1).param("raw_tx", txHex).execute(); + } catch(JsonRpcException e) { + throw new ElectrumServerRpcException(e.getErrorMessage().getMessage(), e); + } + } +} diff --git a/src/main/java/com/sparrowwallet/sparrow/net/ElectrumServer.java b/src/main/java/com/sparrowwallet/sparrow/net/ElectrumServer.java index 229c2f5e..e9deaa43 100644 --- a/src/main/java/com/sparrowwallet/sparrow/net/ElectrumServer.java +++ b/src/main/java/com/sparrowwallet/sparrow/net/ElectrumServer.java @@ -1,9 +1,6 @@ package com.sparrowwallet.sparrow.net; -import com.github.arteam.simplejsonrpc.client.*; -import com.github.arteam.simplejsonrpc.client.builder.BatchRequestBuilder; -import com.github.arteam.simplejsonrpc.client.exception.JsonRpcBatchException; -import com.github.arteam.simplejsonrpc.client.exception.JsonRpcException; +import com.github.arteam.simplejsonrpc.client.Transport; import com.google.common.net.HostAndPort; import com.sparrowwallet.drongo.KeyPurpose; import com.sparrowwallet.drongo.Utils; @@ -12,7 +9,6 @@ import com.sparrowwallet.drongo.wallet.*; import com.sparrowwallet.sparrow.event.ConnectionEvent; import com.sparrowwallet.sparrow.event.FeeRatesUpdatedEvent; import com.sparrowwallet.sparrow.io.Config; -import com.sparrowwallet.sparrow.io.ServerException; import com.sparrowwallet.sparrow.wallet.SendController; import javafx.concurrent.ScheduledService; import javafx.concurrent.Service; @@ -35,6 +31,8 @@ public class ElectrumServer { private static final Map subscribedScriptHashes = Collections.synchronizedMap(new HashMap<>()); + private ElectrumServerRpc electrumServerRpc = new BatchedElectrumServerRpc(); + private static synchronized Transport getTransport() throws ServerException { if(transport == null) { try { @@ -85,24 +83,20 @@ public class ElectrumServer { } public void ping() throws ServerException { - JsonRpcClient client = new JsonRpcClient(getTransport()); - client.createRequest().method("server.ping").id(1).executeNullable(); + electrumServerRpc.ping(getTransport()); } public List getServerVersion() throws ServerException { - JsonRpcClient client = new JsonRpcClient(getTransport()); + return electrumServerRpc.getServerVersion(getTransport(), "Sparrow", SUPPORTED_VERSIONS); //return client.createRequest().returnAsList(String.class).method("server.version").id(1).params("Sparrow", "1.4").execute(); - return client.createRequest().returnAsList(String.class).method("server.version").id(1).param("client_name", "Sparrow").param("protocol_version", SUPPORTED_VERSIONS).execute(); } public String getServerBanner() throws ServerException { - JsonRpcClient client = new JsonRpcClient(getTransport()); - return client.createRequest().returnAs(String.class).method("server.banner").id(1).execute(); + return electrumServerRpc.getServerBanner(getTransport()); } public BlockHeaderTip subscribeBlockHeaders() throws ServerException { - JsonRpcClient client = new JsonRpcClient(getTransport()); - return client.createRequest().returnAs(BlockHeaderTip.class).method("blockchain.headers.subscribe").id(1).execute(); + return electrumServerRpc.subscribeBlockHeaders(getTransport()); } public static synchronized boolean isConnected() { @@ -169,22 +163,15 @@ public class ElectrumServer { public void getReferences(Wallet wallet, String method, Collection nodes, Map> nodeTransactionMap, int startIndex) throws ServerException { try { - JsonRpcClient client = new JsonRpcClient(getTransport()); - BatchRequestBuilder batchRequest = client.createBatchRequest().keysType(String.class).returnType(ScriptHashTx[].class); - + Map pathScriptHashes = new LinkedHashMap<>(nodes.size()); for(WalletNode node : nodes) { if(node.getIndex() >= startIndex) { - batchRequest.add(node.getDerivationPath(), method, getScriptHash(wallet, node)); + pathScriptHashes.put(node.getDerivationPath(), getScriptHash(wallet, node)); } } - Map result; - try { - result = batchRequest.execute(); - } catch (JsonRpcBatchException e) { - //Even if we have some successes, failure to retrieve all references will result in an incomplete wallet history. Don't proceed. - throw new IllegalStateException("Failed to retrieve references for paths: " + e.getErrors().keySet()); - } + //Even if we have some successes, failure to retrieve all references will result in an incomplete wallet history. Don't proceed if that's the case. + Map result = electrumServerRpc.getScriptHashHistory(getTransport(), pathScriptHashes, true); for(String path : result.keySet()) { ScriptHashTx[] txes = result.get(path); @@ -214,8 +201,8 @@ public class ElectrumServer { } } } - } catch (IllegalStateException e) { - throw new ServerException(e.getCause()); + } catch (ElectrumServerRpcException e) { + throw new ServerException(e.getMessage(), e.getCause()); } catch (Exception e) { throw new ServerException(e); } @@ -223,30 +210,22 @@ public class ElectrumServer { public void subscribeWalletNodes(Wallet wallet, Collection nodes, int startIndex) throws ServerException { try { - JsonRpcClient client = new JsonRpcClient(getTransport()); - BatchRequestBuilder batchRequest = client.createBatchRequest().keysType(String.class).returnType(String.class); - Set scriptHashes = new HashSet<>(); + Map pathScriptHashes = new LinkedHashMap<>(); for(WalletNode node : nodes) { if(node.getIndex() >= startIndex) { String scriptHash = getScriptHash(wallet, node); if(!subscribedScriptHashes.containsKey(scriptHash) && scriptHashes.add(scriptHash)) { - batchRequest.add(node.getDerivationPath(), "blockchain.scripthash.subscribe", scriptHash); + pathScriptHashes.put(node.getDerivationPath(), scriptHash); } } } - if(scriptHashes.isEmpty()) { + if(pathScriptHashes.isEmpty()) { return; } - Map result; - try { - result = batchRequest.execute(); - } catch(JsonRpcBatchException e) { - //Even if we have some successes, failure to subscribe for all script hashes will result in outdated wallet view. Don't proceed. - throw new IllegalStateException("Failed to subscribe for updates for paths: " + e.getErrors().keySet()); - } + Map result = electrumServerRpc.subscribeScriptHashes(getTransport(), pathScriptHashes); for(String path : result.keySet()) { String status = result.get(path); @@ -257,40 +236,29 @@ public class ElectrumServer { subscribedScriptHashes.put(getScriptHash(wallet, node), status); } } - } catch (IllegalStateException e) { - throw new ServerException(e.getCause()); + } catch (ElectrumServerRpcException e) { + throw new ServerException(e.getMessage(), e.getCause()); } catch (Exception e) { throw new ServerException(e); } } - @SuppressWarnings("unchecked") public List> getOutputTransactionReferences(Transaction transaction, int indexStart, int indexEnd) throws ServerException { try { - JsonRpcClient client = new JsonRpcClient(getTransport()); - BatchRequestBuilder batchRequest = client.createBatchRequest().keysType(Integer.class).returnType(ScriptHashTx[].class); + Map pathScriptHashes = new LinkedHashMap<>(); for(int i = indexStart; i < transaction.getOutputs().size() && i < indexEnd; i++) { TransactionOutput output = transaction.getOutputs().get(i); - batchRequest.add(i, "blockchain.scripthash.get_history", getScriptHash(output)); + pathScriptHashes.put(Integer.toString(i), getScriptHash(output)); } - Map result; - try { - result = batchRequest.execute(); - } catch (JsonRpcBatchException e) { - result = (Map)e.getSuccesses(); - for(Object index : e.getErrors().keySet()) { - Integer i = (Integer)index; - result.put(i, new ScriptHashTx[] {ScriptHashTx.ERROR_TX}); - } - } + Map result = electrumServerRpc.getScriptHashHistory(getTransport(), pathScriptHashes, false); List> blockTransactionHashes = new ArrayList<>(transaction.getOutputs().size()); for(int i = 0; i < transaction.getOutputs().size(); i++) { blockTransactionHashes.add(null); } - for(Integer index : result.keySet()) { + for(String index : result.keySet()) { ScriptHashTx[] txes = result.get(index); int txBlockHeight = 0; @@ -308,7 +276,7 @@ public class ElectrumServer { .filter(ref -> !ref.getHash().equals(transaction.getTxId()) && ref.getHeight() >= minBlockHeight) .collect(Collectors.toCollection(TreeSet::new)); - blockTransactionHashes.set(index, references); + blockTransactionHashes.set(Integer.parseInt(index), references); } return blockTransactionHashes; @@ -336,7 +304,6 @@ public class ElectrumServer { } } - @SuppressWarnings("unchecked") public Map getBlockHeaders(Set references) throws ServerException { try { Set blockHeights = new TreeSet<>(); @@ -350,18 +317,7 @@ public class ElectrumServer { return Collections.emptyMap(); } - JsonRpcClient client = new JsonRpcClient(getTransport()); - BatchRequestBuilder batchRequest = client.createBatchRequest().keysType(Integer.class).returnType(String.class); - for(Integer height : blockHeights) { - batchRequest.add(height, "blockchain.block.header", height); - } - - Map result; - try { - result = batchRequest.execute(); - } catch (JsonRpcBatchException e) { - result = (Map)e.getSuccesses(); - } + Map result = electrumServerRpc.getBlockHeaders(getTransport(), blockHeights); Map blockHeaderMap = new TreeMap<>(); for(Integer height : result.keySet()) { @@ -383,29 +339,18 @@ public class ElectrumServer { } } - @SuppressWarnings("unchecked") public Map getTransactions(Set references, Map blockHeaderMap) throws ServerException { try { Set checkReferences = new TreeSet<>(references); - JsonRpcClient client = new JsonRpcClient(getTransport()); - BatchRequestBuilder batchRequest = client.createBatchRequest().keysType(String.class).returnType(String.class); + Set txids = new LinkedHashSet<>(references.size()); for(BlockTransactionHash reference : references) { - batchRequest.add(reference.getHashAsString(), "blockchain.transaction.get", reference.getHashAsString()); + txids.add(reference.getHashAsString()); } + Map result = electrumServerRpc.getTransactions(getTransport(), txids); + String strErrorTx = Sha256Hash.ZERO_HASH.toString(); - Map result; - try { - result = batchRequest.execute(); - } catch (JsonRpcBatchException e) { - result = (Map)e.getSuccesses(); - for(Object hash : e.getErrors().keySet()) { - String txhash = (String)hash; - result.put(txhash, strErrorTx); - } - } - Map transactionMap = new HashMap<>(); for(String txid : result.keySet()) { Sha256Hash hash = Sha256Hash.wrap(txid); @@ -537,19 +482,12 @@ public class ElectrumServer { @SuppressWarnings("unchecked") public Map getReferencedTransactions(Set references) throws ServerException { - JsonRpcClient client = new JsonRpcClient(getTransport()); - BatchRequestBuilder batchRequest = client.createBatchRequest().keysType(String.class).returnType(VerboseTransaction.class); + Set txids = new LinkedHashSet<>(references.size()); for(Sha256Hash reference : references) { - batchRequest.add(reference.toString(), "blockchain.transaction.get", reference.toString(), true); + txids.add(reference.toString()); } - Map result; - try { - result = batchRequest.execute(); - } catch (JsonRpcBatchException e) { - log.warn("Some errors retrieving transactions: " + e.getErrors()); - result = (Map)e.getSuccesses(); - } + Map result = electrumServerRpc.getVerboseTransactions(getTransport(), txids); Map transactionMap = new HashMap<>(); for(String txid : result.keySet()) { @@ -562,13 +500,7 @@ public class ElectrumServer { } public Map getFeeEstimates(List targetBlocks) throws ServerException { - JsonRpcClient client = new JsonRpcClient(getTransport()); - BatchRequestBuilder batchRequest = client.createBatchRequest().keysType(Integer.class).returnType(Double.class); - for(Integer targetBlock : targetBlocks) { - batchRequest.add(targetBlock, "blockchain.estimatefee", targetBlock); - } - - Map targetBlocksFeeRatesBtcKb = batchRequest.execute(); + Map targetBlocksFeeRatesBtcKb = electrumServerRpc.getFeeEstimates(getTransport(), targetBlocks); Map targetBlocksFeeRatesSats = new TreeMap<>(); for(Integer target : targetBlocksFeeRatesBtcKb.keySet()) { @@ -582,18 +514,15 @@ public class ElectrumServer { byte[] rawtxBytes = transaction.bitcoinSerialize(); String rawtxHex = Utils.bytesToHex(rawtxBytes); - JsonRpcClient client = new JsonRpcClient(getTransport()); try { - String strTxHash = client.createRequest().returnAs(String.class).method("blockchain.transaction.broadcast").id(1).param("raw_tx", rawtxHex).execute(); + String strTxHash = electrumServerRpc.broadcastTransaction(getTransport(), rawtxHex); Sha256Hash receivedTxid = Sha256Hash.wrap(strTxHash); if(!receivedTxid.equals(transaction.getTxId())) { throw new ServerException("Received txid was different (" + receivedTxid + ")"); } return receivedTxid; - } catch(JsonRpcException e) { - throw new ServerException(e.getErrorMessage().getMessage()); - } catch(IllegalStateException e) { + } catch(ElectrumServerRpcException | IllegalStateException e) { throw new ServerException(e.getMessage()); } } diff --git a/src/main/java/com/sparrowwallet/sparrow/net/ElectrumServerRpc.java b/src/main/java/com/sparrowwallet/sparrow/net/ElectrumServerRpc.java new file mode 100644 index 00000000..1aea0d77 --- /dev/null +++ b/src/main/java/com/sparrowwallet/sparrow/net/ElectrumServerRpc.java @@ -0,0 +1,33 @@ +package com.sparrowwallet.sparrow.net; + +import com.github.arteam.simplejsonrpc.client.Transport; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +public interface ElectrumServerRpc { + void ping(Transport transport); + + List getServerVersion(Transport transport, String clientName, String[] supportedVersions); + + String getServerBanner(Transport transport); + + BlockHeaderTip subscribeBlockHeaders(Transport transport); + + Map getScriptHashHistory(Transport transport, Map pathScriptHashes, boolean failOnError); + + Map getScriptHashMempool(Transport transport, Map pathScriptHashes, boolean failOnError); + + Map subscribeScriptHashes(Transport transport, Map pathScriptHashes); + + Map getBlockHeaders(Transport transport, Set blockHeights); + + Map getTransactions(Transport transport, Set txids); + + Map getVerboseTransactions(Transport transport, Set txids); + + Map getFeeEstimates(Transport transport, List targetBlocks); + + String broadcastTransaction(Transport transport, String txHex); +} diff --git a/src/main/java/com/sparrowwallet/sparrow/net/ElectrumServerRpcException.java b/src/main/java/com/sparrowwallet/sparrow/net/ElectrumServerRpcException.java new file mode 100644 index 00000000..1a9c77cd --- /dev/null +++ b/src/main/java/com/sparrowwallet/sparrow/net/ElectrumServerRpcException.java @@ -0,0 +1,19 @@ +package com.sparrowwallet.sparrow.net; + +public class ElectrumServerRpcException extends RuntimeException { + public ElectrumServerRpcException() { + super(); + } + + public ElectrumServerRpcException(String message) { + super(message); + } + + public ElectrumServerRpcException(Throwable cause) { + super(cause); + } + + public ElectrumServerRpcException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/com/sparrowwallet/sparrow/io/ServerException.java b/src/main/java/com/sparrowwallet/sparrow/net/ServerException.java similarity index 90% rename from src/main/java/com/sparrowwallet/sparrow/io/ServerException.java rename to src/main/java/com/sparrowwallet/sparrow/net/ServerException.java index 5cf459e3..468f9a84 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/ServerException.java +++ b/src/main/java/com/sparrowwallet/sparrow/net/ServerException.java @@ -1,4 +1,4 @@ -package com.sparrowwallet.sparrow.io; +package com.sparrowwallet.sparrow.net; public class ServerException extends Exception { public ServerException() { diff --git a/src/main/java/com/sparrowwallet/sparrow/net/TcpTransport.java b/src/main/java/com/sparrowwallet/sparrow/net/TcpTransport.java index 627373dc..00b512e9 100644 --- a/src/main/java/com/sparrowwallet/sparrow/net/TcpTransport.java +++ b/src/main/java/com/sparrowwallet/sparrow/net/TcpTransport.java @@ -4,7 +4,6 @@ import com.github.arteam.simplejsonrpc.client.Transport; import com.github.arteam.simplejsonrpc.server.JsonRpcServer; import com.google.common.net.HostAndPort; import com.sparrowwallet.sparrow.io.Config; -import com.sparrowwallet.sparrow.io.ServerException; import org.jetbrains.annotations.NotNull; import javax.net.SocketFactory;