Skip to content

Commit

Permalink
refactor rpc elements out
Browse files Browse the repository at this point in the history
  • Loading branch information
craigraw committed Aug 10, 2020
1 parent bb635a6 commit 79c2e29
Show file tree
Hide file tree
Showing 8 changed files with 296 additions and 110 deletions.
2 changes: 1 addition & 1 deletion drongo
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ public List<TransactionSegment> getTransactionSegments(Transaction transaction,
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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String> 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<String, ScriptHashTx[]> getScriptHashHistory(Transport transport, Map<String, String> pathScriptHashes, boolean failOnError) {
JsonRpcClient client = new JsonRpcClient(transport);
BatchRequestBuilder<String, ScriptHashTx[]> 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<String, ScriptHashTx[]> result = (Map<String, ScriptHashTx[]>)e.getSuccesses();
for(Object key : e.getErrors().keySet()) {
result.put((String)key, new ScriptHashTx[] {ScriptHashTx.ERROR_TX});
}

return result;
}
}

@Override
@SuppressWarnings("unchecked")
public Map<String, ScriptHashTx[]> getScriptHashMempool(Transport transport, Map<String, String> pathScriptHashes, boolean failOnError) {
JsonRpcClient client = new JsonRpcClient(transport);
BatchRequestBuilder<String, ScriptHashTx[]> 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<String, ScriptHashTx[]> result = (Map<String, ScriptHashTx[]>)e.getSuccesses();
for(Object key : e.getErrors().keySet()) {
result.put((String)key, new ScriptHashTx[] {ScriptHashTx.ERROR_TX});
}

return result;
}
}

@Override
public Map<String, String> subscribeScriptHashes(Transport transport, Map<String, String> pathScriptHashes) {
JsonRpcClient client = new JsonRpcClient(transport);
BatchRequestBuilder<String, String> 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<Integer, String> getBlockHeaders(Transport transport, Set<Integer> blockHeights) {
JsonRpcClient client = new JsonRpcClient(transport);
BatchRequestBuilder<Integer, String> 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<Integer, String>)e.getSuccesses();
}
}

@Override
@SuppressWarnings("unchecked")
public Map<String, String> getTransactions(Transport transport, Set<String> txids) {
JsonRpcClient client = new JsonRpcClient(transport);
BatchRequestBuilder<String, String> 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<String, String> result = (Map<String, String>)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<String, VerboseTransaction> getVerboseTransactions(Transport transport, Set<String> txids) {
JsonRpcClient client = new JsonRpcClient(transport);
BatchRequestBuilder<String, VerboseTransaction> 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<String, VerboseTransaction>)e.getSuccesses();
}
}

@Override
public Map<Integer, Double> getFeeEstimates(Transport transport, List<Integer> targetBlocks) {
JsonRpcClient client = new JsonRpcClient(transport);
BatchRequestBuilder<Integer, Double> 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);
}
}
}
Loading

0 comments on commit 79c2e29

Please sign in to comment.